From 10d51c3c521d0224c78ee3601528c384b1c4d9a9 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 20:49:30 +0200 Subject: [PATCH 01/21] feat(deploy): despliegue nativo Debian sin Docker - Elimina todos los Dockerfiles y docker-compose.yml - Elimina scripts Docker (start_docker, reset_and_deploy, deploy-clean) - Agrega deploy/debian/ con despliegue nativo via systemd: - install.sh: instalacion completa en Debian (PostgreSQL, Redis, Qdrant binario, Go, Python venv, nginx, frontend compilado) - build.sh: recompila binarios Go y frontend sin reinstalar - env.example: variables de entorno sin referencias Docker - nginx.conf: sirve React estatico + proxy al API Go en localhost - systemd/*.service: 16 servicios (8 Go + 7 Python + Qdrant) Todos los hostnames Docker (db, redis, qdrant) reemplazados por 127.0.0.1 Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile | 50 -- Dockerfile.discovery | 31 - Dockerfile.qdrant | 34 - Dockerfile.related | 32 - Dockerfile.scheduler | 23 - Dockerfile.scraper | 32 - Dockerfile.topics | 30 - Dockerfile.translator | 43 - Dockerfile.translator-gpu | 48 -- Dockerfile.wiki | 31 - backend/Dockerfile | 24 - deploy-clean.sh | 47 -- deploy/debian/build.sh | 69 ++ deploy/debian/env.example | 104 +++ deploy/debian/install.sh | 294 +++++++ deploy/debian/nginx.conf | 91 +++ deploy/debian/systemd/rss2-backend.service | 24 + .../debian/systemd/rss2-categorizer.service | 25 + deploy/debian/systemd/rss2-cluster.service | 25 + deploy/debian/systemd/rss2-discovery.service | 26 + deploy/debian/systemd/rss2-embeddings.service | 30 + deploy/debian/systemd/rss2-ingestor.service | 26 + deploy/debian/systemd/rss2-langdetect.service | 25 + deploy/debian/systemd/rss2-ner.service | 26 + .../debian/systemd/rss2-qdrant-worker.service | 28 + deploy/debian/systemd/rss2-qdrant.service | 25 + deploy/debian/systemd/rss2-related.service | 26 + deploy/debian/systemd/rss2-scraper.service | 25 + deploy/debian/systemd/rss2-topics.service | 25 + .../rss2-translation-scheduler.service | 26 + deploy/debian/systemd/rss2-translator.service | 31 + deploy/debian/systemd/rss2-wiki.service | 24 + docker-compose.yml | 748 ------------------ docker-entrypoint-db.sh | 42 - frontend/Dockerfile | 19 - monitoring/prometheus.yml | 21 - reset_and_deploy.sh | 14 - rss-ingestor-go/Dockerfile | 27 - start_docker.sh | 23 - 39 files changed, 975 insertions(+), 1319 deletions(-) delete mode 100644 Dockerfile delete mode 100644 Dockerfile.discovery delete mode 100644 Dockerfile.qdrant delete mode 100644 Dockerfile.related delete mode 100644 Dockerfile.scheduler delete mode 100644 Dockerfile.scraper delete mode 100644 Dockerfile.topics delete mode 100644 Dockerfile.translator delete mode 100644 Dockerfile.translator-gpu delete mode 100644 Dockerfile.wiki delete mode 100644 backend/Dockerfile delete mode 100755 deploy-clean.sh create mode 100755 deploy/debian/build.sh create mode 100644 deploy/debian/env.example create mode 100755 deploy/debian/install.sh create mode 100644 deploy/debian/nginx.conf create mode 100644 deploy/debian/systemd/rss2-backend.service create mode 100644 deploy/debian/systemd/rss2-categorizer.service create mode 100644 deploy/debian/systemd/rss2-cluster.service create mode 100644 deploy/debian/systemd/rss2-discovery.service create mode 100644 deploy/debian/systemd/rss2-embeddings.service create mode 100644 deploy/debian/systemd/rss2-ingestor.service create mode 100644 deploy/debian/systemd/rss2-langdetect.service create mode 100644 deploy/debian/systemd/rss2-ner.service create mode 100644 deploy/debian/systemd/rss2-qdrant-worker.service create mode 100644 deploy/debian/systemd/rss2-qdrant.service create mode 100644 deploy/debian/systemd/rss2-related.service create mode 100644 deploy/debian/systemd/rss2-scraper.service create mode 100644 deploy/debian/systemd/rss2-topics.service create mode 100644 deploy/debian/systemd/rss2-translation-scheduler.service create mode 100644 deploy/debian/systemd/rss2-translator.service create mode 100644 deploy/debian/systemd/rss2-wiki.service delete mode 100644 docker-compose.yml delete mode 100755 docker-entrypoint-db.sh delete mode 100644 frontend/Dockerfile delete mode 100644 monitoring/prometheus.yml delete mode 100755 reset_and_deploy.sh delete mode 100644 rss-ingestor-go/Dockerfile delete mode 100755 start_docker.sh diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index abd9f82..0000000 --- a/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq-dev gcc git curl \ - && rm -rf /var/lib/apt/lists/* - -ENV PYTHONUNBUFFERED=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - TOKENIZERS_PARALLELISM=false \ - HF_HOME=/root/.cache/huggingface - -COPY requirements.txt . -RUN pip install --no-cache-dir --upgrade pip - -RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu121 - -RUN pip install --no-cache-dir \ - ctranslate2 \ - sentencepiece \ - transformers==4.44.0 \ - protobuf==3.20.3 \ - "numpy<2" \ - psycopg2-binary \ - redis \ - requests \ - beautifulsoup4 \ - lxml \ - langdetect \ - nltk \ - scikit-learn \ - pandas \ - sentence-transformers \ - spacy - -RUN python -m spacy download es_core_news_lg - -COPY workers/ ./workers/ -COPY init-db/ ./init-db/ -COPY migrations/ ./migrations/ -COPY entity_config.json . - -ENV DB_HOST=db -ENV DB_PORT=5432 -ENV DB_NAME=rss -ENV DB_USER=rss -ENV DB_PASS=x - -CMD ["python", "-m", "workers.embeddings_worker"] diff --git a/Dockerfile.discovery b/Dockerfile.discovery deleted file mode 100644 index 90e405d..0000000 --- a/Dockerfile.discovery +++ /dev/null @@ -1,31 +0,0 @@ -FROM golang:1.22-alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/discovery ./cmd/discovery - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/discovery /bin/discovery - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - DISCOVERY_INTERVAL=900 \ - DISCOVERY_BATCH=10 \ - MAX_FEEDS_PER_URL=5 - -ENTRYPOINT ["/bin/discovery"] diff --git a/Dockerfile.qdrant b/Dockerfile.qdrant deleted file mode 100644 index e80bfae..0000000 --- a/Dockerfile.qdrant +++ /dev/null @@ -1,34 +0,0 @@ -FROM golang:1.22-alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/qdrant-worker ./cmd/qdrant - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/qdrant-worker /bin/qdrant-worker - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - QDRANT_HOST=qdrant \ - QDRANT_PORT=6333 \ - QDRANT_COLLECTION=news_vectors \ - OLLAMA_URL=http://ollama:11434 \ - QDRANT_SLEEP=30 \ - QDRANT_BATCH=100 - -ENTRYPOINT ["/bin/qdrant-worker"] diff --git a/Dockerfile.related b/Dockerfile.related deleted file mode 100644 index 12e011d..0000000 --- a/Dockerfile.related +++ /dev/null @@ -1,32 +0,0 @@ -FROM golang:1.22-alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/related ./cmd/related - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/related /bin/related - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - RELATED_SLEEP=10 \ - RELATED_BATCH=200 \ - RELATED_TOPK=10 \ - EMB_MODEL=mxbai-embed-large - -ENTRYPOINT ["/bin/related"] diff --git a/Dockerfile.scheduler b/Dockerfile.scheduler deleted file mode 100644 index 4a81d3e..0000000 --- a/Dockerfile.scheduler +++ /dev/null @@ -1,23 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq-dev \ - && rm -rf /var/lib/apt/lists/* - -ENV PYTHONUNBUFFERED=1 - -COPY requirements.txt . -RUN pip install --no-cache-dir --upgrade pip -RUN pip install --no-cache-dir psycopg2-binary langdetect - -COPY workers/translation_scheduler.py ./workers/ - -ENV DB_HOST=db -ENV DB_PORT=5432 -ENV DB_NAME=rss -ENV DB_USER=rss -ENV DB_PASS=x - -CMD ["python", "workers/translation_scheduler.py"] diff --git a/Dockerfile.scraper b/Dockerfile.scraper deleted file mode 100644 index 9a32bff..0000000 --- a/Dockerfile.scraper +++ /dev/null @@ -1,32 +0,0 @@ -FROM golang:1.22-alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN go mod tidy - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/scraper ./cmd/scraper - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/scraper /bin/scraper - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - SCRAPER_SLEEP=60 \ - SCRAPER_BATCH=10 - -ENTRYPOINT ["/bin/scraper"] diff --git a/Dockerfile.topics b/Dockerfile.topics deleted file mode 100644 index fc82ea7..0000000 --- a/Dockerfile.topics +++ /dev/null @@ -1,30 +0,0 @@ -FROM golang:1.22-alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/topics ./cmd/topics - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/topics /bin/topics - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - TOPICS_SLEEP=10 \ - TOPICS_BATCH=500 - -ENTRYPOINT ["/bin/topics"] diff --git a/Dockerfile.translator b/Dockerfile.translator deleted file mode 100644 index e6a96be..0000000 --- a/Dockerfile.translator +++ /dev/null @@ -1,43 +0,0 @@ -FROM python:3.11-slim-bookworm - -RUN apt-get update && apt-get install -y --no-install-recommends \ - patchelf libpq-dev gcc git curl wget \ - && rm -rf /var/lib/apt/lists/* - -ENV PYTHONUNBUFFERED=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - TOKENIZERS_PARALLELISM=false \ - HF_HOME=/root/.cache/huggingface - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir --upgrade pip - -RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cpu - -RUN pip install --no-cache-dir \ - ctranslate2==3.24.0 \ - sentencepiece \ - transformers==4.36.0 \ - protobuf==3.20.3 \ - "numpy<2" \ - psycopg2-binary \ - langdetect - -# === ARREGLAR EL EXECUTABLE STACK === -RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ - -name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \ - xargs -I {} patchelf --clear-execstack {} || true - -COPY workers/ ./workers/ -COPY init-db/ ./init-db/ -COPY migrations/ ./migrations/ - -ENV DB_HOST=db -ENV DB_PORT=5432 -ENV DB_NAME=rss -ENV DB_USER=rss -ENV DB_PASS=x - -CMD ["python", "-m", "workers.ctranslator_worker"] diff --git a/Dockerfile.translator-gpu b/Dockerfile.translator-gpu deleted file mode 100644 index c3a990b..0000000 --- a/Dockerfile.translator-gpu +++ /dev/null @@ -1,48 +0,0 @@ -FROM python:3.11-slim-bookworm - -RUN apt-get update && apt-get install -y --no-install-recommends \ - patchelf libpq-dev gcc git curl wget \ - && rm -rf /var/lib/apt/lists/* - -ENV PYTHONUNBUFFERED=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - TOKENIZERS_PARALLELISM=false \ - HF_HOME=/root/.cache/huggingface - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir --upgrade pip - -# Install PyTorch with CUDA support (cu118 for broader compatibility) -RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 - -RUN pip install --no-cache-dir \ - ctranslate2==3.24.0 \ - sentencepiece \ - transformers==4.36.0 \ - protobuf==3.20.3 \ - "numpy<2" \ - psycopg2-binary \ - langdetect - -# Fix executable stack -RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ - -name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \ - xargs -I {} patchelf --clear-execstack {} || true - -COPY workers/ ./workers/ -COPY init-db/ ./init-db/ -COPY migrations/ ./migrations/ - -ENV DB_HOST=db -ENV DB_PORT=5432 -ENV DB_NAME=rss -ENV DB_USER=rss -ENV DB_PASS=x - -# GPU Configuration - Override with: docker run --gpus all -ENV CT2_DEVICE=cuda -ENV CT2_COMPUTE_TYPE=float16 - -CMD ["python", "-m", "workers.ctranslator_worker"] diff --git a/Dockerfile.wiki b/Dockerfile.wiki deleted file mode 100644 index fbd84e0..0000000 --- a/Dockerfile.wiki +++ /dev/null @@ -1,31 +0,0 @@ -FROM golang:alpine AS builder - -ENV GOTOOLCHAIN=auto - -RUN apk add --no-cache git - -WORKDIR /app - -COPY backend/go.mod backend/go.sum ./ -RUN go mod download - -COPY backend/ ./ - -RUN go mod tidy - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/wiki_worker ./cmd/wiki_worker - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata - -COPY --from=builder /bin/wiki_worker /bin/wiki_worker - -ENV DB_HOST=db \ - DB_PORT=5432 \ - DB_NAME=rss \ - DB_USER=rss \ - DB_PASS=rss \ - WIKI_SLEEP=10 - -ENTRYPOINT ["/bin/wiki_worker"] diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 6d232b9..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM golang:1.23 AS builder - -WORKDIR /app - -RUN apt-get update && apt-get install -y gcc musl-dev git - -COPY go.mod go.sum ./ -RUN go mod download - -COPY . . - -RUN CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o /server ./cmd/server - -FROM alpine:3.19 - -RUN apk add --no-cache ca-certificates tzdata postgresql-client - -WORKDIR /app - -COPY --from=builder /server . - -EXPOSE 8080 - -CMD ["./server"] diff --git a/deploy-clean.sh b/deploy-clean.sh deleted file mode 100755 index 6f2ccb6..0000000 --- a/deploy-clean.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Script para despliegue limpio de RSS2 - -echo "=== RSS2 Clean Deployment Script ===" -echo "" - -# Detener contenedores -echo "1. Deteniendo contenedores..." -docker compose down -v 2>/dev/null - -# Eliminar volúmenes de datos (si hay permisos) -echo "2. Eliminando volúmenes de datos..." -docker volume rm rss2_db 2>/dev/null || true -docker volume rm rss2_redis 2>/dev/null || true - -# Si los volúmenes Docker tienen problemas, intentar con rm -echo " Intentando limpiar /data/..." -sudo rm -rf /datos/rss2/data/pgdata 2>/dev/null || true -sudo rm -rf /datos/rss2/data/redis-data 2>/dev/null || true - -# Iniciar base de datos -echo "3. Iniciando base de datos..." -docker compose up -d db - -# Esperar a que esté lista -echo "4. Esperando a que la base de datos esté lista..." -sleep 10 - -# Verificar estado -if docker compose ps db | grep -q "healthy"; then - echo " ✓ Base de datos iniciada correctamente" - - # Ejecutar script de schema - echo "5. Ejecutando script de inicialización..." - docker compose exec -T db psql -U rss -d rss -f /docker-entrypoint-initdb.d/00-complete-schema.sql 2>&1 | tail -5 - - # Iniciar demás servicios - echo "6. Iniciando servicios..." - docker compose up -d redis backend-go rss2_frontend nginx rss-ingestor-go - - echo "" - echo "=== Despliegue completado ===" - echo "Accede a: http://localhost:8001" -else - echo " ✗ Error: La base de datos no está healthy" - docker compose logs db -fi diff --git a/deploy/debian/build.sh b/deploy/debian/build.sh new file mode 100755 index 0000000..2699652 --- /dev/null +++ b/deploy/debian/build.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# ============================================================================= +# RSS2 - Recompila binarios y frontend (sin reinstalar el sistema) +# Usar despues de actualizar el codigo: bash build.sh +# ============================================================================= +set -euo pipefail + +RSS2_HOME="/opt/rss2" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +export PATH=$PATH:/usr/local/go/bin + +GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' +info() { echo -e "${GREEN}[BUILD]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } + +# --- Go Backend + Workers --- +if [[ -d "$REPO_ROOT/backend" ]]; then + info "Compilando backend Go..." + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/server" ./cmd/server) + info " [OK] server" + + for cmd in scraper discovery wiki_worker topics related qdrant; do + [[ -d "$REPO_ROOT/backend/cmd/$cmd" ]] || continue + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/$cmd" "./cmd/$cmd") + info " [OK] $cmd" + done +fi + +# --- Ingestor Go --- +if [[ -d "$REPO_ROOT/rss-ingestor-go" ]]; then + info "Compilando ingestor Go..." + (cd "$REPO_ROOT/rss-ingestor-go" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" .) + info " [OK] ingestor" +fi + +# --- Frontend React --- +if [[ -d "$REPO_ROOT/frontend" ]]; then + info "Compilando frontend React..." + (cd "$REPO_ROOT/frontend" && \ + npm install --silent && \ + VITE_API_URL=/api npm run build -- --outDir "$RSS2_HOME/frontend/dist") + info " [OK] frontend" +fi + +# --- Workers Python --- +info "Sincronizando workers Python..." +rsync -a --delete "$REPO_ROOT/workers/" "$RSS2_HOME/src/workers/" +cp "$REPO_ROOT/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true +info " [OK] workers Python" + +chown -R rss2:rss2 "$RSS2_HOME/bin" "$RSS2_HOME/frontend/dist" "$RSS2_HOME/src" + +# --- Restart servicios --- +info "Reiniciando servicios..." +GO_SERVICES=(rss2-backend rss2-ingestor rss2-scraper rss2-discovery rss2-wiki rss2-topics rss2-related rss2-qdrant-worker) +PY_SERVICES=(rss2-langdetect rss2-translation-scheduler rss2-translator rss2-embeddings rss2-ner rss2-cluster rss2-categorizer) + +for svc in "${GO_SERVICES[@]}" "${PY_SERVICES[@]}"; do + systemctl is-active --quiet "$svc" && systemctl restart "$svc" && info " restarted $svc" || true +done + +systemctl reload nginx 2>/dev/null || true + +info "Build completado." diff --git a/deploy/debian/env.example b/deploy/debian/env.example new file mode 100644 index 0000000..52b91e7 --- /dev/null +++ b/deploy/debian/env.example @@ -0,0 +1,104 @@ +# ============================================================================= +# RSS2 - Variables de entorno para despliegue Debian nativo +# Copiar a /opt/rss2/.env y editar valores antes de instalar +# ============================================================================= + +# --- PostgreSQL --- +POSTGRES_DB=rss +POSTGRES_USER=rss +POSTGRES_PASSWORD=CAMBIA_ESTO_postgres_password + +# Usadas por workers Go (equivalente a DATABASE_URL) +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_NAME=rss +DB_USER=rss +DB_PASS=CAMBIA_ESTO_postgres_password + +# URL completa para backend API Go +DATABASE_URL=postgres://rss:CAMBIA_ESTO_postgres_password@127.0.0.1:5432/rss?sslmode=disable + +# --- Redis --- +REDIS_PASSWORD=CAMBIA_ESTO_redis_password +REDIS_URL=redis://:CAMBIA_ESTO_redis_password@127.0.0.1:6379 + +# --- JWT Secret (minimo 32 caracteres, aleatorio) --- +SECRET_KEY=CAMBIA_ESTO_jwt_secret_muy_largo_y_aleatorio + +# --- Backend API --- +SERVER_PORT=8080 + +# --- Zona horaria --- +TZ=Europe/Madrid + +# --- HuggingFace cache (modelos ML) --- +HF_HOME=/opt/rss2/hf_cache + +# --- Qdrant (local, sin Docker) --- +QDRANT_HOST=127.0.0.1 +QDRANT_PORT=6333 +QDRANT_COLLECTION=news_vectors + +# --- Translator (NLLB-200 via CTranslate2) --- +TARGET_LANGS=es +TRANSLATOR_BATCH=32 +CT2_MODEL_PATH=/opt/rss2/models/nllb-ct2 +CT2_DEVICE=cpu +CT2_COMPUTE_TYPE=int8 +UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M + +# --- Embeddings --- +EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +EMB_BATCH=64 +EMB_SLEEP_IDLE=5 +EMB_LANGS=es +EMB_LIMIT=1000 +DEVICE=cpu + +# --- NER --- +NER_LANG=es +NER_BATCH=64 + +# --- Ingestor RSS --- +RSS_MAX_WORKERS=100 +RSS_POKE_INTERVAL_MIN=15 + +# --- Scraper --- +SCRAPER_SLEEP=60 +SCRAPER_BATCH=10 + +# --- Discovery --- +DISCOVERY_INTERVAL=900 +DISCOVERY_BATCH=10 +MAX_FEEDS_PER_URL=5 + +# --- Wiki Worker --- +WIKI_SLEEP=10 + +# --- Topics --- +TOPICS_SLEEP=10 +TOPICS_BATCH=500 + +# --- Related --- +RELATED_SLEEP=10 +RELATED_BATCH=200 +RELATED_TOPK=10 + +# --- Cluster --- +EVENT_DIST_THRESHOLD=0.35 + +# --- Categorizer --- +CATEGORIZER_BATCH_SIZE=10 +CATEGORIZER_SLEEP_IDLE=5 + +# --- Scheduler traduccion --- +SCHEDULER_BATCH=1000 +SCHEDULER_SLEEP=30 + +# --- Lang Detect --- +LANG_DETECT_SLEEP=60 +LANG_DETECT_BATCH=1000 + +# --- Qdrant Worker --- +QDRANT_SLEEP=30 +QDRANT_BATCH=100 diff --git a/deploy/debian/install.sh b/deploy/debian/install.sh new file mode 100755 index 0000000..00da025 --- /dev/null +++ b/deploy/debian/install.sh @@ -0,0 +1,294 @@ +#!/usr/bin/env bash +# ============================================================================= +# RSS2 - Instalacion en Debian (sin Docker) +# Ejecutar como root: bash install.sh +# ============================================================================= +set -euo pipefail + +RSS2_USER="rss2" +RSS2_HOME="/opt/rss2" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } + +[[ "$EUID" -ne 0 ]] && error "Ejecutar como root: sudo bash install.sh" + +# ============================================================================= +# 1. DEPENDENCIAS DEL SISTEMA +# ============================================================================= +info "Instalando dependencias del sistema..." +apt-get update -qq +apt-get install -y --no-install-recommends \ + curl wget git build-essential \ + postgresql postgresql-client \ + redis-server \ + nginx \ + python3 python3-pip python3-venv python3-dev \ + nodejs npm \ + ca-certificates tzdata \ + libpq-dev + +# Go (si no esta instalado o version < 1.22) +if ! command -v go &>/dev/null || [[ "$(go version | awk '{print $3}' | tr -d 'go')" < "1.22" ]]; then + info "Instalando Go 1.23..." + GO_VERSION="1.23.4" + ARCH=$(dpkg --print-architecture) + case "$ARCH" in + amd64) GO_ARCH="amd64" ;; + arm64) GO_ARCH="arm64" ;; + *) error "Arquitectura no soportada: $ARCH" ;; + esac + curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz + rm -rf /usr/local/go + tar -C /usr/local -xzf /tmp/go.tar.gz + echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh + export PATH=$PATH:/usr/local/go/bin + rm /tmp/go.tar.gz +fi +info "Go: $(go version)" + +# Qdrant (binario oficial) +if [[ ! -f "$RSS2_HOME/qdrant/qdrant" ]]; then + info "Descargando Qdrant..." + QDRANT_VERSION="v1.12.1" + ARCH=$(dpkg --print-architecture) + case "$ARCH" in + amd64) QDRANT_ARCH="x86_64-unknown-linux-musl" ;; + arm64) QDRANT_ARCH="aarch64-unknown-linux-musl" ;; + *) error "Arquitectura no soportada para Qdrant: $ARCH" ;; + esac + mkdir -p "$RSS2_HOME/qdrant" + curl -fsSL "https://github.com/qdrant/qdrant/releases/download/${QDRANT_VERSION}/qdrant-${QDRANT_ARCH}.tar.gz" \ + -o /tmp/qdrant.tar.gz + tar -C "$RSS2_HOME/qdrant" -xzf /tmp/qdrant.tar.gz + chmod +x "$RSS2_HOME/qdrant/qdrant" + rm /tmp/qdrant.tar.gz +fi + +# ============================================================================= +# 2. USUARIO Y DIRECTORIOS +# ============================================================================= +info "Creando usuario $RSS2_USER y directorios..." +id "$RSS2_USER" &>/dev/null || useradd -r -m -d "$RSS2_HOME" -s /bin/bash "$RSS2_USER" + +mkdir -p \ + "$RSS2_HOME/bin" \ + "$RSS2_HOME/src" \ + "$RSS2_HOME/data/wiki_images" \ + "$RSS2_HOME/data/qdrant_storage" \ + "$RSS2_HOME/hf_cache" \ + "$RSS2_HOME/models" \ + "$RSS2_HOME/frontend/dist" \ + "$RSS2_HOME/logs" + +# ============================================================================= +# 3. CONFIGURACION ENTORNO +# ============================================================================= +if [[ ! -f "$RSS2_HOME/.env" ]]; then + if [[ -f "$SCRIPT_DIR/env.example" ]]; then + cp "$SCRIPT_DIR/env.example" "$RSS2_HOME/.env" + warn "Copia env.example en $RSS2_HOME/.env - EDITA LAS CONTRASENAS antes de continuar" + warn "Presiona Enter cuando hayas editado el .env, o Ctrl+C para salir" + read -r + else + error "No se encontro env.example en $SCRIPT_DIR" + fi +fi + +# ============================================================================= +# 4. POSTGRESQL +# ============================================================================= +info "Configurando PostgreSQL..." +source "$RSS2_HOME/.env" 2>/dev/null || true + +DB_NAME="${POSTGRES_DB:-rss}" +DB_USER="${POSTGRES_USER:-rss}" +DB_PASS="${POSTGRES_PASSWORD:-changeme}" + +systemctl enable --now postgresql + +# Crear usuario y base de datos si no existen +sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1 || \ + sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" + +sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1 || \ + sudo -u postgres createdb -O "$DB_USER" "$DB_NAME" + +# Ejecutar migraciones SQL +if [[ -d "$REPO_ROOT/migrations" ]]; then + info "Ejecutando migraciones..." + for sql_file in "$REPO_ROOT/migrations"/*.sql; do + [[ -f "$sql_file" ]] || continue + info " Aplicando $(basename "$sql_file")..." + sudo -u postgres psql -d "$DB_NAME" -f "$sql_file" 2>/dev/null || warn " (ya aplicada o error ignorado)" + done +fi + +# Ejecutar init-db scripts (schema inicial) +if [[ -d "$REPO_ROOT/init-db" ]]; then + info "Ejecutando scripts de init-db..." + for sql_file in "$REPO_ROOT/init-db"/*.sql; do + [[ -f "$sql_file" ]] || continue + info " $(basename "$sql_file")..." + sudo -u postgres psql -d "$DB_NAME" -f "$sql_file" 2>/dev/null || warn " (ya aplicada o error ignorado)" + done +fi + +# ============================================================================= +# 5. REDIS +# ============================================================================= +info "Configurando Redis..." +REDIS_PASS="${REDIS_PASSWORD:-changeme_redis}" + +# Agregar autenticacion y limites de memoria a redis.conf +REDIS_CONF="/etc/redis/redis.conf" +grep -q "requirepass $REDIS_PASS" "$REDIS_CONF" 2>/dev/null || { + echo "requirepass $REDIS_PASS" >> "$REDIS_CONF" + echo "maxmemory 512mb" >> "$REDIS_CONF" + echo "maxmemory-policy allkeys-lru" >> "$REDIS_CONF" + echo "appendonly yes" >> "$REDIS_CONF" +} +systemctl enable --now redis-server + +# ============================================================================= +# 6. PYTHON VIRTUALENV + DEPENDENCIAS ML +# ============================================================================= +info "Creando virtualenv Python y instalando dependencias..." +python3 -m venv "$RSS2_HOME/venv" +"$RSS2_HOME/venv/bin/pip" install --upgrade pip -q + +if [[ -f "$REPO_ROOT/requirements.txt" ]]; then + "$RSS2_HOME/venv/bin/pip" install -r "$REPO_ROOT/requirements.txt" -q +fi + +# spaCy modelo en español +"$RSS2_HOME/venv/bin/python" -m spacy download es_core_news_lg 2>/dev/null || \ + warn "spaCy model es_core_news_lg no se pudo descargar, hazlo manualmente" + +# Copiar workers Python al directorio de trabajo +info "Copiando workers Python..." +rsync -a --delete "$REPO_ROOT/workers/" "$RSS2_HOME/src/workers/" +cp "$REPO_ROOT/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true + +# ============================================================================= +# 7. COMPILAR GO (backend + workers) +# ============================================================================= +info "Compilando binarios Go..." +export PATH=$PATH:/usr/local/go/bin +export GOPATH=/tmp/go-build-rss2 + +# Backend API +if [[ -d "$REPO_ROOT/backend" ]]; then + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/server" ./cmd/server && \ + info " [OK] server") || warn " [FAIL] server" + for cmd in scraper discovery wiki_worker topics related qdrant; do + [[ -d "$REPO_ROOT/backend/cmd/$cmd" ]] || continue + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/$cmd" "./cmd/$cmd" && \ + info " [OK] $cmd") || warn " [FAIL] $cmd" + done +fi + +# RSS Ingestor Go (repo separado) +if [[ -d "$REPO_ROOT/rss-ingestor-go" ]]; then + (cd "$REPO_ROOT/rss-ingestor-go" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" . && \ + info " [OK] ingestor") || warn " [FAIL] ingestor" +fi + +# ============================================================================= +# 8. FRONTEND REACT +# ============================================================================= +info "Compilando frontend React..." +if [[ -d "$REPO_ROOT/frontend" ]]; then + (cd "$REPO_ROOT/frontend" && \ + npm install --silent && \ + VITE_API_URL=/api npm run build -- --outDir "$RSS2_HOME/frontend/dist" && \ + info " [OK] frontend compilado") || warn " [FAIL] frontend" +fi + +# ============================================================================= +# 9. NGINX +# ============================================================================= +info "Configurando Nginx..." +cp "$SCRIPT_DIR/nginx.conf" /etc/nginx/nginx.conf +nginx -t && systemctl enable --now nginx && systemctl reload nginx + +# ============================================================================= +# 10. SYSTEMD SERVICES +# ============================================================================= +info "Instalando servicios systemd..." +SERVICES=( + rss2-qdrant + rss2-backend + rss2-ingestor + rss2-scraper + rss2-discovery + rss2-wiki + rss2-topics + rss2-related + rss2-qdrant-worker + rss2-langdetect + rss2-translation-scheduler + rss2-translator + rss2-embeddings + rss2-ner + rss2-cluster + rss2-categorizer +) + +for svc in "${SERVICES[@]}"; do + svc_file="$SCRIPT_DIR/systemd/${svc}.service" + if [[ -f "$svc_file" ]]; then + cp "$svc_file" "/etc/systemd/system/${svc}.service" + else + warn "No se encontro $svc_file" + fi +done + +systemctl daemon-reload + +for svc in "${SERVICES[@]}"; do + systemctl enable "$svc" 2>/dev/null || true +done + +# ============================================================================= +# 11. PERMISOS FINALES +# ============================================================================= +info "Ajustando permisos..." +chown -R "$RSS2_USER:$RSS2_USER" "$RSS2_HOME" +chmod 600 "$RSS2_HOME/.env" + +# ============================================================================= +# 12. ARRANCAR SERVICIOS +# ============================================================================= +info "Arrancando servicios..." +# Infraestructura primero +systemctl start rss2-qdrant +sleep 3 + +# API y workers Go +for svc in rss2-backend rss2-ingestor rss2-scraper rss2-discovery rss2-wiki rss2-topics rss2-related rss2-qdrant-worker; do + systemctl start "$svc" || warn "No se pudo arrancar $svc" +done + +# Workers Python (modelos pesados, arrancan despues) +for svc in rss2-langdetect rss2-translation-scheduler rss2-translator rss2-embeddings rss2-ner rss2-cluster rss2-categorizer; do + systemctl start "$svc" || warn "No se pudo arrancar $svc" +done + +# ============================================================================= +echo "" +info "=============================================" +info " RSS2 instalado en $RSS2_HOME" +info " Acceder en: http://$(hostname -I | awk '{print $1}'):8001" +info "" +info " Ver logs: journalctl -u rss2-backend -f" +info " Ver estado: systemctl status rss2-backend" +info " Editar env: nano $RSS2_HOME/.env" +info "=============================================" diff --git a/deploy/debian/nginx.conf b/deploy/debian/nginx.conf new file mode 100644 index 0000000..5407ce8 --- /dev/null +++ b/deploy/debian/nginx.conf @@ -0,0 +1,91 @@ +user www-data; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /run/nginx.pid; + +events { + worker_connections 2048; + use epoll; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 100M; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/javascript + application/json application/javascript + application/xml text/xml; + + # Go API backend (proceso nativo en localhost) + upstream api_backend { + server 127.0.0.1:8080; + keepalive 32; + } + + server { + listen 8001; + server_name _; + + client_body_timeout 60s; + client_header_timeout 60s; + send_timeout 300s; + + # Frontend React (archivos estaticos compilados) + root /opt/rss2/frontend/dist; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # Imagenes Wikipedia servidas directamente + location /wiki-images/ { + alias /opt/rss2/data/wiki_images/; + expires 7d; + add_header Cache-Control "public, immutable"; + } + + # Proxy al API Go + location /api/ { + proxy_pass http://api_backend/api/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Connection ""; + + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + + location /health { + access_log off; + return 200 "ok"; + } + + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + } +} diff --git a/deploy/debian/systemd/rss2-backend.service b/deploy/debian/systemd/rss2-backend.service new file mode 100644 index 0000000..f50239f --- /dev/null +++ b/deploy/debian/systemd/rss2-backend.service @@ -0,0 +1,24 @@ +[Unit] +Description=RSS2 Backend API (Go) +After=network.target postgresql.service redis.service +Requires=postgresql.service redis.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +ExecStart=/opt/rss2/bin/server +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-backend + +# Limites de recursos +LimitNOFILE=65536 +MemoryMax=1G + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-categorizer.service b/deploy/debian/systemd/rss2-categorizer.service new file mode 100644 index 0000000..61ecf9a --- /dev/null +++ b/deploy/debian/systemd/rss2-categorizer.service @@ -0,0 +1,25 @@ +[Unit] +Description=RSS2 Categorizer Worker (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=CATEGORIZER_BATCH_SIZE=10 +Environment=CATEGORIZER_SLEEP_IDLE=5 +ExecStart=/opt/rss2/venv/bin/python -m workers.simple_categorizer_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-categorizer + +MemoryMax=1G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-cluster.service b/deploy/debian/systemd/rss2-cluster.service new file mode 100644 index 0000000..dd990fb --- /dev/null +++ b/deploy/debian/systemd/rss2-cluster.service @@ -0,0 +1,25 @@ +[Unit] +Description=RSS2 Cluster Worker - Agrupacion de noticias (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=EVENT_DIST_THRESHOLD=0.35 +Environment=EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +ExecStart=/opt/rss2/venv/bin/python -m workers.cluster_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-cluster + +MemoryMax=2G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-discovery.service b/deploy/debian/systemd/rss2-discovery.service new file mode 100644 index 0000000..c9a435b --- /dev/null +++ b/deploy/debian/systemd/rss2-discovery.service @@ -0,0 +1,26 @@ +[Unit] +Description=RSS2 Discovery de Feeds (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=DISCOVERY_INTERVAL=900 +Environment=DISCOVERY_BATCH=10 +Environment=MAX_FEEDS_PER_URL=5 +ExecStart=/opt/rss2/bin/discovery +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-discovery + +MemoryMax=512M +CPUQuota=100% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-embeddings.service b/deploy/debian/systemd/rss2-embeddings.service new file mode 100644 index 0000000..efb0dbd --- /dev/null +++ b/deploy/debian/systemd/rss2-embeddings.service @@ -0,0 +1,30 @@ +[Unit] +Description=RSS2 Embeddings Worker (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +Environment=EMB_BATCH=64 +Environment=EMB_SLEEP_IDLE=5 +Environment=EMB_LANGS=es +Environment=EMB_LIMIT=1000 +Environment=DEVICE=cpu +Environment=HF_HOME=/opt/rss2/hf_cache +ExecStart=/opt/rss2/venv/bin/python -m workers.embeddings_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-embeddings + +MemoryMax=3G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-ingestor.service b/deploy/debian/systemd/rss2-ingestor.service new file mode 100644 index 0000000..8a29a74 --- /dev/null +++ b/deploy/debian/systemd/rss2-ingestor.service @@ -0,0 +1,26 @@ +[Unit] +Description=RSS2 Ingestor RSS (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=RSS_MAX_WORKERS=100 +Environment=RSS_POKE_INTERVAL_MIN=15 +ExecStart=/opt/rss2/bin/ingestor +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-ingestor + +LimitNOFILE=65536 +MemoryMax=2G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-langdetect.service b/deploy/debian/systemd/rss2-langdetect.service new file mode 100644 index 0000000..18c2732 --- /dev/null +++ b/deploy/debian/systemd/rss2-langdetect.service @@ -0,0 +1,25 @@ +[Unit] +Description=RSS2 Language Detection Worker (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=LANG_DETECT_SLEEP=60 +Environment=LANG_DETECT_BATCH=1000 +ExecStart=/opt/rss2/venv/bin/python -m workers.langdetect_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-langdetect + +MemoryMax=512M +CPUQuota=50% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-ner.service b/deploy/debian/systemd/rss2-ner.service new file mode 100644 index 0000000..6f43c78 --- /dev/null +++ b/deploy/debian/systemd/rss2-ner.service @@ -0,0 +1,26 @@ +[Unit] +Description=RSS2 NER Worker - Extraccion de Entidades (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=NER_LANG=es +Environment=NER_BATCH=64 +Environment=HF_HOME=/opt/rss2/hf_cache +ExecStart=/opt/rss2/venv/bin/python -m workers.ner_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-ner + +MemoryMax=2G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-qdrant-worker.service b/deploy/debian/systemd/rss2-qdrant-worker.service new file mode 100644 index 0000000..6334fd1 --- /dev/null +++ b/deploy/debian/systemd/rss2-qdrant-worker.service @@ -0,0 +1,28 @@ +[Unit] +Description=RSS2 Qdrant Sync Worker (Go) +After=network.target postgresql.service rss2-qdrant.service +Requires=postgresql.service rss2-qdrant.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=QDRANT_HOST=127.0.0.1 +Environment=QDRANT_PORT=6333 +Environment=QDRANT_COLLECTION=news_vectors +Environment=QDRANT_SLEEP=30 +Environment=QDRANT_BATCH=100 +ExecStart=/opt/rss2/bin/qdrant_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-qdrant-worker + +MemoryMax=1G +CPUQuota=100% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-qdrant.service b/deploy/debian/systemd/rss2-qdrant.service new file mode 100644 index 0000000..59b8651 --- /dev/null +++ b/deploy/debian/systemd/rss2-qdrant.service @@ -0,0 +1,25 @@ +[Unit] +Description=Qdrant Vector Database +After=network.target + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/qdrant +ExecStart=/opt/rss2/qdrant/qdrant +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-qdrant + +Environment=QDRANT__SERVICE__HTTP_PORT=6333 +Environment=QDRANT__SERVICE__GRPC_PORT=6334 +Environment=QDRANT__STORAGE__STORAGE_PATH=/opt/rss2/data/qdrant_storage + +MemoryMax=4G +CPUQuota=400% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-related.service b/deploy/debian/systemd/rss2-related.service new file mode 100644 index 0000000..cc671f9 --- /dev/null +++ b/deploy/debian/systemd/rss2-related.service @@ -0,0 +1,26 @@ +[Unit] +Description=RSS2 Related News Worker (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=RELATED_SLEEP=10 +Environment=RELATED_BATCH=200 +Environment=RELATED_TOPK=10 +ExecStart=/opt/rss2/bin/related +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-related + +MemoryMax=1G +CPUQuota=100% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-scraper.service b/deploy/debian/systemd/rss2-scraper.service new file mode 100644 index 0000000..83929c4 --- /dev/null +++ b/deploy/debian/systemd/rss2-scraper.service @@ -0,0 +1,25 @@ +[Unit] +Description=RSS2 Scraper HTML (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=SCRAPER_SLEEP=60 +Environment=SCRAPER_BATCH=10 +ExecStart=/opt/rss2/bin/scraper +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-scraper + +MemoryMax=512M +CPUQuota=100% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-topics.service b/deploy/debian/systemd/rss2-topics.service new file mode 100644 index 0000000..f9ab9b5 --- /dev/null +++ b/deploy/debian/systemd/rss2-topics.service @@ -0,0 +1,25 @@ +[Unit] +Description=RSS2 Topics Worker (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=TOPICS_SLEEP=10 +Environment=TOPICS_BATCH=500 +ExecStart=/opt/rss2/bin/topics +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-topics + +MemoryMax=512M +CPUQuota=100% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-translation-scheduler.service b/deploy/debian/systemd/rss2-translation-scheduler.service new file mode 100644 index 0000000..a46f3ad --- /dev/null +++ b/deploy/debian/systemd/rss2-translation-scheduler.service @@ -0,0 +1,26 @@ +[Unit] +Description=RSS2 Translation Scheduler (Python) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=TARGET_LANGS=es +Environment=SCHEDULER_BATCH=1000 +Environment=SCHEDULER_SLEEP=30 +ExecStart=/opt/rss2/venv/bin/python -m workers.translation_scheduler +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-translation-scheduler + +MemoryMax=256M +CPUQuota=50% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-translator.service b/deploy/debian/systemd/rss2-translator.service new file mode 100644 index 0000000..90528e8 --- /dev/null +++ b/deploy/debian/systemd/rss2-translator.service @@ -0,0 +1,31 @@ +[Unit] +Description=RSS2 Translator Worker NLLB-200 (Python) +After=network.target postgresql.service rss2-translation-scheduler.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2/src +EnvironmentFile=/opt/rss2/.env +Environment=TARGET_LANGS=es +Environment=TRANSLATOR_BATCH=32 +Environment=CT2_MODEL_PATH=/opt/rss2/models/nllb-ct2 +Environment=CT2_DEVICE=cpu +Environment=CT2_COMPUTE_TYPE=int8 +Environment=UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M +Environment=HF_HOME=/opt/rss2/hf_cache +ExecStart=/opt/rss2/venv/bin/python -m workers.ctranslator_worker +Restart=always +RestartSec=15 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-translator + +# El modelo NLLB-200 consume bastante RAM en CPU +MemoryMax=4G +CPUQuota=200% + +[Install] +WantedBy=multi-user.target diff --git a/deploy/debian/systemd/rss2-wiki.service b/deploy/debian/systemd/rss2-wiki.service new file mode 100644 index 0000000..01891dd --- /dev/null +++ b/deploy/debian/systemd/rss2-wiki.service @@ -0,0 +1,24 @@ +[Unit] +Description=RSS2 Wiki Worker - imagenes Wikipedia (Go) +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=rss2 +Group=rss2 +WorkingDirectory=/opt/rss2 +EnvironmentFile=/opt/rss2/.env +Environment=WIKI_SLEEP=10 +ExecStart=/opt/rss2/bin/wiki_worker +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=rss2-wiki + +MemoryMax=256M +CPUQuota=50% + +[Install] +WantedBy=multi-user.target diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b126c81..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,748 +0,0 @@ -services: - db: - image: postgres:18-alpine - container_name: rss2_db - shm_size: 4gb - environment: - POSTGRES_DB: ${POSTGRES_DB:-rss} - POSTGRES_USER: ${POSTGRES_USER:-rss} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C.UTF-8" - LANG: C.UTF-8 - LC_ALL: C.UTF-8 - TZ: Europe/Madrid - PGDATA: /var/lib/postgresql/data/18/main - volumes: - - ./data/pgdata:/var/lib/postgresql/data - - ./init-db:/docker-entrypoint-initdb.d:rw - - ./docker-entrypoint-db.sh:/docker-entrypoint-db.sh:ro - entrypoint: ["bash", "/docker-entrypoint-db.sh"] - networks: - backend: - aliases: - - db - - rss2_db - restart: unless-stopped - healthcheck: - test: [ "CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $$POSTGRES_USER -d $$POSTGRES_DB || exit 1" ] - interval: 5s - timeout: 5s - retries: 30 - start_period: 20s - deploy: - resources: - limits: - memory: 8G - reservations: - memory: 4G - - redis: - image: redis:7-alpine - container_name: rss2_redis - environment: - TZ: Europe/Madrid - # SEGURIDAD: Redis con autenticación - command: > - redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD} - volumes: - - ./data/redis-data:/data - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - networks: - backend: - aliases: - - redis - - rss2_redis - restart: unless-stopped - healthcheck: - test: [ "CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping" ] - interval: 5s - timeout: 3s - retries: 5 - deploy: - resources: - limits: - memory: 768M - reservations: - memory: 512M - - rss-ingestor-go: - build: - context: ./rss-ingestor-go - dockerfile: Dockerfile - container_name: rss2_ingestor_go - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - RSS_MAX_WORKERS: 100 - RSS_POKE_INTERVAL_MIN: 15 - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '2' - memory: 2G - reservations: - memory: 512M - - langdetect: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_langdetect_py - command: bash -lc "python -m workers.langdetect_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - LANG_DETECT_SLEEP: 60 - LANG_DETECT_BATCH: 1000 - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - - # ================================================================================== - # SCRAPER WORKER (Go) - Extrae artículos de URLs - # ================================================================================== - scraper: - build: - context: . - dockerfile: Dockerfile.scraper - container_name: rss2_scraper - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - SCRAPER_SLEEP: 60 - SCRAPER_BATCH: 10 - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 512M - - # ================================================================================== - # DISCOVERY WORKER (Go) - Descubre RSS feeds - # ================================================================================== - discovery: - build: - context: . - dockerfile: Dockerfile.discovery - container_name: rss2_discovery - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - DISCOVERY_INTERVAL: 900 - DISCOVERY_BATCH: 10 - MAX_FEEDS_PER_URL: 5 - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 512M - - # ================================================================================== - # WIKI WORKER (Go) - Wikipedia info and thumbnails - # ================================================================================== - wiki-worker: - build: - context: . - dockerfile: Dockerfile.wiki - container_name: rss2_wiki_worker - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - WIKI_SLEEP: 10 - TZ: Europe/Madrid - volumes: - - ./data/wiki_images:/app/data/wiki_images - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - - # ================================================================================== - # BACKEND GO (API REST) - # ================================================================================== - backend-go: - build: - context: ./backend - dockerfile: Dockerfile - container_name: rss2_backend_go - environment: - TZ: Europe/Madrid - DATABASE_URL: postgres://${POSTGRES_USER:-rss}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-rss}?sslmode=disable - REDIS_URL: redis://:${REDIS_PASSWORD:-rss_redis_pass_2024}@redis:6379 - SECRET_KEY: ${SECRET_KEY:-change_this_to_a_long_random_string} - SERVER_PORT: "8080" - volumes: - - ./data/wiki_images:/app/data/wiki_images - networks: - - backend - - frontend - depends_on: - db: - condition: service_healthy - redis: - condition: service_healthy - restart: unless-stopped - - # ================================================================================== - # FRONTEND REACT - # ================================================================================== - rss2_frontend: - build: - context: ./frontend - dockerfile: Dockerfile - container_name: rss2_frontend - environment: - TZ: Europe/Madrid - VITE_API_URL: /api - networks: - - frontend - depends_on: - - backend-go - restart: unless-stopped - - # ================================================================================== - # NGINX (Puerto 8001 - sirve React + proxy API) - # ================================================================================== - nginx: - image: nginx:alpine - container_name: rss2_nginx - ports: - - "8001:80" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - networks: - - frontend - depends_on: - - rss2_frontend - - backend-go - restart: unless-stopped - - # ================================================================================== - # TRANSLATOR CPU (CTranslate2) - Scale with: docker compose up -d --scale translator=3 - # ================================================================================== - translator: - build: - context: . - dockerfile: Dockerfile.translator - image: rss2-translator:latest - command: bash -lc "python -m workers.ctranslator_worker" - security_opt: - - seccomp=unconfined - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - TARGET_LANGS: es - TRANSLATOR_BATCH: 32 - CT2_MODEL_PATH: /app/models/nllb-ct2 - CT2_DEVICE: cpu - CT2_COMPUTE_TYPE: int8 - UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - TRANSLATOR_ID: ${TRANSLATOR_ID:-} - volumes: - - ./workers:/app/workers - - ./hf_cache:/app/hf_cache - - ./models:/app/models - networks: - - backend - profiles: - - cpu-only - depends_on: - db: - condition: service_healthy - restart: unless-stopped - - # ================================================================================== - # TRANSLATION SCHEDULER - Creates translation jobs - # ================================================================================== - translation-scheduler: - build: - context: . - dockerfile: Dockerfile.scheduler - image: rss2-scheduler:latest - container_name: rss2_translation_scheduler - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - TARGET_LANGS: es - SCHEDULER_BATCH: 1000 - SCHEDULER_SLEEP: 30 - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - networks: - - backend - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - depends_on: - db: - condition: service_healthy - restart: unless-stopped - - # ================================================================================== - # TRANSLATOR GPU (CTranslate2 with CUDA) - # ================================================================================== - translator-gpu: - build: - context: . - dockerfile: Dockerfile.translator-gpu - image: rss2-translator-gpu:latest - container_name: rss2_translator_gpu - command: bash -lc "python -m workers.ctranslator_worker" - security_opt: - - seccomp=unconfined - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - TARGET_LANGS: es - TRANSLATOR_BATCH: 64 - CT2_MODEL_PATH: /app/models/nllb-ct2 - CT2_DEVICE: cuda - CT2_COMPUTE_TYPE: float16 - UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - - ./hf_cache:/app/hf_cache - - ./models:/app/models - networks: - - backend - deploy: - resources: - limits: - memory: 4G - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [ gpu ] - depends_on: - db: - condition: service_healthy - restart: unless-stopped - - embeddings: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_embeddings_py - command: bash -lc "python -m workers.embeddings_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 - EMB_BATCH: 64 - EMB_SLEEP_IDLE: 5 - EMB_LANGS: es - EMB_LIMIT: 1000 - DEVICE: cuda - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - - ./hf_cache:/app/hf_cache - networks: - - backend - deploy: - resources: - limits: - memory: 6G - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [ gpu ] - depends_on: - db: - condition: service_healthy - restart: unless-stopped - - # ================================================================================== - # TOPICS WORKER (Go) - Matching temas y países - # ================================================================================== - topics: - build: - context: . - dockerfile: Dockerfile.topics - container_name: rss2_topics - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - TOPICS_SLEEP: 10 - TOPICS_BATCH: 500 - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 512M - - # ================================================================================== - # RELATED WORKER (Go) - Noticias relacionadas - # ================================================================================== - related: - build: - context: . - dockerfile: Dockerfile.related - container_name: rss2_related - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - RELATED_SLEEP: 10 - RELATED_BATCH: 200 - RELATED_TOPK: 10 - EMB_MODEL: mxbai-embed-large - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 1G - - qdrant: - image: qdrant/qdrant:latest - container_name: rss2_qdrant - environment: - TZ: Europe/Madrid - QDRANT__SERVICE__GRPC_PORT: 6334 - volumes: - - ./data/qdrant_storage:/qdrant/storage - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - networks: - - backend - restart: unless-stopped - deploy: - resources: - limits: - cpus: '4' - memory: 4G - reservations: - memory: 2G - - # ================================================================================== - # QDRANT WORKER (Go) - Vectorización y búsqueda semántica - # ================================================================================== - qdrant-worker: - build: - context: . - dockerfile: Dockerfile.qdrant - container_name: rss2_qdrant_worker - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - QDRANT_HOST: qdrant - QDRANT_PORT: 6333 - QDRANT_COLLECTION: news_vectors - OLLAMA_URL: http://ollama:11434 - QDRANT_SLEEP: 30 - QDRANT_BATCH: 100 - TZ: Europe/Madrid - networks: - - backend - depends_on: - db: - condition: service_healthy - qdrant: - condition: service_started - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 1G - - # ================================================================================== - # NER WORKER (Python) - Extracción de entidades - # ================================================================================== - ner: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_ner - command: bash -lc "python -m workers.ner_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - NER_LANG: es - NER_BATCH: 64 - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - - ./hf_cache:/app/hf_cache - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '2' - memory: 2G - - # ================================================================================== - # CLUSTER WORKER (Python) - Agrupación de noticias - # ================================================================================== - cluster: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_cluster_py - command: bash -lc "python -m workers.cluster_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - EVENT_DIST_THRESHOLD: 0.35 - EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '2' - memory: 2G - - # ================================================================================== - # LLM CATEGORIZER (Python) - Categorización con Ollama - # ================================================================================== - llm-categorizer: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_llm_categorizer - command: bash -lc "python -m workers.simple_categorizer_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - CATEGORIZER_BATCH_SIZE: 10 - CATEGORIZER_SLEEP_IDLE: 5 - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '2' - memory: 1G - - # ================================================================================== - # MONITORING STACK - SECURED - # ================================================================================== - - prometheus: - image: prom/prometheus:latest - container_name: rss2_prometheus - volumes: - - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--web.console.libraries=/usr/share/prometheus/console_libraries' - - '--web.console.templates=/usr/share/prometheus/consoles' - # SEGURIDAD: Sin exposición de puertos - acceso solo vía Grafana o túnel SSH - # ports: - # - "9090:9090" - networks: - - monitoring - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 2G - - grafana: - image: grafana/grafana:latest - container_name: rss2_grafana - # SEGURIDAD: Acceso solo en localhost o vía túnel SSH - # Para acceso remoto, usar túnel SSH: ssh -L 3001:localhost:3001 user@server - ports: - - "127.0.0.1:3001:3000" - environment: - # SEGURIDAD: Cambiar este password en producción - - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-change_this_password} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_SERVER_ROOT_URL=http://localhost:3001 - - GF_SECURITY_COOKIE_SECURE=false - - GF_SECURITY_COOKIE_SAMESITE=lax - volumes: - - grafana_data:/var/lib/grafana - networks: - - monitoring - depends_on: - - prometheus - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 1G - - cadvisor: - image: gcr.io/cadvisor/cadvisor:latest - container_name: rss2_cadvisor - # SEGURIDAD: Sin exposición de puertos - solo acceso interno - # ports: - # - "8081:8080" - volumes: - - /:/rootfs:ro - - /var/run:/var/run:ro - - /sys:/sys:ro - - /var/lib/docker/:/var/lib/docker:ro - - /dev/disk/:/dev/disk:ro - devices: - - /dev/kmsg - networks: - - monitoring - restart: unless-stopped - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - -# ================================================================================== -# REDES SEGMENTADAS -# ================================================================================== -networks: - # Red frontal - Solo nginx y web app - frontend: - name: rss2_frontend - driver: bridge - internal: false - - # Red backend - Base de datos, workers, redis, qdrant - backend: - name: rss2_backend - driver: bridge - internal: false # Acceso externo permitido (necesario para ingestor) - - # Red de monitoreo - Prometheus, Grafana, cAdvisor - monitoring: - name: rss2_monitoring - driver: bridge - internal: true - -volumes: - prometheus_data: - grafana_data: - torch_extensions: diff --git a/docker-entrypoint-db.sh b/docker-entrypoint-db.sh deleted file mode 100755 index 1eb2722..0000000 --- a/docker-entrypoint-db.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -e - -# Detectar si la base de datos necesita reinicialización -PGDATA_DIR="/var/lib/postgresql/data/18/main" - -echo "RSS2: Checking database integrity..." - -# Si no existe el archivo de versión, es una base de datos nueva -if [ ! -f "$PGDATA_DIR/PG_VERSION" ]; then - echo "RSS2: New database - will be initialized by docker-entrypoint" -else - # Verificar si la base de datos es funcional - if ! pg_isready -h localhost -p 5432 -U "${POSTGRES_USER:-rss}" 2>/dev/null; then - echo "RSS2: Database appears corrupted - removing old data files for fresh initialization..." - # Eliminar solo los archivos de datos, no todo el directorio - rm -rf "$PGDATA_DIR"/* - echo "RSS2: Data files removed - docker-entrypoint will initialize fresh database" - else - echo "RSS2: Database is healthy" - fi -fi - -# Ejecutar el entrypoint original con los parámetros de PostgreSQL -exec docker-entrypoint.sh \ - postgres \ - -c max_connections=200 \ - -c shared_buffers=4GB \ - -c effective_cache_size=12GB \ - -c work_mem=16MB \ - -c maintenance_work_mem=512MB \ - -c autovacuum_max_workers=3 \ - -c autovacuum_vacuum_scale_factor=0.02 \ - -c autovacuum_vacuum_cost_limit=1000 \ - -c max_worker_processes=8 \ - -c max_parallel_workers=6 \ - -c max_parallel_workers_per_gather=2 \ - -c wal_level=replica \ - -c max_wal_senders=5 \ - -c wal_keep_size=1GB \ - -c hot_standby=on \ - "$@" diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 5f06218..0000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM node:20-alpine AS builder - -WORKDIR /app - -COPY package*.json ./ -RUN npm install - -COPY . . -RUN npm run build - -FROM nginx:alpine - -COPY --from=builder /app/dist /usr/share/nginx/html - -COPY nginx.conf /etc/nginx/nginx.conf - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml deleted file mode 100644 index 6cc80ce..0000000 --- a/monitoring/prometheus.yml +++ /dev/null @@ -1,21 +0,0 @@ -global: - scrape_interval: 15s - -scrape_configs: - - job_name: 'prometheus' - static_configs: - - targets: ['localhost:9090'] - - - job_name: 'cadvisor' - static_configs: - - targets: ['cadvisor:8080'] - - # If we had Node Exporter (for host metrics): - # - job_name: 'node_exporter' - # static_configs: - # - targets: ['node-exporter:9100'] - - # If the app exposes metrics (e.g. Flask/Gunicorn with prometheus_client) - # - job_name: 'rss2_web' - # static_configs: - # - targets: ['rss2_web:8000'] diff --git a/reset_and_deploy.sh b/reset_and_deploy.sh deleted file mode 100755 index c103aef..0000000 --- a/reset_and_deploy.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -echo "Stopping all containers..." -docker-compose down - -echo "Removing data volumes..." -# Use sudo if necessary, or ensure current user has permissions -rm -rf data/pgdata data/pgdata-replica data/redis-data data/qdrant_storage - -echo "Starting deployment from scratch..." -docker-compose up -d --build - -echo "Deployment complete. Checking status..." -docker-compose ps diff --git a/rss-ingestor-go/Dockerfile b/rss-ingestor-go/Dockerfile deleted file mode 100644 index b75cbaa..0000000 --- a/rss-ingestor-go/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM golang:alpine AS builder - -WORKDIR /app - -# Install git and SSL certs -RUN apk add --no-cache git ca-certificates - -# Copy source code immediately -COPY . . - -# Download dependencies -RUN go mod tidy && go mod download - -# Build the Go app -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o rss-ingestor . - -# Final stage -FROM alpine:latest - -WORKDIR /root/ - -# Copy the Pre-built binary file from the previous stage -COPY --from=builder /app/rss-ingestor . -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ - -# Command to run the executable -CMD ["./rss-ingestor"] diff --git a/start_docker.sh b/start_docker.sh deleted file mode 100755 index 1aef36d..0000000 --- a/start_docker.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Script para iniciar los servicios de Docker -# Ejecutar con: sudo ./start_docker.sh - -set -e -cd "$(dirname "$0")" - -echo "=== RSS2 Docker Services ===" - -# Verificación de modelo eliminada (script de conversión no disponible) - -echo "" -echo "Iniciando servicios Docker..." -docker compose up -d --build - -echo "" -echo "✓ Servicios iniciados" -echo "" -echo "Para ver los logs:" -echo " docker compose logs -f translator" -echo "" -echo "Para verificar el estado:" -echo " docker compose ps" From 00c0254e6c99076cdb5e71e3c638a5ce81370499 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 21:02:27 +0200 Subject: [PATCH 02/21] docs: guia de despliegue nativo Debian sin Docker Documentacion completa para instalar COCONEWS en Debian/Ubuntu: - Requisitos hardware por modo (CPU/GPU) - Tabla de servicios y como se gestionan - Instalacion paso a paso incluyendo conversion del modelo NLLB-200 - Gestion de servicios systemd y logs - Estructura de directorios en servidor - Reglas de firewall recomendadas - Procedimiento de backup - Solucion de problemas frecuentes Co-Authored-By: Claude Sonnet 4.6 --- DEPLOY_DEBIAN.md | 283 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 DEPLOY_DEBIAN.md diff --git a/DEPLOY_DEBIAN.md b/DEPLOY_DEBIAN.md new file mode 100644 index 0000000..54a8784 --- /dev/null +++ b/DEPLOY_DEBIAN.md @@ -0,0 +1,283 @@ +# COCONEWS · Despliegue en Debian (sin Docker) + +Guía completa para instalar y operar COCONEWS en un servidor Debian 12 (Bookworm) o Ubuntu 22.04+, sin Docker ni contenedores. + +--- + +## Requisitos de hardware + +| Modo | CPU | RAM | Disco | +|------|-----|-----|-------| +| **Mínimo (solo CPU)** | 4 cores | 8 GB | 40 GB | +| **Recomendado** | 8 cores | 16 GB | 80 GB | +| **Con GPU** | 8 cores + NVIDIA | 16 GB | 80 GB | + +> Los modelos de IA (NLLB-200 + MiniLM + spaCy) ocupan ~5 GB en disco una vez descargados. + +--- + +## Servicios que se instalan en el servidor + +| Servicio | Tecnología | Gestionado por | +|----------|-----------|----------------| +| Base de datos | PostgreSQL 16 | apt + systemd | +| Caché | Redis 7 | apt + systemd | +| Búsqueda vectorial | Qdrant (binario) | systemd | +| API REST | Go (backend) | systemd | +| Ingestor RSS | Go | systemd | +| Scraper / Discovery / Wiki / Topics / Related / Qdrant-worker | Go | systemd | +| Traducción (NLLB-200) | Python + CTranslate2 | systemd | +| Embeddings | Python + Sentence-Transformers | systemd | +| NER | Python + spaCy | systemd | +| Clustering / Categorización | Python | systemd | +| Frontend | React (estático compilado) | nginx | +| Proxy / Web | nginx | apt + systemd | + +--- + +## Instalación paso a paso + +### 1. Clonar el repositorio + +```bash +git clone https://gitea.laenre.net/pietre/rss2.git /opt/src/rss2 +cd /opt/src/rss2 +git checkout coconews +``` + +### 2. Configurar variables de entorno + +```bash +cp deploy/debian/env.example /opt/rss2/.env +nano /opt/rss2/.env +``` + +Valores que **debes cambiar obligatoriamente**: + +```env +POSTGRES_PASSWORD=contraseña_segura_postgres +DB_PASS=contraseña_segura_postgres +REDIS_PASSWORD=contraseña_segura_redis +SECRET_KEY=cadena_aleatoria_minimo_32_caracteres +``` + +Genera claves seguras con: +```bash +openssl rand -hex 32 +``` + +### 3. Descargar y convertir el modelo de traducción (NLLB-200) + +Este paso se hace **una sola vez** y puede tardar 10-30 minutos dependiendo de la conexión. + +```bash +# Instalar dependencias Python primero +python3 -m venv /opt/rss2/venv +/opt/rss2/venv/bin/pip install ctranslate2 transformers sentencepiece + +# Convertir modelo NLLB-200 a formato CTranslate2 +/opt/rss2/venv/bin/ct2-opus-mt-converter \ + --model facebook/nllb-200-distilled-600M \ + --output_dir /opt/rss2/models/nllb-ct2 \ + --quantization int8 + +# Alternativa si el comando anterior falla: +/opt/rss2/venv/bin/python -c " +import ctranslate2 +ctranslate2.converters.OpusMTConverter( + 'facebook/nllb-200-distilled-600M' +).convert('/opt/rss2/models/nllb-ct2', quantization='int8') +" +``` + +### 4. Ejecutar el instalador + +```bash +sudo bash /opt/src/rss2/deploy/debian/install.sh +``` + +El script hace automáticamente: +- Instala PostgreSQL, Redis, nginx, Go, Node.js via apt +- Descarga el binario de Qdrant +- Crea el usuario `rss2` del sistema +- Crea la base de datos y ejecuta las migraciones +- Compila los 8 binarios Go +- Compila el frontend React +- Instala y habilita los 16 servicios systemd + +--- + +## Verificar que funciona + +```bash +# Estado de todos los servicios +systemctl status rss2-backend rss2-ingestor rss2-translator rss2-embeddings + +# Ver logs en tiempo real +journalctl -u rss2-backend -f +journalctl -u rss2-translator -f + +# Comprobar que el API responde +curl http://localhost:8080/api/stats + +# Acceder al frontend +# http://IP_DEL_SERVIDOR:8001 +``` + +--- + +## Gestión de servicios + +### Iniciar / parar / reiniciar + +```bash +# Un servicio concreto +systemctl start rss2-backend +systemctl stop rss2-translator +systemctl restart rss2-embeddings + +# Todos los workers de una vez +systemctl restart rss2-backend rss2-ingestor rss2-scraper rss2-discovery \ + rss2-wiki rss2-topics rss2-related rss2-qdrant-worker \ + rss2-langdetect rss2-translation-scheduler rss2-translator \ + rss2-embeddings rss2-ner rss2-cluster rss2-categorizer +``` + +### Ver logs + +```bash +journalctl -u rss2-backend -f # API Go +journalctl -u rss2-translator -f # Traductor +journalctl -u rss2-embeddings -f # Embeddings +journalctl -u rss2-ner -f # NER entidades +journalctl -u rss2-ingestor -f # Ingestor RSS +``` + +--- + +## Actualizar el código + +Cuando hay nuevos cambios en el repositorio: + +```bash +cd /opt/src/rss2 +git pull +sudo bash deploy/debian/build.sh +``` + +El script `build.sh` recompila los binarios Go, el frontend y sincroniza los workers Python, y reinicia los servicios automáticamente. + +--- + +## Estructura de directorios en el servidor + +``` +/opt/rss2/ +├── .env # Variables de entorno (permisos 600) +├── bin/ # Binarios Go compilados +│ ├── server # API REST +│ ├── ingestor # Ingestor RSS +│ ├── scraper +│ ├── discovery +│ ├── wiki_worker +│ ├── topics +│ ├── related +│ └── qdrant_worker +├── src/ +│ └── workers/ # Workers Python +├── venv/ # Virtualenv Python (ML) +├── models/ +│ └── nllb-ct2/ # Modelo traduccion CTranslate2 +├── hf_cache/ # Cache HuggingFace (embeddings, NER) +├── frontend/ +│ └── dist/ # Frontend React compilado (servido por nginx) +├── data/ +│ ├── wiki_images/ # Imagenes Wikipedia descargadas +│ └── qdrant_storage/ # Datos vectoriales Qdrant +└── qdrant/ + └── qdrant # Binario Qdrant +``` + +--- + +## Requisitos de red / firewall + +Solo exponer al exterior el puerto **8001** (nginx). El resto deben ser internos: + +```bash +# Con ufw: +ufw allow 8001/tcp # COCONEWS web +ufw deny 5432/tcp # PostgreSQL - solo localhost +ufw deny 6379/tcp # Redis - solo localhost +ufw deny 6333/tcp # Qdrant - solo localhost +ufw deny 8080/tcp # API Go - solo localhost (nginx hace proxy) +ufw enable +``` + +--- + +## Backup de datos + +```bash +# PostgreSQL +sudo -u postgres pg_dump rss > /opt/rss2/backups/rss_$(date +%Y%m%d).sql + +# Datos Qdrant +systemctl stop rss2-qdrant +tar -czf /opt/rss2/backups/qdrant_$(date +%Y%m%d).tar.gz /opt/rss2/data/qdrant_storage +systemctl start rss2-qdrant + +# Imagenes Wikipedia (opcional, se pueden re-descargar) +tar -czf /opt/rss2/backups/wiki_images_$(date +%Y%m%d).tar.gz /opt/rss2/data/wiki_images +``` + +--- + +## Solución de problemas frecuentes + +### El traductor no arranca + +```bash +journalctl -u rss2-translator -n 50 +# Si dice "model not found": el modelo NLLB-200 no está convertido +# Ejecutar el paso 3 de la instalación +``` + +### PostgreSQL rechaza la conexión + +```bash +# Verificar que el .env tiene DB_HOST=127.0.0.1 (no "db") +grep DB_HOST /opt/rss2/.env + +# Verificar que el usuario existe +sudo -u postgres psql -c "\du" +``` + +### nginx devuelve 502 Bad Gateway + +```bash +# El backend Go no está corriendo +systemctl status rss2-backend +journalctl -u rss2-backend -n 30 +``` + +### Memoria insuficiente para los modelos Python + +Con 8 GB RAM el translator + embeddings + NER pueden coincidir. Si el servidor tiene poca RAM, deshabilitar el translator-gpu y bajar el batch: + +```bash +# En /opt/rss2/.env +TRANSLATOR_BATCH=8 +EMB_BATCH=32 +NER_BATCH=16 +systemctl restart rss2-translator rss2-embeddings rss2-ner +``` + +--- + +## Primer inicio de sesión + +1. Abrir `http://IP:8001` en el navegador +2. Al no haber usuarios, el sistema te redirige al registro +3. El primer usuario registrado se convierte en **administrador** +4. Ir a Configuración → Feeds → Importar el `feeds.csv` del repositorio para empezar con fuentes precargadas From ab3b0b53c552e99fd1ca53e8db200fbb8ebdfd90 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 21:11:56 +0200 Subject: [PATCH 03/21] fix(deploy): corregir 5 problemas bloqueantes para despliegue Debian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - install.sh/build.sh: actualizar Go 1.23 → 1.25 (requerido por rss-ingestor-go) - install.sh/build.sh: nombrar binario qdrant como qdrant_worker para coincidir con rss2-qdrant-worker.service (ExecStart) - install.sh/build.sh: GOTOOLCHAIN=local en ingestor para evitar descarga automatica de toolchain Go superior - rss2-backend.service: sobreescribir hostnames Docker (libretranslate, ollama, spacy) por 127.0.0.1 para despliegue nativo - env.example: agregar TRANSLATION_URL, OLLAMA_URL, SPACY_URL con nota explicativa sobre uso en endpoints admin - DEPLOY_DEBIAN.md: corregir comando conversion NLLB-200 a CTranslate2 usando OpusMTConverter Python API en lugar de CLI incorrecto Co-Authored-By: Claude Sonnet 4.6 --- DEPLOY_DEBIAN.md | 25 ++++++++++------------ deploy/debian/build.sh | 9 ++++++-- deploy/debian/env.example | 7 ++++++ deploy/debian/install.sh | 19 ++++++++++------ deploy/debian/systemd/rss2-backend.service | 4 ++++ 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/DEPLOY_DEBIAN.md b/DEPLOY_DEBIAN.md index 54a8784..9835f0a 100644 --- a/DEPLOY_DEBIAN.md +++ b/DEPLOY_DEBIAN.md @@ -71,25 +71,22 @@ openssl rand -hex 32 Este paso se hace **una sola vez** y puede tardar 10-30 minutos dependiendo de la conexión. ```bash -# Instalar dependencias Python primero +# Instalar dependencias Python primero (si aun no se hizo) python3 -m venv /opt/rss2/venv /opt/rss2/venv/bin/pip install ctranslate2 transformers sentencepiece -# Convertir modelo NLLB-200 a formato CTranslate2 -/opt/rss2/venv/bin/ct2-opus-mt-converter \ - --model facebook/nllb-200-distilled-600M \ - --output_dir /opt/rss2/models/nllb-ct2 \ - --quantization int8 - -# Alternativa si el comando anterior falla: -/opt/rss2/venv/bin/python -c " -import ctranslate2 -ctranslate2.converters.OpusMTConverter( - 'facebook/nllb-200-distilled-600M' -).convert('/opt/rss2/models/nllb-ct2', quantization='int8') -" +# Convertir modelo NLLB-200 a formato CTranslate2 (tarda 10-30 min) +/opt/rss2/venv/bin/python - <<'EOF' +from ctranslate2.converters import OpusMTConverter +converter = OpusMTConverter("facebook/nllb-200-distilled-600M") +converter.convert("/opt/rss2/models/nllb-ct2", quantization="int8", force=True) +print("Modelo convertido OK en /opt/rss2/models/nllb-ct2") +EOF ``` +> El modelo ocupa ~600 MB convertido. Si la descarga de HuggingFace falla, exporta +> `HF_ENDPOINT=https://huggingface.co` o usa un mirror. + ### 4. Ejecutar el instalador ```bash diff --git a/deploy/debian/build.sh b/deploy/debian/build.sh index 2699652..6dd81b2 100755 --- a/deploy/debian/build.sh +++ b/deploy/debian/build.sh @@ -22,19 +22,24 @@ if [[ -d "$REPO_ROOT/backend" ]]; then CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/server" ./cmd/server) info " [OK] server" - for cmd in scraper discovery wiki_worker topics related qdrant; do + for cmd in scraper discovery wiki_worker topics related; do [[ -d "$REPO_ROOT/backend/cmd/$cmd" ]] || continue (cd "$REPO_ROOT/backend" && \ CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/$cmd" "./cmd/$cmd") info " [OK] $cmd" done + # qdrant worker: nombre del binario debe coincidir con el service + [[ -d "$REPO_ROOT/backend/cmd/qdrant" ]] && \ + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/qdrant_worker" "./cmd/qdrant") + info " [OK] qdrant_worker" fi # --- Ingestor Go --- if [[ -d "$REPO_ROOT/rss-ingestor-go" ]]; then info "Compilando ingestor Go..." (cd "$REPO_ROOT/rss-ingestor-go" && \ - CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" .) + GOTOOLCHAIN=local CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" .) info " [OK] ingestor" fi diff --git a/deploy/debian/env.example b/deploy/debian/env.example index 52b91e7..406f928 100644 --- a/deploy/debian/env.example +++ b/deploy/debian/env.example @@ -34,6 +34,13 @@ TZ=Europe/Madrid # --- HuggingFace cache (modelos ML) --- HF_HOME=/opt/rss2/hf_cache +# --- Endpoints ML opcionales (solo si corres Ollama o LibreTranslate por separado) --- +# Los workers Python van directo a BD; estos endpoints solo los usa el backend para +# llamadas on-demand desde el panel admin (NER, traduccion manual, etc.) +TRANSLATION_URL=http://127.0.0.1:7790 +OLLAMA_URL=http://127.0.0.1:11434 +SPACY_URL=http://127.0.0.1:8000 + # --- Qdrant (local, sin Docker) --- QDRANT_HOST=127.0.0.1 QDRANT_PORT=6333 diff --git a/deploy/debian/install.sh b/deploy/debian/install.sh index 00da025..9627bbc 100755 --- a/deploy/debian/install.sh +++ b/deploy/debian/install.sh @@ -32,10 +32,10 @@ apt-get install -y --no-install-recommends \ ca-certificates tzdata \ libpq-dev -# Go (si no esta instalado o version < 1.22) -if ! command -v go &>/dev/null || [[ "$(go version | awk '{print $3}' | tr -d 'go')" < "1.22" ]]; then - info "Instalando Go 1.23..." - GO_VERSION="1.23.4" +# Go (rss-ingestor-go requiere Go 1.25) +if ! command -v go &>/dev/null || [[ "$(go version | awk '{print $3}' | tr -d 'go')" < "1.25" ]]; then + info "Instalando Go 1.25..." + GO_VERSION="1.25.0" ARCH=$(dpkg --print-architecture) case "$ARCH" in amd64) GO_ARCH="amd64" ;; @@ -186,18 +186,23 @@ if [[ -d "$REPO_ROOT/backend" ]]; then (cd "$REPO_ROOT/backend" && \ CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/server" ./cmd/server && \ info " [OK] server") || warn " [FAIL] server" - for cmd in scraper discovery wiki_worker topics related qdrant; do + for cmd in scraper discovery wiki_worker topics related; do [[ -d "$REPO_ROOT/backend/cmd/$cmd" ]] || continue (cd "$REPO_ROOT/backend" && \ CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/$cmd" "./cmd/$cmd" && \ info " [OK] $cmd") || warn " [FAIL] $cmd" done + # qdrant worker: output como qdrant_worker para coincidir con el service + [[ -d "$REPO_ROOT/backend/cmd/qdrant" ]] && \ + (cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/qdrant_worker" "./cmd/qdrant" && \ + info " [OK] qdrant_worker") || warn " [FAIL] qdrant_worker" fi -# RSS Ingestor Go (repo separado) +# RSS Ingestor Go (repo separado, requiere Go 1.25) if [[ -d "$REPO_ROOT/rss-ingestor-go" ]]; then (cd "$REPO_ROOT/rss-ingestor-go" && \ - CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" . && \ + GOTOOLCHAIN=local CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o "$RSS2_HOME/bin/ingestor" . && \ info " [OK] ingestor") || warn " [FAIL] ingestor" fi diff --git a/deploy/debian/systemd/rss2-backend.service b/deploy/debian/systemd/rss2-backend.service index f50239f..3919116 100644 --- a/deploy/debian/systemd/rss2-backend.service +++ b/deploy/debian/systemd/rss2-backend.service @@ -9,6 +9,10 @@ User=rss2 Group=rss2 WorkingDirectory=/opt/rss2 EnvironmentFile=/opt/rss2/.env +# Sobreescribir hostnames Docker por localhost (los workers Python van directo a DB) +Environment=TRANSLATION_URL=http://127.0.0.1:7790 +Environment=OLLAMA_URL=http://127.0.0.1:11434 +Environment=SPACY_URL=http://127.0.0.1:8000 ExecStart=/opt/rss2/bin/server Restart=always RestartSec=5 From e3c682a36fcfbc781352caa7a3fa051427812999 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 21:17:11 +0200 Subject: [PATCH 04/21] feat: prerequisites, POC local y README reescrito MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy/debian/prerequisites.sh: - Instalador de dependencias del sistema para Debian/Ubuntu - Detecta OS, instala PostgreSQL 16 (repo oficial), Redis, nginx, Go 1.25, Node.js 20 LTS, Qdrant binario, Python venv - Crea usuario rss2 y estructura /opt/rss2 - Pregunta interactivamente si instalar modelos ML pesados (ctranslate2, transformers, spaCy es_core_news_lg, NLLB-200) - Separado de install.sh para poder ejecutarlo independientemente poc/poc.sh: - POC local en ~2 minutos sin Docker, sin workers ML - Crea BD temporal coconews_poc con schema completo - Carga 10 noticias de muestra en español listas para ver - Compila backend Go y frontend React en /tmp/coconews-poc - Lanza Redis en puerto alternativo (6380) sin interferir - Sirve frontend con npx serve en http://127.0.0.1:18001 - Limpieza automatica al Ctrl+C poc/seed.sql: - 10 noticias de muestra en español (no requieren traduccion) - Categorias, continentes y paises basicos - 5 feeds de ejemplo (El Pais, BBC Mundo, etc.) README.md: - Reescrito completamente sin referencias Docker - Diagrama ASCII de arquitectura - Inicio rapido con poc.sh (2 minutos) - Instrucciones de install en Debian con prerequisites.sh - Tabla de requisitos hardware por modo - Mapa completo del repositorio Co-Authored-By: Claude Sonnet 4.6 --- README.md | 255 ++++++++++++++++----------- deploy/debian/prerequisites.sh | 305 +++++++++++++++++++++++++++++++++ poc/poc.sh | 213 +++++++++++++++++++++++ poc/seed.sql | 94 ++++++++++ 4 files changed, 766 insertions(+), 101 deletions(-) create mode 100755 deploy/debian/prerequisites.sh create mode 100755 poc/poc.sh create mode 100644 poc/seed.sql diff --git a/README.md b/README.md index 4019aaa..d70ea43 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,177 @@ -# RSS2 - AI-Powered News Intelligence Platform +# COCONEWS -RSS2 es una plataforma avanzada de agregación, traducción, análisis y vectorización de noticias, diseñada para transformar flujos masivos de información en inteligencia accionable. Utiliza una arquitectura híbrida de microservicios (Go + Python) integrada con modelos de inteligencia artificial de última generación para ofrecer búsqueda semántica, clasificación inteligente y automatización de contenidos. +Plataforma de inteligencia de noticias. Agrega feeds RSS de cualquier idioma, los traduce automáticamente al español, extrae entidades, los agrupa por eventos y los hace buscables semánticamente. --- -## 🚀 Capacidades Principales +## Qué hace -* **Enriquecimiento con Wikipedia**: Sistema automatizado que detecta personas y organizaciones, descarga sus biografías e imágenes oficiales de Wikipedia para mostrarlas en tooltips interactivos con avatares circulares. -* **Categorización Inteligente (LLM)**: Clasificación de noticias mediante una instancia local de Mistral-7B / Llama-3 (vía Ollama), procesando contenido en tiempo real. -* **Búsqueda Semántica**: Motor vectorial Qdrant para descubrir noticias por contexto y significado, yendo más allá de las palabras clave tradicionales. -* **Traducción Neuronal de Alta Calidad**: Integración de NLLB-200 (vía CTranslate2) para traducir noticias de múltiples idiomas al español con precisión profesional. -* **Inteligencia de Entidades (NER)**: Extracción y normalización automática de Personas, Organizaciones y Lugares para análisis de tendencias y mapeo de relaciones. -* **Búsqueda de Noticias Relacionadas**: Algoritmos de similitud que agrupan noticias sobre el mismo tema automáticamente. +- **Ingesta** feeds RSS/Atom de cualquier idioma de forma continua +- **Traduce** al español con NLLB-200 (200 idiomas soportados) +- **Extrae entidades** (personas, organizaciones, lugares) con spaCy y las enriquece con Wikipedia +- **Genera embeddings** para búsqueda por significado, no solo por palabras clave +- **Agrupa noticias** del mismo evento automáticamente +- **Categoriza** contenido con reglas y modelos de lenguaje +- Interfaz web React con búsqueda semántica, filtros y tooltips de Wikipedia --- -## 🏗️ Arquitectura de Servicios (Docker) +## Arquitectura -El sistema se orquestra mediante Docker Compose y se divide en capas especializadas: - -### Capa de Acceso y API -| Servicio | Tecnología | Descripción | -|---------|------------|-------------| -| **`nginx`** | Nginx Alpine | Gateway y Proxy Inverso (Puerto **8001**). | -| **`rss2_frontend`** | React + Vite | Interfaz web de usuario moderna y responsiva. | -| **`backend-go`** | Go + Gin | API REST principal y gestión de lógica de negocio. | - -### Ingesta y Descubrimiento (Go) -| Servicio | Tecnología | Descripción | -|---------|------------|-------------| -| **`rss-ingestor-go`** | Go | Crawler de alto rendimiento para feeds RSS. | -| **`scraper`** | Go | Scraper profundo con sanitización de HTML y extracción de texto. | -| **`discovery`** | Go | Agente autónomo para descubrir nuevos feeds a partir de URLs. | - -### Procesamiento de Datos e IA (Go & Python) -| Servicio | Tecnología | Descripción | -|---------|------------|-------------| -| **`translator`** | NLLB-200 (CPU) | Traducción neuronal optimizada con CTranslate2. | -| **`translator-gpu`**| NLLB-200 (GPU) | Traducción acelerada por hardware (CUDA). | -| **`wiki-worker`** | Go | **[NUEVO]** Integración con Wikipedia y gestión de imágenes locales. | -| **`embeddings`** | S-Transformers | Generación de vectores para búsqueda semántica. | -| **`ner`** | Spacy / BERT | Reconocimiento de entidades nombradas (NER). | -| **`llm-categorizer`**| Ollama / Mistral | Clasificación avanzada mediante modelos de lenguaje. | -| **`topics`** | Go | Matcher automático de países y temas predefinidos. | -| **`related`** | Go | Motor de detección de noticias relacionadas. | - -### Capa de Almacenamiento -| Servicio | Tecnología | Descripción | -|---------|------------|-------------| -| **`db`** | PostgreSQL 18 | Base de datos relacional principal. | -| **`qdrant`** | Qdrant | Base de datos vectorial para búsqueda por similitud. | -| **`redis`** | Redis 7 | Colas de mensajes y caché de alto desempeño. | - ---- - -## ⚙️ Guía de Configuración - -### 1. Requisitos de Hardware -* **Modo Básico (CPU)**: 4+ Cores CPU, 8GB RAM. -* **Modo Avanzado (IA)**: NVIDIA GPU con 8GB+ VRAM (mínimo recomendado para LLM y Traducción GPU). - -### 2. Instalación Rápida -```bash -git clone -cd rss2 -cp .env.example .env -# Edita .env con tus credenciales -docker compose up -d +``` +Internet (RSS/Atom) + │ + ▼ + rss-ingestor-go ──→ PostgreSQL ──→ langdetect + │ │ │ + scraper/discovery │ translator (NLLB-200) + │ │ + │ embeddings (MiniLM) + │ │ + │ ner (spaCy) + │ │ + │ cluster / related + │ │ + │ qdrant-worker ──→ Qdrant + │ + backend-go (API REST :8080) + │ + nginx (:8001) + │ + Frontend React ``` -### 3. Escalado de Workers (¡Importante!) -Para aumentar la velocidad de procesamiento (especialmente la traducción), puedes escalar los workers: +**Stack:** +- Go 1.25 — API REST (Gin), ingestor RSS, scraper, workers +- Python 3 — Workers ML (NLLB-200, MiniLM, spaCy, CTranslate2) +- PostgreSQL 16 — datos relacionales + full-text search +- Qdrant — búsqueda vectorial semántica +- Redis 7 — caché de consultas +- React 18 + TypeScript + Tailwind — frontend +- nginx — proxy inverso + archivos estáticos + +--- + +## Inicio rápido (POC local) + +Prueba COCONEWS en tu máquina en ~2 minutos con datos de muestra, sin instalar los modelos ML. + +**Requisitos mínimos para el POC:** +- Go 1.25+ +- Node.js 18+ +- PostgreSQL (corriendo) +- Redis (corriendo) ```bash -# Ejecutar 4 traductores en paralelo -docker compose up -d --scale translator=4 +git clone https://gitea.laenre.net/pietre/rss2.git coconews +cd coconews +git checkout coconews -# Si usas GPU y tienes capacidad -docker compose up -d --scale translator-gpu=2 +bash poc/poc.sh +``` + +Abre `http://127.0.0.1:18001` en el navegador. +El primer usuario que se registre será administrador. + +--- + +## Instalación en servidor Debian + +Para un despliegue completo con todos los workers ML en producción: + +### 1. Instalar prerequisites + +```bash +sudo bash deploy/debian/prerequisites.sh +``` + +Instala: PostgreSQL 16, Redis, nginx, Go 1.25, Node.js 20, Qdrant, Python 3 venv. +Pregunta si instalar los modelos ML pesados ahora o después. + +### 2. Configurar entorno + +```bash +cp deploy/debian/env.example /opt/rss2/.env +nano /opt/rss2/.env # edita contraseñas y SECRET_KEY +``` + +### 3. Instalar y arrancar + +```bash +sudo bash deploy/debian/install.sh +``` + +Compila los binarios Go, el frontend React, crea los servicios systemd y arranca todo. + +### Acceder + +``` +http://IP_DEL_SERVIDOR:8001 +``` + +Guía completa: [DEPLOY_DEBIAN.md](DEPLOY_DEBIAN.md) + +--- + +## Gestión de servicios + +```bash +# Estado general +systemctl status rss2-backend rss2-ingestor rss2-translator + +# Logs en tiempo real +journalctl -u rss2-backend -f +journalctl -u rss2-translator -f + +# Reiniciar tras actualizar código +git pull +sudo bash deploy/debian/build.sh ``` --- -## 🛡️ Administración y Mantenimiento +## Actualizar el código -### Copias de Seguridad (Backups) -Desde el panel de Administración (`/admin/settings`), puedes realizar: -* **Backup Completo**: Volcado SQL de toda la base de datos. -* **Backup de Noticias (ZIP)**: **[NUEVO]** Genera un archivo comprimido que incluye las tablas de noticias, traducciones y todas sus etiquetas. Ideal para migraciones de contenido. - -### Variables de Entorno Clave (`.env`) -| Variable | Descripción | -|----------|-------------| -| `WIKI_SLEEP` | Tiempo de espera entre peticiones a Wikipedia (evita bloqueos). | -| `SCHEDULER_BATCH`| Cantidad de noticias a enviar a traducir por ciclo. | -| `TARGET_LANGS` | Idiomas destino (ej: `es`). | -| `OLLAMA_HOST` | Dirección del servidor Ollama para categorización. | - ---- - -## 📖 Documentación de la API (Campos Wikipedia) - -Las respuestas de noticias ahora incluyen el objeto `entities` enriquecido: - -```json -{ - "id": 67449, - "titulo": "...", - "entities": [ - { - "valor": "Apple", - "tipo": "organizacion", - "wiki_summary": "Apple Inc. es una empresa estadounidense...", - "wiki_url": "https://es.wikipedia.org/wiki/Apple", - "image_path": "/api/wiki-images/wiki_5723.png" - } - ] -} +```bash +cd /ruta/al/repo +git pull +sudo bash deploy/debian/build.sh ``` +`build.sh` recompila los binarios Go, el frontend y sincroniza los workers Python, y reinicia los servicios automáticamente. + --- -**RSS2** - *Transformando noticias en inteligencia con IA localizada.* +## Requisitos de hardware + +| Modo | CPU | RAM | Disco | +|------|-----|-----|-------| +| POC local | 2 cores | 4 GB | 10 GB | +| Producción CPU | 4+ cores | 8 GB | 40 GB | +| Producción recomendado | 8 cores | 16 GB | 80 GB | + +--- + +## Estructura del repositorio + +``` +├── backend/ Go — API REST + workers (scraper, discovery, wiki, topics, related, qdrant) +├── rss-ingestor-go/ Go — Ingestor de feeds RSS +├── frontend/ React + TypeScript + Tailwind +├── workers/ Python — ML workers (traducción, embeddings, NER, cluster, categorización) +├── init-db/ SQL — Schema y datos iniciales +├── migrations/ SQL — Migraciones incrementales +├── deploy/debian/ Scripts de despliegue para Debian sin Docker +│ ├── prerequisites.sh Instala todas las dependencias del sistema +│ ├── install.sh Instalación completa +│ ├── build.sh Recompila y reinicia tras actualizar código +│ ├── env.example Plantilla de variables de entorno +│ ├── nginx.conf Configuración nginx para despliegue nativo +│ └── systemd/ Ficheros de servicio systemd (16 servicios) +├── poc/ +│ ├── poc.sh POC local con datos de prueba (sin Docker, sin ML) +│ └── seed.sql Datos de muestra para el POC +├── feeds.csv Feeds RSS precargados para importar desde el admin +├── entity_config.json Aliases y blacklist para normalización de entidades NER +└── DEPLOY_DEBIAN.md Guía detallada de despliegue en Debian +``` diff --git a/deploy/debian/prerequisites.sh b/deploy/debian/prerequisites.sh new file mode 100755 index 0000000..156b9c6 --- /dev/null +++ b/deploy/debian/prerequisites.sh @@ -0,0 +1,305 @@ +#!/usr/bin/env bash +# ============================================================================= +# COCONEWS - Instalacion de prerequisites en Debian 12 / Ubuntu 22.04+ +# Ejecutar ANTES de install.sh +# Uso: sudo bash prerequisites.sh +# ============================================================================= +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +info() { echo -e "${GREEN}[OK]${NC} $*"; } +step() { echo -e "${BLUE}[-->]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } + +[[ "$EUID" -ne 0 ]] && error "Ejecutar como root: sudo bash prerequisites.sh" + +# Detectar OS +if [[ -f /etc/os-release ]]; then + . /etc/os-release + OS_ID="$ID" + OS_VER="$VERSION_ID" +else + error "No se puede detectar el sistema operativo" +fi + +[[ "$OS_ID" == "debian" || "$OS_ID" == "ubuntu" ]] || \ + error "Solo soportado en Debian/Ubuntu. Detectado: $OS_ID" + +echo "" +echo "=================================================" +echo " COCONEWS - Instalador de Prerequisites" +echo " OS: $PRETTY_NAME" +echo "=================================================" +echo "" + +# ============================================================================= +# 1. PAQUETES APT BASE +# ============================================================================= +step "Actualizando repositorios apt..." +apt-get update -qq + +step "Instalando paquetes del sistema..." +apt-get install -y --no-install-recommends \ + curl wget git build-essential \ + ca-certificates gnupg lsb-release \ + software-properties-common apt-transport-https \ + tzdata locales \ + rsync \ + openssl \ + libpq-dev \ + libssl-dev \ + libffi-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + zlib1g-dev +info "Paquetes base instalados" + +# ============================================================================= +# 2. POSTGRESQL 16 +# ============================================================================= +step "Instalando PostgreSQL 16..." +if ! command -v psql &>/dev/null; then + # Repositorio oficial de PostgreSQL + install -d /usr/share/postgresql-common/pgdg + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + | gpg --dearmor -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg + echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg] \ +https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \ + > /etc/apt/sources.list.d/pgdg.list + apt-get update -qq + apt-get install -y postgresql-16 postgresql-client-16 +fi +info "PostgreSQL: $(psql --version)" + +# ============================================================================= +# 3. REDIS +# ============================================================================= +step "Instalando Redis..." +if ! command -v redis-server &>/dev/null; then + apt-get install -y redis-server +fi +info "Redis: $(redis-server --version | cut -d' ' -f3)" + +# ============================================================================= +# 4. NGINX +# ============================================================================= +step "Instalando Nginx..." +if ! command -v nginx &>/dev/null; then + apt-get install -y nginx +fi +info "Nginx: $(nginx -v 2>&1 | cut -d'/' -f2)" + +# ============================================================================= +# 5. PYTHON 3 + pip + venv +# ============================================================================= +step "Instalando Python 3..." +apt-get install -y python3 python3-pip python3-venv python3-dev +info "Python: $(python3 --version)" + +# ============================================================================= +# 6. NODE.JS 20 LTS +# ============================================================================= +step "Instalando Node.js 20 LTS..." +if ! command -v node &>/dev/null || [[ "$(node -v | tr -d 'v' | cut -d. -f1)" -lt 18 ]]; then + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs +fi +info "Node.js: $(node --version) | npm: $(npm --version)" + +# ============================================================================= +# 7. GO 1.25 +# ============================================================================= +step "Instalando Go 1.25..." +GO_VERSION="1.25.0" +INSTALLED_GO=$(go version 2>/dev/null | awk '{print $3}' | tr -d 'go' || echo "0") + +# Comparar version instalada +needs_go=false +if ! command -v go &>/dev/null; then + needs_go=true +else + IFS='.' read -ra INS <<< "$INSTALLED_GO" + IFS='.' read -ra REQ <<< "$GO_VERSION" + if [[ "${INS[0]}" -lt "${REQ[0]}" ]] || \ + ([[ "${INS[0]}" == "${REQ[0]}" ]] && [[ "${INS[1]:-0}" -lt "${REQ[1]:-0}" ]]); then + needs_go=true + fi +fi + +if [[ "$needs_go" == "true" ]]; then + ARCH=$(dpkg --print-architecture) + case "$ARCH" in + amd64) GO_ARCH="amd64" ;; + arm64) GO_ARCH="arm64" ;; + *) error "Arquitectura no soportada para Go: $ARCH" ;; + esac + step " Descargando Go ${GO_VERSION} (${GO_ARCH})..." + curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz + rm -rf /usr/local/go + tar -C /usr/local -xzf /tmp/go.tar.gz + rm /tmp/go.tar.gz + # Perfil global + cat > /etc/profile.d/golang.sh << 'GOEOF' +export PATH=$PATH:/usr/local/go/bin +export GOPATH=$HOME/go +export PATH=$PATH:$GOPATH/bin +GOEOF + chmod +x /etc/profile.d/golang.sh + export PATH=$PATH:/usr/local/go/bin + info "Go instalado: $(go version)" +else + export PATH=$PATH:/usr/local/go/bin + info "Go ya instalado: $(go version)" +fi + +# ============================================================================= +# 8. QDRANT (binario oficial) +# ============================================================================= +step "Instalando Qdrant..." +QDRANT_VERSION="v1.12.1" +QDRANT_INSTALL_DIR="/opt/rss2/qdrant" +mkdir -p "$QDRANT_INSTALL_DIR" + +if [[ ! -f "$QDRANT_INSTALL_DIR/qdrant" ]]; then + ARCH=$(dpkg --print-architecture) + case "$ARCH" in + amd64) QDRANT_ARCH="x86_64-unknown-linux-musl" ;; + arm64) QDRANT_ARCH="aarch64-unknown-linux-musl" ;; + *) error "Arquitectura no soportada para Qdrant: $ARCH" ;; + esac + step " Descargando Qdrant ${QDRANT_VERSION}..." + curl -fsSL \ + "https://github.com/qdrant/qdrant/releases/download/${QDRANT_VERSION}/qdrant-${QDRANT_ARCH}.tar.gz" \ + -o /tmp/qdrant.tar.gz + tar -C "$QDRANT_INSTALL_DIR" -xzf /tmp/qdrant.tar.gz + chmod +x "$QDRANT_INSTALL_DIR/qdrant" + rm /tmp/qdrant.tar.gz + info "Qdrant ${QDRANT_VERSION} instalado en ${QDRANT_INSTALL_DIR}" +else + info "Qdrant ya instalado en ${QDRANT_INSTALL_DIR}" +fi + +# ============================================================================= +# 9. USUARIO DEL SISTEMA rss2 +# ============================================================================= +step "Creando usuario del sistema 'rss2'..." +if ! id rss2 &>/dev/null; then + useradd -r -m -d /opt/rss2 -s /bin/bash rss2 + info "Usuario 'rss2' creado" +else + info "Usuario 'rss2' ya existe" +fi + +# Crear estructura de directorios +mkdir -p \ + /opt/rss2/bin \ + /opt/rss2/src \ + /opt/rss2/data/wiki_images \ + /opt/rss2/data/qdrant_storage \ + /opt/rss2/hf_cache \ + /opt/rss2/models \ + /opt/rss2/frontend/dist \ + /opt/rss2/logs \ + /opt/rss2/backups +chown -R rss2:rss2 /opt/rss2 +info "Directorios /opt/rss2 creados" + +# ============================================================================= +# 10. PYTHON VIRTUALENV + DEPENDENCIAS BASE +# ============================================================================= +step "Creando virtualenv Python en /opt/rss2/venv..." +if [[ ! -d /opt/rss2/venv ]]; then + python3 -m venv /opt/rss2/venv +fi +/opt/rss2/venv/bin/pip install --upgrade pip setuptools wheel -q +info "Virtualenv listo" + +# Dependencias base (sin los modelos pesados de ML) +step "Instalando dependencias Python base..." +/opt/rss2/venv/bin/pip install -q \ + psycopg2-binary \ + langdetect \ + python-dotenv \ + requests \ + beautifulsoup4 \ + lxml \ + redis \ + qdrant-client \ + numpy \ + scikit-learn \ + tqdm +info "Dependencias Python base instaladas" + +# ============================================================================= +# 11. DEPENDENCIAS ML (pesadas - opcional en este paso) +# ============================================================================= +echo "" +echo -e "${YELLOW}[?]${NC} Instalar dependencias ML pesadas ahora?" +echo " (ctranslate2, transformers, sentence-transformers, spaCy)" +echo " Puede tardar 20-40 minutos y usar ~5 GB de disco." +echo -n " [s/N]: " +read -r install_ml + +if [[ "${install_ml,,}" == "s" || "${install_ml,,}" == "si" || "${install_ml,,}" == "y" ]]; then + step "Instalando dependencias ML (esto tarda)..." + /opt/rss2/venv/bin/pip install -q \ + ctranslate2>=4.0.0 \ + transformers==4.43.3 \ + sentencepiece \ + sacremoses \ + accelerate \ + sentence-transformers==3.0.1 \ + "spacy>=3.7,<4.0" \ + torch --index-url https://download.pytorch.org/whl/cpu + info "Dependencias ML instaladas" + + step "Descargando modelo spaCy en español..." + /opt/rss2/venv/bin/python -m spacy download es_core_news_lg + info "Modelo spaCy es_core_news_lg listo" + + step "Convirtiendo modelo NLLB-200 a CTranslate2..." + warn "Esto puede tardar 10-30 minutos y requiere ~2 GB de RAM" + mkdir -p /opt/rss2/models + /opt/rss2/venv/bin/python - <<'EOF' +import os, sys +os.makedirs("/opt/rss2/models/nllb-ct2", exist_ok=True) +os.environ["HF_HOME"] = "/opt/rss2/hf_cache" +try: + from ctranslate2.converters import OpusMTConverter + converter = OpusMTConverter("facebook/nllb-200-distilled-600M") + converter.convert("/opt/rss2/models/nllb-ct2", quantization="int8", force=True) + print("[OK] Modelo NLLB-200 convertido en /opt/rss2/models/nllb-ct2") +except Exception as e: + print(f"[ERROR] {e}") + print("Convierte manualmente despues con: deploy/debian/convert_model.sh") + sys.exit(0) +EOF + chown -R rss2:rss2 /opt/rss2/models /opt/rss2/hf_cache +else + warn "ML omitido. Ejecuta 'deploy/debian/install.sh' para instalarlas junto con el resto." + warn "Sin ML: la traduccion y los embeddings no funcionaran." +fi + +chown -R rss2:rss2 /opt/rss2 + +# ============================================================================= +# RESUMEN +# ============================================================================= +echo "" +echo "=================================================" +echo -e " ${GREEN}Prerequisites instalados correctamente${NC}" +echo "=================================================" +echo "" +echo " Sistema: $PRETTY_NAME" +echo " Go: $(go version 2>/dev/null | awk '{print $3}')" +echo " Python: $(python3 --version)" +echo " Node.js: $(node --version)" +echo " PostgreSQL: $(psql --version | awk '{print $3}')" +echo " Redis: $(redis-server --version | awk '{print $3}' | tr -d ',')" +echo " Nginx: $(nginx -v 2>&1 | cut -d'/' -f2)" +echo "" +echo " Siguiente paso:" +echo " sudo bash deploy/debian/install.sh" +echo "" diff --git a/poc/poc.sh b/poc/poc.sh new file mode 100755 index 0000000..b51552a --- /dev/null +++ b/poc/poc.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash +# ============================================================================= +# COCONEWS - POC local (sin Docker, sin ML workers) +# Levanta backend + frontend con datos de prueba en ~2 minutos +# +# Requisitos mínimos: Go 1.25, Node.js 18+, PostgreSQL, Redis +# Uso: bash poc/poc.sh +# ============================================================================= +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +POC_DIR="$REPO_ROOT/poc" +TMP_DIR="/tmp/coconews-poc" +PID_FILE="$TMP_DIR/pids" + +export PATH=$PATH:/usr/local/go/bin + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m' +info() { echo -e "${GREEN}[✓]${NC} $*"; } +step() { echo -e "${BLUE}[→]${NC} $*"; } +warn() { echo -e "${YELLOW}[!]${NC} $*"; } +error() { echo -e "${RED}[✗]${NC} $*"; exit 1; } + +# Manejar Ctrl+C: para todos los procesos del POC +cleanup() { + echo "" + warn "Deteniendo POC..." + if [[ -f "$PID_FILE" ]]; then + while IFS= read -r pid; do + kill "$pid" 2>/dev/null || true + done < "$PID_FILE" + rm -f "$PID_FILE" + fi + echo -e "${YELLOW}POC detenido.${NC}" + exit 0 +} +trap cleanup INT TERM + +mkdir -p "$TMP_DIR" +> "$PID_FILE" + +echo "" +echo -e "${BOLD}=================================================${NC}" +echo -e "${BOLD} COCONEWS · POC Local${NC}" +echo -e "${BOLD}=================================================${NC}" +echo "" + +# ============================================================================= +# 1. VERIFICAR PREREQUISITOS +# ============================================================================= +step "Verificando prerequisitos..." +command -v go &>/dev/null || error "Go no encontrado. Instala Go 1.25+." +command -v psql &>/dev/null || error "PostgreSQL no encontrado. Instala postgresql." +command -v redis-cli &>/dev/null || error "Redis no encontrado. Instala redis-server." +command -v node &>/dev/null || error "Node.js no encontrado. Instala Node.js 18+." +info "Prerequisitos OK (Go: $(go version | awk '{print $3}'), Node: $(node -v))" + +# ============================================================================= +# 2. CONFIGURACION POC (sin contrasenas fuertes, solo local) +# ============================================================================= +POC_DB_NAME="coconews_poc" +POC_DB_USER="coconews_poc" +POC_DB_PASS="poc_password_local" +POC_REDIS_PORT="6380" # Puerto alternativo para no interferir con Redis principal +POC_API_PORT="18080" +POC_FRONTEND_PORT="18001" + +export DATABASE_URL="postgres://${POC_DB_USER}:${POC_DB_PASS}@127.0.0.1:5432/${POC_DB_NAME}?sslmode=disable" +export REDIS_URL="redis://127.0.0.1:${POC_REDIS_PORT}" +export SECRET_KEY="poc_secret_key_solo_para_desarrollo_local" +export SERVER_PORT="$POC_API_PORT" +export GIN_MODE="release" + +# ============================================================================= +# 3. POSTGRESQL - crear DB de prueba +# ============================================================================= +step "Preparando base de datos POC (${POC_DB_NAME})..." + +# Verificar que PostgreSQL está corriendo +pg_isready -q || { + warn "PostgreSQL no está corriendo. Intentando iniciar..." + sudo systemctl start postgresql 2>/dev/null || \ + sudo service postgresql start 2>/dev/null || \ + error "No se puede iniciar PostgreSQL. Inícialo manualmente." +} + +# Crear usuario y BD de prueba +sudo -u postgres psql -q -tc "SELECT 1 FROM pg_roles WHERE rolname='${POC_DB_USER}'" \ + | grep -q 1 || \ + sudo -u postgres psql -q -c "CREATE USER ${POC_DB_USER} WITH PASSWORD '${POC_DB_PASS}';" 2>/dev/null + +sudo -u postgres psql -q -tc "SELECT 1 FROM pg_database WHERE datname='${POC_DB_NAME}'" \ + | grep -q 1 || \ + sudo -u postgres createdb -O "${POC_DB_USER}" "${POC_DB_NAME}" 2>/dev/null + +# Aplicar schema completo +sudo -u postgres psql -q -d "${POC_DB_NAME}" \ + -f "$REPO_ROOT/init-db/00-complete-schema.sql" 2>/dev/null || true + +# Otorgar permisos al usuario POC +sudo -u postgres psql -q -d "${POC_DB_NAME}" \ + -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${POC_DB_USER}; + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${POC_DB_USER};" \ + 2>/dev/null || true + +# Cargar datos de muestra +sudo -u postgres psql -q -d "${POC_DB_NAME}" -f "$POC_DIR/seed.sql" 2>/dev/null || true + +NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' ') +info "BD lista: ${NEWS_COUNT} noticias de prueba cargadas" + +# ============================================================================= +# 4. REDIS (instancia temporal en puerto alternativo) +# ============================================================================= +step "Iniciando Redis en puerto ${POC_REDIS_PORT}..." +redis-server --port "$POC_REDIS_PORT" --daemonize yes \ + --logfile "$TMP_DIR/redis-poc.log" \ + --pidfile "$TMP_DIR/redis-poc.pid" \ + --maxmemory 128mb --maxmemory-policy allkeys-lru \ + 2>/dev/null || warn "Redis ya corriendo en ${POC_REDIS_PORT}" +sleep 1 +redis-cli -p "$POC_REDIS_PORT" ping &>/dev/null && info "Redis OK (puerto ${POC_REDIS_PORT})" +cat "$TMP_DIR/redis-poc.pid" 2>/dev/null >> "$PID_FILE" || true + +# ============================================================================= +# 5. COMPILAR BACKEND GO +# ============================================================================= +step "Compilando backend Go..." +mkdir -p "$TMP_DIR/bin" + +(cd "$REPO_ROOT/backend" && \ + CGO_ENABLED=0 go build -buildvcs=false -o "$TMP_DIR/bin/server" ./cmd/server \ + 2>"$TMP_DIR/build-backend.log") || { + cat "$TMP_DIR/build-backend.log" + error "Fallo al compilar backend. Ver log arriba." +} +info "Backend compilado OK" + +# ============================================================================= +# 6. ARRANCAR BACKEND API +# ============================================================================= +step "Arrancando API en puerto ${POC_API_PORT}..." +"$TMP_DIR/bin/server" > "$TMP_DIR/backend.log" 2>&1 & +BACKEND_PID=$! +echo "$BACKEND_PID" >> "$PID_FILE" + +# Esperar a que el backend responda +for i in {1..15}; do + sleep 1 + if curl -sf "http://127.0.0.1:${POC_API_PORT}/api/stats" &>/dev/null; then + info "API respondiendo en http://127.0.0.1:${POC_API_PORT}" + break + fi + if [[ $i -eq 15 ]]; then + cat "$TMP_DIR/backend.log" + error "El backend no responde. Ver log arriba." + fi +done + +# ============================================================================= +# 7. FRONTEND REACT +# ============================================================================= +step "Preparando frontend..." +cd "$REPO_ROOT/frontend" + +if [[ ! -d node_modules ]]; then + step " Instalando dependencias npm (primera vez)..." + npm install --silent +fi + +# Compilar apuntando al API local del POC +VITE_API_URL="http://127.0.0.1:${POC_API_PORT}" \ +npm run build -- --outDir "$TMP_DIR/frontend-dist" 2>"$TMP_DIR/build-frontend.log" || { + cat "$TMP_DIR/build-frontend.log" + error "Fallo al compilar frontend. Ver log arriba." +} +info "Frontend compilado OK" + +# Servir frontend con npx serve (simple, sin nginx) +step "Sirviendo frontend en puerto ${POC_FRONTEND_PORT}..." +npx --yes serve "$TMP_DIR/frontend-dist" -l "$POC_FRONTEND_PORT" \ + > "$TMP_DIR/frontend.log" 2>&1 & +FRONTEND_PID=$! +echo "$FRONTEND_PID" >> "$PID_FILE" +sleep 2 + +cd "$REPO_ROOT" + +# ============================================================================= +# LISTO +# ============================================================================= +echo "" +echo -e "${BOLD}${GREEN}=================================================${NC}" +echo -e "${BOLD}${GREEN} COCONEWS POC corriendo${NC}" +echo -e "${BOLD}${GREEN}=================================================${NC}" +echo "" +echo -e " ${BOLD}Frontend:${NC} http://127.0.0.1:${POC_FRONTEND_PORT}" +echo -e " ${BOLD}API:${NC} http://127.0.0.1:${POC_API_PORT}/api/stats" +echo "" +echo -e " ${BOLD}Login:${NC} Registra el primer usuario en la UI" +echo -e " (será admin automáticamente)" +echo "" +echo -e " ${BOLD}Noticias:${NC} ${NEWS_COUNT} artículos de prueba en español" +echo -e " ${YELLOW}Nota:${NC} Sin workers ML activos." +echo -e " Noticias no tendrán traducción ni entidades." +echo "" +echo -e " ${BLUE}Logs:${NC} $TMP_DIR/*.log" +echo "" +echo -e " Pulsa ${BOLD}Ctrl+C${NC} para detener el POC." +echo "" + +# Mantener el script corriendo +wait diff --git a/poc/seed.sql b/poc/seed.sql new file mode 100644 index 0000000..44772d7 --- /dev/null +++ b/poc/seed.sql @@ -0,0 +1,94 @@ +-- ============================================================================= +-- COCONEWS POC - Datos de prueba mínimos +-- Carga rápida para ver la interfaz funcionando sin workers ML +-- ============================================================================= + +-- Taxonomía base +INSERT INTO continentes (id, nombre) VALUES + (1, 'África'), (2, 'América'), (3, 'Asia'), + (4, 'Europa'), (5, 'Oceanía') +ON CONFLICT (id) DO NOTHING; + +INSERT INTO categorias (nombre) VALUES + ('Ciencia'), ('Cultura'), ('Deportes'), ('Economía'), + ('Internacional'), ('Política'), ('Salud'), ('Tecnología'), ('Sociedad') +ON CONFLICT DO NOTHING; + +INSERT INTO paises (nombre, continente_id) VALUES + ('España', 4), + ('Argentina', 2), + ('México', 2), + ('Francia', 4), + ('Estados Unidos', 2) +ON CONFLICT DO NOTHING; + +-- Config básica +INSERT INTO config (key, value) VALUES + ('translator_type', 'cpu'), + ('translator_workers', '1'), + ('translator_status', 'stopped') +ON CONFLICT (key) DO NOTHING; + +-- Feeds de muestra (en español, no necesitan traducción) +INSERT INTO feeds (nombre, descripcion, url, idioma, activo, fallos) +VALUES + ('El País', 'Noticias de España y el mundo', 'https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada', 'es', true, 0), + ('El Mundo', 'Diario de información general', 'https://e00-elmundo.uecdn.es/elmundo/rss/portada.xml', 'es', true, 0), + ('La Vanguardia','Noticias de España y Cataluña', 'https://www.lavanguardia.com/mvc/feed/rss/home', 'es', true, 0), + ('BBC Mundo', 'Noticias en español de la BBC', 'https://feeds.bbci.co.uk/mundo/rss.xml', 'es', true, 0), + ('RT Español', 'Russia Today en español', 'https://actualidad.rt.com/rss', 'es', true, 0) +ON CONFLICT (url) DO NOTHING; + +-- Noticias de muestra (en español, listas para mostrarse sin traducción) +INSERT INTO noticias (id, titulo, resumen, url, fecha, fuente_nombre, categoria_id, lang, topics_processed) +VALUES + (md5('poc-001'), 'La inteligencia artificial transforma el mercado laboral global', + 'Los modelos de lenguaje de gran escala están redefiniendo sectores enteros de la economía, desde el servicio al cliente hasta el desarrollo de software. Empresas de todo el mundo aceleran su adopción mientras sindicatos y gobiernos debaten marcos regulatorios.', + 'https://example.com/ia-mercado-laboral', NOW() - INTERVAL '2 hours', + 'El País', 8, 'es', false), + + (md5('poc-002'), 'Cumbre climática de la ONU aprueba fondo de 100.000 millones para países vulnerables', + 'Los representantes de 196 países alcanzaron un acuerdo histórico en la última jornada de negociaciones. El fondo estará operativo en 2026 y priorizará adaptación en África subsahariana y pequeñas islas del Pacífico.', + 'https://example.com/cumbre-climatica-onu', NOW() - INTERVAL '4 hours', + 'BBC Mundo', 5, 'es', false), + + (md5('poc-003'), 'España registra el mayor crecimiento económico de la eurozona en el primer trimestre', + 'El PIB español creció un 3,2% interanual en los primeros tres meses del año, impulsado por el turismo, las exportaciones y el consumo interno. El Banco de España revisa al alza sus previsiones para el conjunto del ejercicio.', + 'https://example.com/economia-espana-pib', NOW() - INTERVAL '5 hours', + 'El Mundo', 4, 'es', false), + + (md5('poc-004'), 'La selección española de fútbol golea en la fase de clasificación', + 'La Roja aplastó por 4-0 al combinado rival con dos goles de Yamal y otros tantos de Morata en un partido que dejó pocas dudas sobre el potencial del equipo de Luis de la Fuente de cara al próximo gran torneo.', + 'https://example.com/seleccion-espana-futbol', NOW() - INTERVAL '6 hours', + 'La Vanguardia', 3, 'es', false), + + (md5('poc-005'), 'Descubrimiento arqueológico en Extremadura revela ciudad romana inédita', + 'Un equipo de la Universidad de Extremadura ha localizado los restos de un asentamiento romano del siglo II d.C. con teatro, termas y foro en perfecto estado de conservación bajo un olivar de la comarca de La Serena.', + 'https://example.com/arqueologia-extremadura', NOW() - INTERVAL '8 hours', + 'El País', 2, 'es', false), + + (md5('poc-006'), 'Nuevo fármaco contra el Alzheimer obtiene aprobación de la EMA', + 'La Agencia Europea del Medicamento ha dado luz verde al primer tratamiento que demuestra ralentizar significativamente el deterioro cognitivo en fases tempranas. El medicamento llegará a las farmacias europeas antes de final de año.', + 'https://example.com/farmaco-alzheimer-ema', NOW() - INTERVAL '10 hours', + 'BBC Mundo', 7, 'es', false), + + (md5('poc-007'), 'México anuncia plan de inversión en energías renovables por 50.000 millones', + 'El gobierno mexicano presentó su Estrategia Nacional de Transición Energética que contempla duplicar la capacidad solar y eólica instalada antes de 2030, con fuerte participación de capital privado nacional e internacional.', + 'https://example.com/mexico-renovables', NOW() - INTERVAL '12 hours', + 'RT Español', 5, 'es', false), + + (md5('poc-008'), 'OpenAI lanza modelo multimodal capaz de generar video fotorrealista en tiempo real', + 'La empresa californiana presentó Sora 2, capaz de producir secuencias de vídeo de alta definición en menos de 30 segundos. Investigadores advierten sobre los riesgos de desinformación mientras la compañía promete mecanismos de marca de agua.', + 'https://example.com/openai-sora2', NOW() - INTERVAL '14 hours', + 'El Mundo', 8, 'es', false), + + (md5('poc-009'), 'Argentina cierra acuerdo comercial con la Unión Europea tras 25 años de negociaciones', + 'El tratado de libre comercio Mercosur-UE entra en vigor de forma provisional tras superar los últimos obstáculos relacionados con protección ambiental y acceso al mercado agrícola europeo para los productos del cono sur.', + 'https://example.com/argentina-acuerdo-ue', NOW() - INTERVAL '18 hours', + 'BBC Mundo', 4, 'es', false), + + (md5('poc-010'), 'Telescopio James Webb detecta atmósfera en exoplaneta a 40 años luz de la Tierra', + 'Astrónomos del Instituto de Tecnología de California confirmaron la presencia de dióxido de carbono y vapor de agua en la atmósfera del exoplaneta K2-18b, abriendo nuevas posibilidades en la búsqueda de condiciones habitables fuera del sistema solar.', + 'https://example.com/james-webb-exoplaneta', NOW() - INTERVAL '22 hours', + 'El País', 1, 'es', false) +ON CONFLICT (id) DO NOTHING; From ec839b5b54b629e12c15a95274eab815c5c5a5cc Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 22:04:22 +0200 Subject: [PATCH 05/21] feat(debug): troubleshooting y diagnostico en scripts poc/poc.sh: - Verificacion de versiones: Go >=1.22, Node.js >=18 con mensaje de fix exacto - Deteccion de puertos en uso antes de arrancar (API, frontend, Redis) con instruccion de quien ocupa el puerto - show_log_tail(): muestra solo las lineas relevantes del log al fallar - Compilacion Go: filtra lineas de error reales (error:/undefined:) en vez de volcar todo el log - Backend no responde: sugiere probar DB y Redis individualmente con el comando exacto para diagnosticar - Frontend: distingue error de npm install vs error de build TypeScript - Flag --clean para borrar BD POC y empezar de cero - Logs separados por componente en /tmp/coconews-poc/logs/ deploy/debian/check.sh (nuevo): - Diagnostico completo del sistema post-instalacion - Verifica 16 servicios systemd con estado y fix especifico por cada uno - Prueba conectividad real: PostgreSQL, Redis (con auth), Qdrant HTTP, API Go, nginx - Muestra metricas de BD: total noticias, traducciones hechas y pendientes - Verifica binarios Go compilados y su tamano - Verifica modelos ML: NLLB-200, spaCy es_core_news_lg, sentence-transformers, ctranslate2 - Comprueba disco (avisa si >75% o >90%), permisos de /opt/rss2 y .env - Detecta si .env tiene valores por defecto sin cambiar - Modo --quick para ver solo estado arriba/abajo rapidamente - Resumen final con conteo de errores y advertencias, exit code 1 si hay errores deploy/debian/prerequisites.sh: - Comprobacion de espacio libre en disco al inicio (avisa si <10 GB) - apt-get update con log de error y sugerencias de fix - Seccion de troubleshooting en el resumen final con fixes comunes Co-Authored-By: Claude Sonnet 4.6 --- deploy/debian/check.sh | 342 ++++++++++++++++++++++++++++++++ deploy/debian/prerequisites.sh | 26 ++- poc/poc.sh | 352 ++++++++++++++++++++++++++------- 3 files changed, 651 insertions(+), 69 deletions(-) create mode 100755 deploy/debian/check.sh diff --git a/deploy/debian/check.sh b/deploy/debian/check.sh new file mode 100755 index 0000000..c001e0b --- /dev/null +++ b/deploy/debian/check.sh @@ -0,0 +1,342 @@ +#!/usr/bin/env bash +# ============================================================================= +# COCONEWS - Diagnóstico del sistema +# Verifica que todos los servicios estén OK y muestra estado + sugerencias +# +# Uso: +# bash deploy/debian/check.sh # diagnóstico completo +# bash deploy/debian/check.sh --quick # solo servicios arriba/abajo +# ============================================================================= + +RSS2_HOME="/opt/rss2" +API_PORT="8080" +QUICK=false +[[ "${1:-}" == "--quick" ]] && QUICK=true + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +BLUE='\033[0;34m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m' +CYAN='\033[0;36m' + +OK() { echo -e " ${GREEN}[✓]${NC} $*"; } +FAIL() { echo -e " ${RED}[✗]${NC} $*"; } +WARN() { echo -e " ${YELLOW}[!]${NC} $*"; } +INFO() { echo -e " ${DIM} $*${NC}"; } +HEAD() { echo -e "\n${BOLD}${CYAN}── $* ${NC}"; } +FIX() { echo -e " ${YELLOW} → Fix:${NC} $*"; } + +ERRORS=0 +WARNINGS=0 + +fail_with_fix() { + FAIL "$1" + shift + for fix in "$@"; do FIX "$fix"; done + (( ERRORS++ )) || true +} + +warn_with_fix() { + WARN "$1" + shift + for fix in "$@"; do FIX "$fix"; done + (( WARNINGS++ )) || true +} + +echo "" +echo -e "${BOLD}╔═══════════════════════════════════════════╗${NC}" +echo -e "${BOLD}║ COCONEWS · Diagnóstico del Sistema ║${NC}" +echo -e "${BOLD}╚═══════════════════════════════════════════╝${NC}" +echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S') · $(hostname)${NC}" + +# ============================================================================= +# 1. SERVICIOS SYSTEMD +# ============================================================================= +HEAD "Servicios systemd" + +GO_SERVICES=( + "rss2-backend:API REST Go" + "rss2-ingestor:Ingestor RSS" + "rss2-scraper:Scraper HTML" + "rss2-discovery:Discovery de feeds" + "rss2-wiki:Wiki worker" + "rss2-topics:Topics matcher" + "rss2-related:Related news" + "rss2-qdrant-worker:Qdrant sync" +) +PY_SERVICES=( + "rss2-langdetect:Detección de idioma" + "rss2-translation-scheduler:Scheduler traducción" + "rss2-translator:Traductor NLLB-200" + "rss2-embeddings:Embeddings ML" + "rss2-ner:NER entidades" + "rss2-cluster:Clustering eventos" + "rss2-categorizer:Categorizador" +) +INFRA_SERVICES=( + "rss2-qdrant:Vector DB Qdrant" + "postgresql:PostgreSQL" + "redis-server:Redis" + "nginx:Nginx" +) + +check_service() { + local svc="${1%%:*}" + local name="${1##*:}" + if ! command -v systemctl &>/dev/null; then + WARN "$name (systemctl no disponible)" + return + fi + local state + state=$(systemctl is-active "$svc" 2>/dev/null || echo "unknown") + case "$state" in + active) + OK "$name ($svc)" + ;; + failed) + fail_with_fix "$name ($svc) — FAILED" \ + "journalctl -u $svc -n 30 --no-pager" \ + "systemctl restart $svc" + ;; + inactive) + warn_with_fix "$name ($svc) — inactivo" \ + "systemctl start $svc" \ + "systemctl enable $svc" + ;; + *) + warn_with_fix "$name ($svc) — $state" \ + "systemctl status $svc" + ;; + esac +} + +echo -e "\n ${DIM}Infraestructura:${NC}" +for svc in "${INFRA_SERVICES[@]}"; do check_service "$svc"; done + +echo -e "\n ${DIM}Workers Go:${NC}" +for svc in "${GO_SERVICES[@]}"; do check_service "$svc"; done + +echo -e "\n ${DIM}Workers Python (ML):${NC}" +for svc in "${PY_SERVICES[@]}"; do check_service "$svc"; done + +[[ "$QUICK" == "true" ]] && { + echo "" + [[ "$ERRORS" -eq 0 && "$WARNINGS" -eq 0 ]] && \ + echo -e " ${GREEN}${BOLD}Todo OK${NC}" || \ + echo -e " ${RED}Errores: $ERRORS ${YELLOW}Advertencias: $WARNINGS${NC}" + exit 0 +} + +# ============================================================================= +# 2. CONECTIVIDAD +# ============================================================================= +HEAD "Conectividad" + +# PostgreSQL +PG_ENV="$RSS2_HOME/.env" +if [[ -f "$PG_ENV" ]]; then + source "$PG_ENV" 2>/dev/null || true +fi +DB_NAME="${POSTGRES_DB:-rss}" +DB_USER="${POSTGRES_USER:-rss}" + +if pg_isready -q 2>/dev/null; then + OK "PostgreSQL acepta conexiones" + # Probar conexión con usuario rss2 + if sudo -u postgres psql -d "$DB_NAME" -c "SELECT 1" &>/dev/null 2>&1; then + NEWS=$(sudo -u postgres psql -tq -d "$DB_NAME" \ + -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' \n' || echo "?") + TRANS=$(sudo -u postgres psql -tq -d "$DB_NAME" \ + -c "SELECT COUNT(*) FROM traducciones WHERE status='done';" 2>/dev/null | tr -d ' \n' || echo "?") + PEND=$(sudo -u postgres psql -tq -d "$DB_NAME" \ + -c "SELECT COUNT(*) FROM traducciones WHERE status='pending';" 2>/dev/null | tr -d ' \n' || echo "?") + INFO "Base de datos: $DB_NAME" + INFO "Noticias: $NEWS | Traducciones hechas: $TRANS | Pendientes: $PEND" + else + warn_with_fix "No se puede conectar a la BD '$DB_NAME' con usuario '$DB_USER'" \ + "sudo -u postgres psql -c \"\\du\" # listar usuarios" \ + "Revisa DB_USER y DB_PASS en $RSS2_HOME/.env" + fi +else + fail_with_fix "PostgreSQL no responde" \ + "sudo systemctl start postgresql" \ + "sudo journalctl -u postgresql -n 20 --no-pager" \ + "sudo pg_ctlcluster 16 main status" +fi + +# Redis +REDIS_PASS="${REDIS_PASSWORD:-}" +REDIS_AUTH="" +[[ -n "$REDIS_PASS" ]] && REDIS_AUTH="-a $REDIS_PASS" +if redis-cli $REDIS_AUTH ping 2>/dev/null | grep -q PONG; then + REDIS_MEM=$(redis-cli $REDIS_AUTH info memory 2>/dev/null | grep used_memory_human | cut -d: -f2 | tr -d '\r' || echo "?") + OK "Redis responde (memoria usada: ${REDIS_MEM})" +else + fail_with_fix "Redis no responde" \ + "sudo systemctl start redis-server" \ + "redis-cli ping # sin auth" \ + "Verifica REDIS_PASSWORD en $RSS2_HOME/.env" +fi + +# Qdrant +if curl -sf "http://127.0.0.1:6333/healthz" &>/dev/null; then + QDRANT_VER=$(curl -sf "http://127.0.0.1:6333/" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('version','?'))" 2>/dev/null || echo "?") + OK "Qdrant responde (v${QDRANT_VER})" +else + warn_with_fix "Qdrant no responde en :6333" \ + "systemctl start rss2-qdrant" \ + "journalctl -u rss2-qdrant -n 20 --no-pager" \ + "Búsqueda semántica no funcionará hasta que Qdrant esté activo" +fi + +# API Backend Go +if curl -sf "http://127.0.0.1:${API_PORT}/api/stats" &>/dev/null; then + STATS=$(curl -sf "http://127.0.0.1:${API_PORT}/api/stats" 2>/dev/null | \ + python3 -c "import sys,json; d=json.load(sys.stdin); print(f\"noticias={d.get('total_news','?')} feeds={d.get('total_feeds','?')}\")" 2>/dev/null || echo "") + OK "API backend responde (:${API_PORT}) — $STATS" +else + fail_with_fix "API backend no responde en :${API_PORT}" \ + "systemctl restart rss2-backend" \ + "journalctl -u rss2-backend -n 30 --no-pager" \ + "curl http://127.0.0.1:${API_PORT}/api/stats # prueba manual" +fi + +# Nginx +if curl -sf "http://127.0.0.1:8001/health" &>/dev/null; then + OK "Nginx responde (:8001)" +else + fail_with_fix "Nginx no responde en :8001" \ + "nginx -t # verificar config" \ + "systemctl restart nginx" \ + "journalctl -u nginx -n 20 --no-pager" +fi + +# ============================================================================= +# 3. BINARIOS GO +# ============================================================================= +HEAD "Binarios compilados" + +BINS=(server ingestor scraper discovery wiki_worker topics related qdrant_worker) +for bin in "${BINS[@]}"; do + BIN_PATH="$RSS2_HOME/bin/$bin" + if [[ -x "$BIN_PATH" ]]; then + SIZE=$(du -sh "$BIN_PATH" 2>/dev/null | cut -f1) + OK "$bin (${SIZE})" + else + fail_with_fix "$bin no encontrado o sin permisos de ejecución en $BIN_PATH" \ + "sudo bash deploy/debian/build.sh # recompila todos los binarios" + fi +done + +# ============================================================================= +# 4. MODELOS ML +# ============================================================================= +HEAD "Modelos ML" + +# NLLB-200 CTranslate2 +if [[ -d "$RSS2_HOME/models/nllb-ct2" ]] && \ + [[ -f "$RSS2_HOME/models/nllb-ct2/model.bin" ]]; then + SIZE=$(du -sh "$RSS2_HOME/models/nllb-ct2" 2>/dev/null | cut -f1) + OK "NLLB-200 CTranslate2 (${SIZE})" +else + warn_with_fix "Modelo NLLB-200 no encontrado en $RSS2_HOME/models/nllb-ct2" \ + "El traductor no funcionará hasta que esté el modelo" \ + "Convierte con: ver sección 3 de DEPLOY_DEBIAN.md" +fi + +# spaCy +if "$RSS2_HOME/venv/bin/python" -c "import spacy; spacy.load('es_core_news_lg')" &>/dev/null 2>&1; then + OK "spaCy es_core_news_lg" +else + warn_with_fix "Modelo spaCy es_core_news_lg no disponible" \ + "$RSS2_HOME/venv/bin/python -m spacy download es_core_news_lg" \ + "El worker NER no funcionará hasta instalarlo" +fi + +# sentence-transformers (embeddings) +if "$RSS2_HOME/venv/bin/python" -c "from sentence_transformers import SentenceTransformer" &>/dev/null 2>&1; then + OK "sentence-transformers disponible" +else + warn_with_fix "sentence-transformers no instalado" \ + "$RSS2_HOME/venv/bin/pip install sentence-transformers==3.0.1" \ + "Los embeddings y búsqueda semántica no funcionarán" +fi + +# ctranslate2 +if "$RSS2_HOME/venv/bin/python" -c "import ctranslate2" &>/dev/null 2>&1; then + OK "ctranslate2 disponible" +else + warn_with_fix "ctranslate2 no instalado" \ + "$RSS2_HOME/venv/bin/pip install ctranslate2>=4.0.0" \ + "La traducción NLLB-200 no funcionará" +fi + +# ============================================================================= +# 5. DISCO Y PERMISOS +# ============================================================================= +HEAD "Disco y permisos" + +# Espacio en disco +DISK_FREE=$(df -h "$RSS2_HOME" 2>/dev/null | tail -1 | awk '{print $4}') +DISK_PCT=$(df "$RSS2_HOME" 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%') +if [[ "${DISK_PCT:-0}" -gt 90 ]]; then + fail_with_fix "Disco al ${DISK_PCT}% — quedan solo ${DISK_FREE}" \ + "du -sh $RSS2_HOME/* | sort -rh | head -10 # ver qué ocupa más" \ + "Los modelos ML necesitan ~5 GB libres" +elif [[ "${DISK_PCT:-0}" -gt 75 ]]; then + warn_with_fix "Disco al ${DISK_PCT}% (libre: ${DISK_FREE})" \ + "Revisa el espacio antes de descargar modelos ML" +else + OK "Disco: ${DISK_PCT}% usado, ${DISK_FREE} libres" +fi + +# Permisos de /opt/rss2 +if [[ -d "$RSS2_HOME" ]]; then + OWNER=$(stat -c '%U' "$RSS2_HOME" 2>/dev/null || echo "?") + if [[ "$OWNER" == "rss2" ]]; then + OK "Propietario de $RSS2_HOME: rss2" + else + warn_with_fix "Propietario de $RSS2_HOME es '$OWNER', debería ser 'rss2'" \ + "sudo chown -R rss2:rss2 $RSS2_HOME" + fi +fi + +# .env +if [[ -f "$RSS2_HOME/.env" ]]; then + ENV_PERMS=$(stat -c '%a' "$RSS2_HOME/.env" 2>/dev/null || echo "?") + if [[ "$ENV_PERMS" == "600" ]]; then + OK ".env con permisos 600 (correcto)" + else + warn_with_fix ".env con permisos ${ENV_PERMS} (debería ser 600)" \ + "chmod 600 $RSS2_HOME/.env" + fi + # Verificar que no quedan valores por defecto peligrosos + if grep -q "CAMBIA_ESTO\|changeme\|change_this" "$RSS2_HOME/.env" 2>/dev/null; then + warn_with_fix ".env tiene valores por defecto sin cambiar" \ + "nano $RSS2_HOME/.env # cambia POSTGRES_PASSWORD, REDIS_PASSWORD, SECRET_KEY" + fi +else + fail_with_fix ".env no encontrado en $RSS2_HOME/.env" \ + "cp deploy/debian/env.example $RSS2_HOME/.env" \ + "nano $RSS2_HOME/.env # edita contraseñas" +fi + +# ============================================================================= +# RESUMEN FINAL +# ============================================================================= +echo "" +echo -e "${BOLD}══════════════════════════════════════════════${NC}" +if [[ "$ERRORS" -eq 0 && "$WARNINGS" -eq 0 ]]; then + echo -e " ${GREEN}${BOLD}Sistema OK — todo funcionando correctamente${NC}" +elif [[ "$ERRORS" -eq 0 ]]; then + echo -e " ${YELLOW}${BOLD}Sistema funcional con $WARNINGS advertencia(s)${NC}" + echo -e " ${DIM}Las advertencias no bloquean el funcionamiento básico${NC}" +else + echo -e " ${RED}${BOLD}$ERRORS error(es) encontrado(s)${YELLOW} $WARNINGS advertencia(s)${NC}" + echo -e " ${DIM}Sigue los fixes indicados arriba para resolverlos${NC}" +fi +echo -e "${BOLD}══════════════════════════════════════════════${NC}" +echo "" +echo -e " ${DIM}Logs de servicios: journalctl -u rss2-backend -f${NC}" +echo -e " ${DIM}Diagnóstico rápido: bash deploy/debian/check.sh --quick${NC}" +echo "" + +exit $(( ERRORS > 0 ? 1 : 0 )) diff --git a/deploy/debian/prerequisites.sh b/deploy/debian/prerequisites.sh index 156b9c6..3f65328 100755 --- a/deploy/debian/prerequisites.sh +++ b/deploy/debian/prerequisites.sh @@ -36,8 +36,22 @@ echo "" # ============================================================================= # 1. PAQUETES APT BASE # ============================================================================= +# Comprobar espacio en disco (mínimo 10 GB libres) +DISK_FREE_GB=$(df / --output=avail -BG | tail -1 | tr -d 'G ') +if [[ "${DISK_FREE_GB:-0}" -lt 10 ]]; then + warn "Espacio libre en disco: ${DISK_FREE_GB} GB (recomendado mínimo 10 GB)" + warn "Los modelos ML necesitan ~5 GB. Continua bajo tu responsabilidad." +fi + step "Actualizando repositorios apt..." -apt-get update -qq +apt-get update -qq 2>/tmp/apt-update.log || { + echo -e "${RED}Error al actualizar repositorios apt.${NC}" + echo " Posibles causas:" + echo " • Sin conexión a internet" + echo " • Repositorios con errores: cat /tmp/apt-update.log" + echo " • Solución: apt-get update --fix-missing" + exit 1 +} step "Instalando paquetes del sistema..." apt-get install -y --no-install-recommends \ @@ -285,7 +299,7 @@ fi chown -R rss2:rss2 /opt/rss2 # ============================================================================= -# RESUMEN +# RESUMEN Y SIGUIENTES PASOS # ============================================================================= echo "" echo "=================================================" @@ -303,3 +317,11 @@ echo "" echo " Siguiente paso:" echo " sudo bash deploy/debian/install.sh" echo "" +echo " Si algo falló durante la instalación:" +echo " • apt falla: apt-get update --fix-missing" +echo " • Go no descarga: verifica conectividad → curl https://go.dev" +echo " • Qdrant no baja: descárgalo manualmente en" +echo " https://github.com/qdrant/qdrant/releases" +echo " • pip falla: python3 -m venv /opt/rss2/venv --clear" +echo " • Diagnóstico: bash deploy/debian/check.sh" +echo "" diff --git a/poc/poc.sh b/poc/poc.sh index b51552a..f18cd4c 100755 --- a/poc/poc.sh +++ b/poc/poc.sh @@ -5,6 +5,7 @@ # # Requisitos mínimos: Go 1.25, Node.js 18+, PostgreSQL, Redis # Uso: bash poc/poc.sh +# bash poc/poc.sh --clean (borra BD y empieza de cero) # ============================================================================= set -euo pipefail @@ -12,16 +13,37 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" POC_DIR="$REPO_ROOT/poc" TMP_DIR="/tmp/coconews-poc" PID_FILE="$TMP_DIR/pids" +LOG_DIR="$TMP_DIR/logs" +CLEAN_MODE=false + +[[ "${1:-}" == "--clean" ]] && CLEAN_MODE=true export PATH=$PATH:/usr/local/go/bin -RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m' +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +BLUE='\033[0;34m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m' + info() { echo -e "${GREEN}[✓]${NC} $*"; } step() { echo -e "${BLUE}[→]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } -error() { echo -e "${RED}[✗]${NC} $*"; exit 1; } +error() { + echo -e "${RED}[✗] $*${NC}" + echo -e "${DIM} → Logs en: $LOG_DIR/${NC}" + exit 1 +} -# Manejar Ctrl+C: para todos los procesos del POC +# Muestra las últimas líneas de un log con contexto +show_log_tail() { + local logfile="$1" + local lines="${2:-20}" + if [[ -f "$logfile" && -s "$logfile" ]]; then + echo -e "${DIM}--- últimas líneas de $(basename "$logfile") ---${NC}" + tail -n "$lines" "$logfile" | sed 's/^/ /' + echo -e "${DIM}--- fin del log ---${NC}" + fi +} + +# Manejar Ctrl+C cleanup() { echo "" warn "Deteniendo POC..." @@ -31,12 +53,15 @@ cleanup() { done < "$PID_FILE" rm -f "$PID_FILE" fi + # Parar Redis POC si está corriendo + [[ -f "$TMP_DIR/redis-poc.pid" ]] && \ + kill "$(cat "$TMP_DIR/redis-poc.pid")" 2>/dev/null || true echo -e "${YELLOW}POC detenido.${NC}" exit 0 } trap cleanup INT TERM -mkdir -p "$TMP_DIR" +mkdir -p "$TMP_DIR" "$LOG_DIR" "$TMP_DIR/bin" > "$PID_FILE" echo "" @@ -44,24 +69,87 @@ echo -e "${BOLD}=================================================${NC}" echo -e "${BOLD} COCONEWS · POC Local${NC}" echo -e "${BOLD}=================================================${NC}" echo "" +[[ "$CLEAN_MODE" == "true" ]] && warn "Modo --clean: se borrará la BD de prueba anterior" # ============================================================================= # 1. VERIFICAR PREREQUISITOS # ============================================================================= step "Verificando prerequisitos..." -command -v go &>/dev/null || error "Go no encontrado. Instala Go 1.25+." -command -v psql &>/dev/null || error "PostgreSQL no encontrado. Instala postgresql." -command -v redis-cli &>/dev/null || error "Redis no encontrado. Instala redis-server." -command -v node &>/dev/null || error "Node.js no encontrado. Instala Node.js 18+." -info "Prerequisitos OK (Go: $(go version | awk '{print $3}'), Node: $(node -v))" + +PREREQ_OK=true + +# Go +if ! command -v go &>/dev/null; then + echo -e " ${RED}[✗]${NC} Go no encontrado" + echo -e " Instala Go 1.25: https://go.dev/dl/" + echo -e " O en Debian: sudo bash deploy/debian/prerequisites.sh" + PREREQ_OK=false +else + GO_VER=$(go version | awk '{print $3}' | tr -d 'go') + IFS='.' read -ra V <<< "$GO_VER" + if [[ "${V[0]}" -lt 1 ]] || [[ "${V[0]}" -eq 1 && "${V[1]:-0}" -lt 22 ]]; then + echo -e " ${RED}[✗]${NC} Go ${GO_VER} instalado, se necesita 1.22+ (recomendado 1.25)" + PREREQ_OK=false + else + echo -e " ${GREEN}[✓]${NC} Go ${GO_VER}" + fi +fi + +# Node.js +if ! command -v node &>/dev/null; then + echo -e " ${RED}[✗]${NC} Node.js no encontrado" + echo -e " Instala: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -" + echo -e " sudo apt-get install -y nodejs" + PREREQ_OK=false +else + NODE_VER=$(node -v | tr -d 'v') + NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1) + if [[ "$NODE_MAJOR" -lt 18 ]]; then + echo -e " ${RED}[✗]${NC} Node.js v${NODE_VER} instalado, se necesita v18+" + PREREQ_OK=false + else + echo -e " ${GREEN}[✓]${NC} Node.js v${NODE_VER}" + fi +fi + +# PostgreSQL +if ! command -v psql &>/dev/null; then + echo -e " ${RED}[✗]${NC} psql no encontrado" + echo -e " Instala: sudo apt-get install -y postgresql postgresql-client" + PREREQ_OK=false +else + echo -e " ${GREEN}[✓]${NC} PostgreSQL $(psql --version | awk '{print $3}')" +fi + +# Redis +if ! command -v redis-cli &>/dev/null; then + echo -e " ${RED}[✗]${NC} redis-cli no encontrado" + echo -e " Instala: sudo apt-get install -y redis-server" + PREREQ_OK=false +else + echo -e " ${GREEN}[✓]${NC} Redis $(redis-server --version | awk '{print $3}' | tr -d ',')" +fi + +# curl (para health check del backend) +if ! command -v curl &>/dev/null; then + echo -e " ${RED}[✗]${NC} curl no encontrado" + echo -e " Instala: sudo apt-get install -y curl" + PREREQ_OK=false +fi + +[[ "$PREREQ_OK" == "false" ]] && { + echo "" + error "Faltan prerequisitos. Corrígelos y vuelve a ejecutar." +} +info "Todos los prerequisitos OK" # ============================================================================= -# 2. CONFIGURACION POC (sin contrasenas fuertes, solo local) +# 2. CONFIGURACION POC # ============================================================================= POC_DB_NAME="coconews_poc" POC_DB_USER="coconews_poc" POC_DB_PASS="poc_password_local" -POC_REDIS_PORT="6380" # Puerto alternativo para no interferir con Redis principal +POC_REDIS_PORT="6380" POC_API_PORT="18080" POC_FRONTEND_PORT="18001" @@ -71,68 +159,150 @@ export SECRET_KEY="poc_secret_key_solo_para_desarrollo_local" export SERVER_PORT="$POC_API_PORT" export GIN_MODE="release" +# Verificar que los puertos necesarios estén libres +check_port() { + local port="$1" name="$2" + if ss -tlnp 2>/dev/null | grep -q ":${port} " || \ + lsof -i ":${port}" &>/dev/null 2>&1; then + warn "Puerto ${port} (${name}) ya en uso." + echo -e " Proceso usando ese puerto:" + ss -tlnp 2>/dev/null | grep ":${port} " | sed 's/^/ /' || true + echo -e " Puedes cambiarlo editando las variables POC_*_PORT en poc.sh" + return 1 + fi + return 0 +} + +step "Verificando puertos disponibles..." +PORT_OK=true +check_port "$POC_API_PORT" "API backend" || PORT_OK=false +check_port "$POC_FRONTEND_PORT" "Frontend" || PORT_OK=false +check_port "$POC_REDIS_PORT" "Redis POC" || PORT_OK=false +[[ "$PORT_OK" == "false" ]] && error "Hay puertos en conflicto. Resuélvelos antes de continuar." +info "Puertos ${POC_API_PORT}, ${POC_FRONTEND_PORT}, ${POC_REDIS_PORT} disponibles" + # ============================================================================= -# 3. POSTGRESQL - crear DB de prueba +# 3. POSTGRESQL # ============================================================================= step "Preparando base de datos POC (${POC_DB_NAME})..." # Verificar que PostgreSQL está corriendo -pg_isready -q || { +if ! pg_isready -q 2>/dev/null; then warn "PostgreSQL no está corriendo. Intentando iniciar..." sudo systemctl start postgresql 2>/dev/null || \ - sudo service postgresql start 2>/dev/null || \ - error "No se puede iniciar PostgreSQL. Inícialo manualmente." -} + sudo service postgresql start 2>/dev/null || { + echo -e " ${RED}Fallo al iniciar PostgreSQL.${NC}" + echo -e " Posibles causas y soluciones:" + echo -e " • Ver logs: sudo journalctl -u postgresql -n 30" + echo -e " • Ver estado: sudo systemctl status postgresql" + echo -e " • Puerto en uso: sudo ss -tlnp | grep 5432" + echo -e " • Datos corruptos: sudo pg_ctlcluster 16 main status" + error "PostgreSQL no pudo iniciarse." + } + sleep 2 + pg_isready -q || error "PostgreSQL sigue sin responder tras el inicio." +fi +info "PostgreSQL corriendo" -# Crear usuario y BD de prueba +# Limpiar si --clean +if [[ "$CLEAN_MODE" == "true" ]]; then + sudo -u postgres psql -q -c "DROP DATABASE IF EXISTS ${POC_DB_NAME};" 2>/dev/null || true + sudo -u postgres psql -q -c "DROP USER IF EXISTS ${POC_DB_USER};" 2>/dev/null || true + info "BD anterior eliminada" +fi + +# Crear usuario POC sudo -u postgres psql -q -tc "SELECT 1 FROM pg_roles WHERE rolname='${POC_DB_USER}'" \ - | grep -q 1 || \ - sudo -u postgres psql -q -c "CREATE USER ${POC_DB_USER} WITH PASSWORD '${POC_DB_PASS}';" 2>/dev/null + | grep -q 1 2>/dev/null || \ + sudo -u postgres psql -q -c "CREATE USER ${POC_DB_USER} WITH PASSWORD '${POC_DB_PASS}';" \ + 2>"$LOG_DIR/psql-setup.log" || { + show_log_tail "$LOG_DIR/psql-setup.log" + echo -e " Verifica que tienes permisos sudo para el usuario postgres:" + echo -e " sudo -u postgres psql -c '\\du'" + error "No se pudo crear el usuario PostgreSQL ${POC_DB_USER}." + } +# Crear BD sudo -u postgres psql -q -tc "SELECT 1 FROM pg_database WHERE datname='${POC_DB_NAME}'" \ - | grep -q 1 || \ - sudo -u postgres createdb -O "${POC_DB_USER}" "${POC_DB_NAME}" 2>/dev/null + | grep -q 1 2>/dev/null || \ + sudo -u postgres createdb -O "${POC_DB_USER}" "${POC_DB_NAME}" \ + 2>"$LOG_DIR/psql-createdb.log" || { + show_log_tail "$LOG_DIR/psql-createdb.log" + error "No se pudo crear la base de datos ${POC_DB_NAME}." + } -# Aplicar schema completo +# Schema sudo -u postgres psql -q -d "${POC_DB_NAME}" \ - -f "$REPO_ROOT/init-db/00-complete-schema.sql" 2>/dev/null || true + -f "$REPO_ROOT/init-db/00-complete-schema.sql" \ + >"$LOG_DIR/psql-schema.log" 2>&1 || \ + warn "Algunos statements del schema fallaron (puede ser normal si ya existían)" -# Otorgar permisos al usuario POC +# Permisos sudo -u postgres psql -q -d "${POC_DB_NAME}" \ -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${POC_DB_USER}; - GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${POC_DB_USER};" \ + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${POC_DB_USER}; + GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ${POC_DB_USER};" \ 2>/dev/null || true -# Cargar datos de muestra -sudo -u postgres psql -q -d "${POC_DB_NAME}" -f "$POC_DIR/seed.sql" 2>/dev/null || true +# Seed +sudo -u postgres psql -q -d "${POC_DB_NAME}" \ + -f "$POC_DIR/seed.sql" >"$LOG_DIR/psql-seed.log" 2>&1 || \ + warn "Algunos inserts del seed fallaron (puede que ya existieran)" -NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' ') -info "BD lista: ${NEWS_COUNT} noticias de prueba cargadas" +NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" \ + -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' \n' || echo "?") +info "BD lista: ${NEWS_COUNT} noticias de prueba" # ============================================================================= # 4. REDIS (instancia temporal en puerto alternativo) # ============================================================================= step "Iniciando Redis en puerto ${POC_REDIS_PORT}..." -redis-server --port "$POC_REDIS_PORT" --daemonize yes \ - --logfile "$TMP_DIR/redis-poc.log" \ + +redis-server \ + --port "$POC_REDIS_PORT" \ + --daemonize yes \ + --logfile "$LOG_DIR/redis.log" \ --pidfile "$TMP_DIR/redis-poc.pid" \ - --maxmemory 128mb --maxmemory-policy allkeys-lru \ - 2>/dev/null || warn "Redis ya corriendo en ${POC_REDIS_PORT}" + --maxmemory 128mb \ + --maxmemory-policy allkeys-lru \ + 2>"$LOG_DIR/redis-start.log" || { + show_log_tail "$LOG_DIR/redis-start.log" + echo -e " Posibles causas:" + echo -e " • Puerto ${POC_REDIS_PORT} en uso: sudo ss -tlnp | grep ${POC_REDIS_PORT}" + echo -e " • Permisos: redis-server necesita poder escribir en $LOG_DIR" + error "Redis no pudo iniciarse en puerto ${POC_REDIS_PORT}." + } + sleep 1 -redis-cli -p "$POC_REDIS_PORT" ping &>/dev/null && info "Redis OK (puerto ${POC_REDIS_PORT})" +if redis-cli -p "$POC_REDIS_PORT" ping 2>/dev/null | grep -q PONG; then + info "Redis OK (puerto ${POC_REDIS_PORT})" +else + show_log_tail "$LOG_DIR/redis.log" + error "Redis inició pero no responde a PING." +fi cat "$TMP_DIR/redis-poc.pid" 2>/dev/null >> "$PID_FILE" || true # ============================================================================= # 5. COMPILAR BACKEND GO # ============================================================================= step "Compilando backend Go..." -mkdir -p "$TMP_DIR/bin" (cd "$REPO_ROOT/backend" && \ - CGO_ENABLED=0 go build -buildvcs=false -o "$TMP_DIR/bin/server" ./cmd/server \ - 2>"$TMP_DIR/build-backend.log") || { - cat "$TMP_DIR/build-backend.log" - error "Fallo al compilar backend. Ver log arriba." + CGO_ENABLED=0 go build -buildvcs=false \ + -o "$TMP_DIR/bin/server" ./cmd/server \ + 2>"$LOG_DIR/build-backend.log") || { + echo "" + # Filtrar errores relevantes del log de compilación + echo -e " ${RED}Error de compilación:${NC}" + grep -E "^(.*\.go:[0-9]+:|error:|undefined:)" "$LOG_DIR/build-backend.log" \ + | head -20 | sed 's/^/ /' || show_log_tail "$LOG_DIR/build-backend.log" 15 + echo "" + echo -e " Posibles causas:" + echo -e " • Version de Go insuficiente: $(go version)" + echo -e " Se necesita 1.22+. Instala: sudo bash deploy/debian/prerequisites.sh" + echo -e " • Dependencias faltantes: cd backend && go mod download" + echo -e " • Log completo: cat $LOG_DIR/build-backend.log" + error "Fallo al compilar backend Go." } info "Backend compilado OK" @@ -140,74 +310,122 @@ info "Backend compilado OK" # 6. ARRANCAR BACKEND API # ============================================================================= step "Arrancando API en puerto ${POC_API_PORT}..." -"$TMP_DIR/bin/server" > "$TMP_DIR/backend.log" 2>&1 & + +"$TMP_DIR/bin/server" > "$LOG_DIR/backend.log" 2>&1 & BACKEND_PID=$! echo "$BACKEND_PID" >> "$PID_FILE" -# Esperar a que el backend responda -for i in {1..15}; do +# Esperar con feedback visual +printf " Esperando respuesta" +BACKEND_UP=false +for i in {1..20}; do sleep 1 + printf "." if curl -sf "http://127.0.0.1:${POC_API_PORT}/api/stats" &>/dev/null; then - info "API respondiendo en http://127.0.0.1:${POC_API_PORT}" + BACKEND_UP=true break fi - if [[ $i -eq 15 ]]; then - cat "$TMP_DIR/backend.log" - error "El backend no responde. Ver log arriba." + # Detectar si el proceso murió + if ! kill -0 "$BACKEND_PID" 2>/dev/null; then + break fi done +echo "" + +if [[ "$BACKEND_UP" == "false" ]]; then + echo "" + echo -e " ${RED}El backend no arrancó correctamente.${NC}" + echo -e " ${BOLD}Log del backend:${NC}" + show_log_tail "$LOG_DIR/backend.log" 25 + echo "" + echo -e " Posibles causas:" + echo -e " • Error de conexión a BD: revisa DATABASE_URL" + echo -e " Prueba: psql \"${DATABASE_URL}\" -c 'SELECT 1'" + echo -e " • Error de conexión a Redis: revisa REDIS_URL" + echo -e " Prueba: redis-cli -p ${POC_REDIS_PORT} ping" + echo -e " • Puerto ${POC_API_PORT} ocupado: ss -tlnp | grep ${POC_API_PORT}" + echo -e " • Log completo: cat $LOG_DIR/backend.log" + error "Backend no responde." +fi +info "API respondiendo en http://127.0.0.1:${POC_API_PORT}" # ============================================================================= # 7. FRONTEND REACT # ============================================================================= -step "Preparando frontend..." +step "Preparando frontend React..." cd "$REPO_ROOT/frontend" if [[ ! -d node_modules ]]; then - step " Instalando dependencias npm (primera vez)..." - npm install --silent + step " Instalando dependencias npm (primera vez, puede tardar)..." + npm install 2>"$LOG_DIR/npm-install.log" || { + show_log_tail "$LOG_DIR/npm-install.log" 20 + echo -e " Posibles causas:" + echo -e " • Sin conexión a internet (npm registry)" + echo -e " • Versión de Node.js incompatible: $(node -v)" + echo -e " • Disco lleno: df -h ." + echo -e " • Prueba manualmente: cd frontend && npm install" + error "npm install falló." + } fi -# Compilar apuntando al API local del POC +step " Compilando frontend..." VITE_API_URL="http://127.0.0.1:${POC_API_PORT}" \ -npm run build -- --outDir "$TMP_DIR/frontend-dist" 2>"$TMP_DIR/build-frontend.log" || { - cat "$TMP_DIR/build-frontend.log" - error "Fallo al compilar frontend. Ver log arriba." +npm run build -- --outDir "$TMP_DIR/frontend-dist" \ + >"$LOG_DIR/build-frontend.log" 2>&1 || { + echo -e " ${RED}Error de compilación del frontend:${NC}" + grep -E "(error TS|Error:|ERROR)" "$LOG_DIR/build-frontend.log" \ + | head -15 | sed 's/^/ /' || show_log_tail "$LOG_DIR/build-frontend.log" 15 + echo "" + echo -e " Posibles causas:" + echo -e " • Error TypeScript: revisa cambios recientes en src/" + echo -e " • Log completo: cat $LOG_DIR/build-frontend.log" + error "Compilación del frontend falló." } info "Frontend compilado OK" -# Servir frontend con npx serve (simple, sin nginx) -step "Sirviendo frontend en puerto ${POC_FRONTEND_PORT}..." +step " Sirviendo frontend en puerto ${POC_FRONTEND_PORT}..." npx --yes serve "$TMP_DIR/frontend-dist" -l "$POC_FRONTEND_PORT" \ - > "$TMP_DIR/frontend.log" 2>&1 & + > "$LOG_DIR/frontend-serve.log" 2>&1 & FRONTEND_PID=$! echo "$FRONTEND_PID" >> "$PID_FILE" + sleep 2 +if ! kill -0 "$FRONTEND_PID" 2>/dev/null; then + show_log_tail "$LOG_DIR/frontend-serve.log" + echo -e " Prueba manual: npx serve $TMP_DIR/frontend-dist -l ${POC_FRONTEND_PORT}" + error "El servidor del frontend no arrancó." +fi +info "Frontend sirviendo en http://127.0.0.1:${POC_FRONTEND_PORT}" cd "$REPO_ROOT" # ============================================================================= -# LISTO +# RESUMEN FINAL # ============================================================================= echo "" echo -e "${BOLD}${GREEN}=================================================${NC}" echo -e "${BOLD}${GREEN} COCONEWS POC corriendo${NC}" echo -e "${BOLD}${GREEN}=================================================${NC}" echo "" -echo -e " ${BOLD}Frontend:${NC} http://127.0.0.1:${POC_FRONTEND_PORT}" -echo -e " ${BOLD}API:${NC} http://127.0.0.1:${POC_API_PORT}/api/stats" +echo -e " ${BOLD}Abrir en el navegador:${NC}" +echo -e " → http://127.0.0.1:${POC_FRONTEND_PORT}" echo "" -echo -e " ${BOLD}Login:${NC} Registra el primer usuario en la UI" -echo -e " (será admin automáticamente)" +echo -e " ${BOLD}Endpoints útiles:${NC}" +echo -e " API stats: http://127.0.0.1:${POC_API_PORT}/api/stats" +echo -e " API noticias: http://127.0.0.1:${POC_API_PORT}/api/news" echo "" -echo -e " ${BOLD}Noticias:${NC} ${NEWS_COUNT} artículos de prueba en español" -echo -e " ${YELLOW}Nota:${NC} Sin workers ML activos." -echo -e " Noticias no tendrán traducción ni entidades." +echo -e " ${BOLD}Primer login:${NC}" +echo -e " Regístrate en la UI → el primer usuario es admin automáticamente" echo "" -echo -e " ${BLUE}Logs:${NC} $TMP_DIR/*.log" +echo -e " ${BOLD}Datos cargados:${NC} ${NEWS_COUNT} noticias de prueba en español" +echo -e " ${YELLOW}Sin workers ML:${NC} no hay traducción ni entidades (normal en POC)" echo "" -echo -e " Pulsa ${BOLD}Ctrl+C${NC} para detener el POC." +echo -e " ${BOLD}Si algo falla:${NC}" +echo -e " Logs: $LOG_DIR/" +echo -e " Backend: tail -f $LOG_DIR/backend.log" +echo -e " Limpiar: bash poc/poc.sh --clean" +echo "" +echo -e " ${DIM}Pulsa Ctrl+C para detener${NC}" echo "" -# Mantener el script corriendo wait From b3bf3d7a7fdce6d28b12c24f4307f135a9f411f6 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 22:29:50 +0200 Subject: [PATCH 06/21] refactor: reorganizar estructura de archivos en raiz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Antes la raiz tenia 20+ archivos sueltos. Ahora organizado en: docs/ 10 archivos .md de documentacion tecnica scripts/ 3 scripts utilitarios (credentials, migrate, verify) config/ entity_config.json (aliases y blacklist NER) data/ feeds.csv (feeds precargados) Eliminados restos de Docker que ya no aplican: .dockerignore, .env.example, .env.secure.example, nginx.conf (raiz) Makefile: eliminados targets docker-build, añadidos install/rebuild/check/poc Referencias actualizadas en: deploy/debian/install.sh entity_config.json -> config/entity_config.json deploy/debian/build.sh entity_config.json -> config/entity_config.json README.md links a docs/ y data/ actualizados, arbol de estructura del repo reescrito Co-Authored-By: Claude Sonnet 4.6 --- .dockerignore | 13 -- .env.example | 67 ---------- .env.secure.example | 117 ------------------ Makefile | 33 +++-- README.md | 63 +++++++--- .../entity_config.json | 0 feeds.csv => data/feeds.csv | 0 deploy/debian/build.sh | 2 +- deploy/debian/install.sh | 2 +- DEPLOY.md => docs/DEPLOY.md | 0 DEPLOY_DEBIAN.md => docs/DEPLOY_DEBIAN.md | 0 .../FUNCIONES_DE_ARCHIVOS.md | 0 .../IMPLEMENTACION_LLM_RESUMEN.md | 0 .../NEWSPAPER_STYLE_GUIDE.md | 0 QDRANT_SETUP.md => docs/QDRANT_SETUP.md | 0 QUICKSTART_LLM.md => docs/QUICKSTART_LLM.md | 0 SECURITY_AUDIT.md => docs/SECURITY_AUDIT.md | 0 SECURITY_GUIDE.md => docs/SECURITY_GUIDE.md | 0 .../TRANSLATION_FIX_SUMMARY.md | 0 nginx.conf | 97 --------------- .../generate_secure_credentials.sh | 0 .../migrate_to_secure.sh | 0 .../verify_security.sh | 0 23 files changed, 59 insertions(+), 335 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .env.example delete mode 100644 .env.secure.example rename entity_config.json => config/entity_config.json (100%) rename feeds.csv => data/feeds.csv (100%) rename DEPLOY.md => docs/DEPLOY.md (100%) rename DEPLOY_DEBIAN.md => docs/DEPLOY_DEBIAN.md (100%) rename FUNCIONES_DE_ARCHIVOS.md => docs/FUNCIONES_DE_ARCHIVOS.md (100%) rename IMPLEMENTACION_LLM_RESUMEN.md => docs/IMPLEMENTACION_LLM_RESUMEN.md (100%) rename NEWSPAPER_STYLE_GUIDE.md => docs/NEWSPAPER_STYLE_GUIDE.md (100%) rename QDRANT_SETUP.md => docs/QDRANT_SETUP.md (100%) rename QUICKSTART_LLM.md => docs/QUICKSTART_LLM.md (100%) rename SECURITY_AUDIT.md => docs/SECURITY_AUDIT.md (100%) rename SECURITY_GUIDE.md => docs/SECURITY_GUIDE.md (100%) rename TRANSLATION_FIX_SUMMARY.md => docs/TRANSLATION_FIX_SUMMARY.md (100%) delete mode 100644 nginx.conf rename generate_secure_credentials.sh => scripts/generate_secure_credentials.sh (100%) rename migrate_to_secure.sh => scripts/migrate_to_secure.sh (100%) rename verify_security.sh => scripts/verify_security.sh (100%) diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index b472c77..0000000 --- a/.dockerignore +++ /dev/null @@ -1,13 +0,0 @@ -.git -pgdata -pgdata-replica -pgdata-replica.old.* -pgdata.failed_restore -redis-data -hf_cache -qdrant_storage - -venv -__pycache__ -*.pyc -*.log diff --git a/.env.example b/.env.example deleted file mode 100644 index 4f5ab34..0000000 --- a/.env.example +++ /dev/null @@ -1,67 +0,0 @@ -# Database Configuration -POSTGRES_DB=rss -POSTGRES_USER=rss -POSTGRES_PASSWORD=change_this_password -DB_NAME=rss -DB_USER=rss -DB_PASS=change_this_password -DB_HOST=db -DB_PORT=5432 -DB_WRITE_HOST=db -DB_READ_HOST=db-replica - -# Redis Configuration -REDIS_HOST=redis -REDIS_PORT=6379 - -# Application Secrets -SECRET_KEY=change_this_to_a_long_random_string - -# External Services -ALLTALK_URL=http://host.docker.internal:7851 - -# AI Models & Workers -RSS_MAX_WORKERS=3 -# Translation Pipeline -TARGET_LANGS=es -TRANSLATOR_BATCH=16 -SCHEDULER_BATCH=2000 -SCHEDULER_SLEEP=30 -LANG_DETECT_BATCH=1000 -LANG_DETECT_SLEEP=60 - -# RSS Ingestor Configuration -RSS_POKE_INTERVAL_MIN=15 -RSS_MAX_FAILURES=10 -RSS_FEED_TIMEOUT=60 - -# URL Feed Discovery Worker -URL_DISCOVERY_INTERVAL_MIN=15 -URL_DISCOVERY_BATCH_SIZE=10 -MAX_FEEDS_PER_URL=5 - -# CTranslate2 / AI Model Paths -CT2_MODEL_PATH=/app/models/nllb-ct2 -CT2_DEVICE=cuda -CT2_COMPUTE_TYPE=int8_float16 -UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M - -# Embeddings -EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 -EMB_BATCH=64 -EMB_DEVICE=cuda - -# NER -NER_LANG=es -NER_BATCH=64 - -# Flask / Gunicorn -GUNICORN_WORKERS=8 -FLASK_DEBUG=0 - -# Qdrant Configuration -QDRANT_HOST=qdrant -QDRANT_PORT=6333 -QDRANT_COLLECTION_NAME=news_vectors -QDRANT_BATCH_SIZE=100 -QDRANT_SLEEP_IDLE=30 diff --git a/.env.secure.example b/.env.secure.example deleted file mode 100644 index 68b84cc..0000000 --- a/.env.secure.example +++ /dev/null @@ -1,117 +0,0 @@ -# ================================================================================== -# SEGURIDAD: CONFIGURACIÓN DE PRODUCCIÓN -# ================================================================================== -# -# IMPORTANTE: -# 1. Copia este archivo a .env -# 2. Cambia TODOS los valores de contraseñas y secrets -# 3. NO compartas este archivo en repositorios públicos -# 4. Añade .env al .gitignore -# -# ================================================================================== - -# ================================================================================== -# DATABASE CONFIGURATION - PostgreSQL -# ================================================================================== -POSTGRES_DB=rss -POSTGRES_USER=rss -# CRÍTICO: Genera una contraseña fuerte (mínimo 32 caracteres aleatorios) -# Ejemplo para generar: openssl rand -base64 32 -POSTGRES_PASSWORD=CAMBIAR_ESTO_POR_UNA_CONTRASEÑA_FUERTE_DE_32_CARACTERES - -DB_NAME=rss -DB_USER=rss -DB_PASS=CAMBIAR_ESTO_POR_UNA_CONTRASEÑA_FUERTE_DE_32_CARACTERES -DB_HOST=db -DB_PORT=5432 -DB_WRITE_HOST=db -DB_READ_HOST=db-replica - -# ================================================================================== -# REDIS CONFIGURATION - Autenticación habilitada -# ================================================================================== -REDIS_HOST=redis -REDIS_PORT=6379 -# CRÍTICO: Genera una contraseña fuerte para Redis -# Ejemplo: openssl rand -base64 32 -REDIS_PASSWORD=CAMBIAR_ESTO_POR_UNA_CONTRASEÑA_FUERTE_REDIS - -# ================================================================================== -# APPLICATION SECRETS -# ================================================================================== -# CRÍTICO: Secret key para Flask - debe ser único y secreto -# Genera con: python -c "import secrets; print(secrets.token_hex(32))" -SECRET_KEY=CAMBIAR_ESTO_POR_UN_TOKEN_HEX_DE_64_CARACTERES - -# ================================================================================== -# MONITORING - Grafana -# ================================================================================== -# IMPORTANTE: Cambia el password de admin de Grafana -GRAFANA_PASSWORD=CAMBIAR_ESTO_POR_UNA_CONTRASEÑA_FUERTE_GRAFANA - -# ================================================================================== -# EXTERNAL SERVICES -# ================================================================================== -ALLTALK_URL=http://host.docker.internal:7851 - -# ================================================================================== -# AI MODELS & WORKERS -# ================================================================================== -RSS_MAX_WORKERS=3 -TARGET_LANGS=es -TRANSLATOR_BATCH=128 -ENQUEUE=300 - -# RSS Ingestor Configuration -RSS_POKE_INTERVAL_MIN=15 -RSS_MAX_FAILURES=10 -RSS_FEED_TIMEOUT=60 - -# URL Feed Discovery Worker -URL_DISCOVERY_INTERVAL_MIN=15 -URL_DISCOVERY_BATCH_SIZE=10 -MAX_FEEDS_PER_URL=5 - -# CTranslate2 / AI Model Paths -CT2_MODEL_PATH=/app/models/nllb-ct2 -CT2_DEVICE=cuda -CT2_COMPUTE_TYPE=int8_float16 -UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M - -# Embeddings -EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 -EMB_BATCH=64 -EMB_DEVICE=cuda - -# NER -NER_LANG=es -NER_BATCH=64 - -# Flask / Gunicorn -GUNICORN_WORKERS=8 -FLASK_DEBUG=0 - -# Qdrant Configuration -QDRANT_HOST=qdrant -QDRANT_PORT=6333 -QDRANT_COLLECTION_NAME=news_vectors -QDRANT_BATCH_SIZE=100 -QDRANT_SLEEP_IDLE=30 - -# ================================================================================== -# COMANDOS ÚTILES PARA GENERAR CONTRASEÑAS SEGURAS -# ================================================================================== -# -# PostgreSQL Password (32 caracteres): -# openssl rand -base64 32 -# -# Redis Password (32 caracteres): -# openssl rand -base64 32 -# -# Flask Secret Key (64 hex chars): -# python -c "import secrets; print(secrets.token_hex(32))" -# -# Grafana Password (fuerte): -# openssl rand -base64 24 -# -# ================================================================================== diff --git a/Makefile b/Makefile index 5d463f2..3d7a6fb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # RSS2 Workers Makefile -.PHONY: all build clean deps ingestor scraper discovery topics related qdrant server +.PHONY: all build clean deps ingestor scraper discovery topics related qdrant server install rebuild check poc # Binary output directory BIN_DIR := bin @@ -69,21 +69,16 @@ run-qdrant: DB_HOST=localhost DB_PORT=5432 DB_NAME=rss DB_USER=rss DB_PASS=rss \ QDRANT_HOST=localhost QDRANT_PORT=6333 OLLAMA_URL=http://localhost:11434 $(QDRANT) -# Docker builds -docker-build: - docker build -t rss2-ingestor -f rss-ingestor-go/Dockerfile ./rss-ingestor-go - docker build -t rss2-server -f backend/Dockerfile ./backend - docker build -t rss2-scraper -f Dockerfile.scraper ./backend - docker build -t rss2-discovery -f Dockerfile.discovery ./backend - docker build -t rss2-topics -f Dockerfile.topics ./backend - docker build -t rss2-related -f Dockerfile.related ./backend - docker build -t rss2-qdrant -f Dockerfile.qdrant ./backend - docker build -t rss2-langdetect -f Dockerfile . - docker build -t rss2-scheduler -f Dockerfile.scheduler . - docker build -t rss2-translator -f Dockerfile.translator . - docker build -t rss2-translator-gpu -f Dockerfile.translator-gpu . - docker build -t rss2-embeddings -f Dockerfile.embeddings_worker . - docker build -t rss2-ner -f Dockerfile . - docker build -t rss2-llm-categorizer -f Dockerfile.llm_worker . - docker build -t rss2-frontend -f frontend/Dockerfile ./frontend - docker build -t rss2-nginx -f Dockerfile.nginx . +# Despliegue en Debian (sin Docker) +install: + sudo bash deploy/debian/prerequisites.sh + sudo bash deploy/debian/install.sh + +rebuild: + sudo bash deploy/debian/build.sh + +check: + bash deploy/debian/check.sh + +poc: + bash poc/poc.sh diff --git a/README.md b/README.md index d70ea43..8f18314 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ Compila los binarios Go, el frontend React, crea los servicios systemd y arranca http://IP_DEL_SERVIDOR:8001 ``` -Guía completa: [DEPLOY_DEBIAN.md](DEPLOY_DEBIAN.md) +Guía completa: [docs/DEPLOY_DEBIAN.md](docs/DEPLOY_DEBIAN.md) --- @@ -155,23 +155,46 @@ sudo bash deploy/debian/build.sh ## Estructura del repositorio ``` -├── backend/ Go — API REST + workers (scraper, discovery, wiki, topics, related, qdrant) -├── rss-ingestor-go/ Go — Ingestor de feeds RSS -├── frontend/ React + TypeScript + Tailwind -├── workers/ Python — ML workers (traducción, embeddings, NER, cluster, categorización) -├── init-db/ SQL — Schema y datos iniciales -├── migrations/ SQL — Migraciones incrementales -├── deploy/debian/ Scripts de despliegue para Debian sin Docker -│ ├── prerequisites.sh Instala todas las dependencias del sistema -│ ├── install.sh Instalación completa -│ ├── build.sh Recompila y reinicia tras actualizar código -│ ├── env.example Plantilla de variables de entorno -│ ├── nginx.conf Configuración nginx para despliegue nativo -│ └── systemd/ Ficheros de servicio systemd (16 servicios) -├── poc/ -│ ├── poc.sh POC local con datos de prueba (sin Docker, sin ML) -│ └── seed.sql Datos de muestra para el POC -├── feeds.csv Feeds RSS precargados para importar desde el admin -├── entity_config.json Aliases y blacklist para normalización de entidades NER -└── DEPLOY_DEBIAN.md Guía detallada de despliegue en Debian +├── README.md +├── requirements.txt Dependencias Python para workers ML +├── Makefile Compilación local de binarios Go +│ +├── backend/ Go — API REST + workers (scraper, discovery, wiki, topics, related, qdrant) +├── rss-ingestor-go/ Go — Ingestor de feeds RSS +├── frontend/ React + TypeScript + Tailwind +├── workers/ Python — Workers ML (traducción, embeddings, NER, cluster, categorización) +│ +├── init-db/ SQL — Schema completo y datos iniciales +├── migrations/ SQL — Migraciones incrementales +│ +├── config/ +│ └── entity_config.json Aliases y blacklist para normalización de entidades NER +│ +├── data/ +│ └── feeds.csv Feeds RSS precargados para importar desde el admin +│ +├── docs/ +│ ├── DEPLOY_DEBIAN.md Guía detallada de despliegue en Debian +│ ├── SECURITY_GUIDE.md Guía de seguridad +│ ├── SECURITY_AUDIT.md Resultado del audit de seguridad +│ ├── QDRANT_SETUP.md Configuración de Qdrant +│ └── ... Resto de documentación técnica +│ +├── scripts/ +│ ├── generate_secure_credentials.sh +│ ├── migrate_to_secure.sh +│ └── verify_security.sh +│ +├── deploy/debian/ Despliegue nativo en Debian (sin Docker) +│ ├── prerequisites.sh Instala todas las dependencias del sistema +│ ├── install.sh Instalación completa +│ ├── build.sh Recompila y reinicia tras actualizar código +│ ├── check.sh Diagnóstico del sistema +│ ├── env.example Plantilla de variables de entorno +│ ├── nginx.conf Configuración nginx +│ └── systemd/ 16 ficheros de servicio systemd +│ +└── poc/ + ├── poc.sh POC local en 2 minutos (sin Docker, sin ML) + └── seed.sql 10 noticias de muestra en español ``` diff --git a/entity_config.json b/config/entity_config.json similarity index 100% rename from entity_config.json rename to config/entity_config.json diff --git a/feeds.csv b/data/feeds.csv similarity index 100% rename from feeds.csv rename to data/feeds.csv diff --git a/deploy/debian/build.sh b/deploy/debian/build.sh index 6dd81b2..43efd5b 100755 --- a/deploy/debian/build.sh +++ b/deploy/debian/build.sh @@ -55,7 +55,7 @@ fi # --- Workers Python --- info "Sincronizando workers Python..." rsync -a --delete "$REPO_ROOT/workers/" "$RSS2_HOME/src/workers/" -cp "$REPO_ROOT/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true +cp "$REPO_ROOT/config/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true info " [OK] workers Python" chown -R rss2:rss2 "$RSS2_HOME/bin" "$RSS2_HOME/frontend/dist" "$RSS2_HOME/src" diff --git a/deploy/debian/install.sh b/deploy/debian/install.sh index 9627bbc..128336f 100755 --- a/deploy/debian/install.sh +++ b/deploy/debian/install.sh @@ -172,7 +172,7 @@ fi # Copiar workers Python al directorio de trabajo info "Copiando workers Python..." rsync -a --delete "$REPO_ROOT/workers/" "$RSS2_HOME/src/workers/" -cp "$REPO_ROOT/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true +cp "$REPO_ROOT/config/entity_config.json" "$RSS2_HOME/src/" 2>/dev/null || true # ============================================================================= # 7. COMPILAR GO (backend + workers) diff --git a/DEPLOY.md b/docs/DEPLOY.md similarity index 100% rename from DEPLOY.md rename to docs/DEPLOY.md diff --git a/DEPLOY_DEBIAN.md b/docs/DEPLOY_DEBIAN.md similarity index 100% rename from DEPLOY_DEBIAN.md rename to docs/DEPLOY_DEBIAN.md diff --git a/FUNCIONES_DE_ARCHIVOS.md b/docs/FUNCIONES_DE_ARCHIVOS.md similarity index 100% rename from FUNCIONES_DE_ARCHIVOS.md rename to docs/FUNCIONES_DE_ARCHIVOS.md diff --git a/IMPLEMENTACION_LLM_RESUMEN.md b/docs/IMPLEMENTACION_LLM_RESUMEN.md similarity index 100% rename from IMPLEMENTACION_LLM_RESUMEN.md rename to docs/IMPLEMENTACION_LLM_RESUMEN.md diff --git a/NEWSPAPER_STYLE_GUIDE.md b/docs/NEWSPAPER_STYLE_GUIDE.md similarity index 100% rename from NEWSPAPER_STYLE_GUIDE.md rename to docs/NEWSPAPER_STYLE_GUIDE.md diff --git a/QDRANT_SETUP.md b/docs/QDRANT_SETUP.md similarity index 100% rename from QDRANT_SETUP.md rename to docs/QDRANT_SETUP.md diff --git a/QUICKSTART_LLM.md b/docs/QUICKSTART_LLM.md similarity index 100% rename from QUICKSTART_LLM.md rename to docs/QUICKSTART_LLM.md diff --git a/SECURITY_AUDIT.md b/docs/SECURITY_AUDIT.md similarity index 100% rename from SECURITY_AUDIT.md rename to docs/SECURITY_AUDIT.md diff --git a/SECURITY_GUIDE.md b/docs/SECURITY_GUIDE.md similarity index 100% rename from SECURITY_GUIDE.md rename to docs/SECURITY_GUIDE.md diff --git a/TRANSLATION_FIX_SUMMARY.md b/docs/TRANSLATION_FIX_SUMMARY.md similarity index 100% rename from TRANSLATION_FIX_SUMMARY.md rename to docs/TRANSLATION_FIX_SUMMARY.md diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index af65c19..0000000 --- a/nginx.conf +++ /dev/null @@ -1,97 +0,0 @@ -user nginx; -worker_processes auto; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 2048; - use epoll; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - types_hash_max_size 2048; - client_max_body_size 100M; - - gzip on; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_types text/plain text/css text/javascript - application/json application/javascript - application/xml text/xml; - - # Upstream for Go API - upstream api_backend { - server backend-go:8080; - keepalive 32; - } - - # Upstream for React Frontend - upstream frontend { - server rss2_frontend:80; - keepalive 16; - } - - server { - listen 80; - server_name _; - - client_body_timeout 60s; - client_header_timeout 60s; - send_timeout 300s; - - # Serve React Frontend - location / { - proxy_pass http://frontend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # Proxy to Go API - location /api/ { - proxy_pass http://api_backend/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ""; - - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - proxy_read_timeout 300s; - } - - # Health check - location /health { - access_log off; - return 200 "ok"; - } - - # Block sensitive files - location ~ /\. { - deny all; - access_log off; - log_not_found off; - } - } -} diff --git a/generate_secure_credentials.sh b/scripts/generate_secure_credentials.sh similarity index 100% rename from generate_secure_credentials.sh rename to scripts/generate_secure_credentials.sh diff --git a/migrate_to_secure.sh b/scripts/migrate_to_secure.sh similarity index 100% rename from migrate_to_secure.sh rename to scripts/migrate_to_secure.sh diff --git a/verify_security.sh b/scripts/verify_security.sh similarity index 100% rename from verify_security.sh rename to scripts/verify_security.sh From 10f0555c468d534cc3fde964da7222a0cf6d4069 Mon Sep 17 00:00:00 2001 From: SITO Date: Mon, 30 Mar 2026 22:42:12 +0200 Subject: [PATCH 07/21] feat(poc): expandir POC con datos multilingues, admin pre-creado y guia completa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - poc/seed.sql: 17 noticias (ES/EN/FR) con traducciones y 25 entidades NER - poc/poc.sh: corregir VITE_API_URL (faltaba sufijo /api), crear admin con bcrypt - docs/POC_GUIDE.md: guia paso a paso para que el compañero explore la demo - README.md: añadir credenciales admin y enlace a la guia POC Co-Authored-By: Claude Sonnet 4.6 --- README.md | 4 +- docs/POC_GUIDE.md | 287 ++++++++++++++++++++++++++++++++++++++++++++ poc/poc.sh | 40 ++++++- poc/seed.sql | 295 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 553 insertions(+), 73 deletions(-) create mode 100644 docs/POC_GUIDE.md diff --git a/README.md b/README.md index 8f18314..f4a8ece 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,9 @@ bash poc/poc.sh ``` Abre `http://127.0.0.1:18001` en el navegador. -El primer usuario que se registre será administrador. +Login: **admin** / **admin123** (creado automáticamente si tienes `pip3 install bcrypt`). + +Guía detallada para explorar la demo: [docs/POC_GUIDE.md](docs/POC_GUIDE.md) --- diff --git a/docs/POC_GUIDE.md b/docs/POC_GUIDE.md new file mode 100644 index 0000000..6e83960 --- /dev/null +++ b/docs/POC_GUIDE.md @@ -0,0 +1,287 @@ +# COCONEWS — Guía de la POC local + +Esta guía te lleva de cero a tener COCONEWS corriendo en tu máquina en menos de 5 minutos, sin Docker, sin modelos ML y sin configuración compleja. + +--- + +## ¿Qué es esta POC? + +La POC (Proof of Concept) arranca únicamente: + +- **Backend Go** (API REST en puerto 18080) +- **Frontend React** compilado y servido estáticamente (puerto 18001) +- **PostgreSQL** con 17 noticias de muestra en español, inglés y francés +- **Redis** en un puerto alternativo (6380) para no interferir con Redis del sistema + +**No se ejecutan** los workers ML (NLLB-200, embeddings, NER, etc.). Las noticias ya vienen con traducciones y entidades precargadas en la BD. + +--- + +## Requisitos mínimos + +| Herramienta | Versión mínima | Instalar en Debian/Ubuntu | +|-------------|----------------|---------------------------| +| Go | 1.22+ (recomendado 1.25) | `sudo bash deploy/debian/prerequisites.sh` | +| Node.js | 18+ | `curl -fsSL https://deb.nodesource.com/setup_20.x \| sudo bash - && sudo apt install nodejs` | +| PostgreSQL | 14+ | `sudo apt install postgresql postgresql-client` | +| Redis | 6+ | `sudo apt install redis-server` | +| Python 3 + bcrypt | opcional | `pip3 install bcrypt` (para auto-crear admin) | + +Para instalar todo de golpe en Debian/Ubuntu: + +```bash +sudo bash deploy/debian/prerequisites.sh +``` + +--- + +## Ejecución + +```bash +# Desde la raíz del repositorio +bash poc/poc.sh +``` + +La primera vez tarda ~2-3 minutos (compilación Go + instalación de dependencias npm). +Las siguientes ejecuciones arrancan en ~30 segundos. + +**Abrir en el navegador:** `http://127.0.0.1:18001` + +--- + +## Login + +Si tienes `python3-bcrypt` instalado, el script crea automáticamente un usuario admin: + +| Campo | Valor | +|-------|-------| +| Usuario | `admin` | +| Contraseña | `admin123` | + +Si no tienes bcrypt, regístrate desde la UI. El **primer usuario registrado** se convierte automáticamente en administrador (comportamiento del WelcomeWizard integrado). + +--- + +## Qué puedes explorar + +### 1. Feed principal de noticias + +En la página de inicio verás 17 noticias de muestra distribuidas en varias categorías: + +- **Ciencia y tecnología**: GPT-5, inteligencia artificial, energía solar, exploración espacial +- **Economía**: criptomonedas, mercados, política fiscal +- **Deportes**: Lamine Yamal, Champions League, Fórmula 1 +- **Política internacional**: Macron, conflictos globales +- **Medio ambiente**: energía eólica, cambio climático + +### 2. Filtros y búsqueda + +- **Búsqueda por texto**: prueba "inteligencia artificial" o "energía" +- **Filtrar por idioma original**: muestra noticias en inglés o francés tal como llegaron +- **Solo traducidas**: filtra las que ya tienen traducción al español (todas en la POC) +- **Filtrar por categoría**: Ciencia, Economía, Deportes, etc. +- **Filtrar por entidades**: personas, organizaciones, lugares + +### 3. Entidades NER (personas, organizaciones, lugares) + +Las noticias tienen entidades precargadas. En el panel lateral o en los tooltips verás: + +| Tipo | Ejemplos | +|------|---------| +| Persona | Elon Musk, Lamine Yamal, Emmanuel Macron, Sam Altman | +| Organización | OpenAI, NASA, Tesla, FC Barcelona, UEFA | +| Lugar | España, China, Estados Unidos, París | +| Tema | inteligencia artificial, energía solar, criptomonedas | + +Haz clic en una entidad para ver todas las noticias relacionadas con ella. + +### 4. Panel de administración + +Accede con el usuario admin a: + +- **Feeds RSS**: lista de fuentes configuradas (importa desde `data/feeds.csv`) +- **Gestión de usuarios**: ver y gestionar cuentas +- **Configuración del sistema**: parámetros del backend +- **Estadísticas**: contadores de noticias, feeds, entidades + +Para importar los feeds de muestra incluidos en el repo: + +1. Ve a Admin → Feeds +2. Usa la opción de importar CSV +3. Selecciona `data/feeds.csv` (incluye ~200 fuentes precargadas) + +### 5. API REST directa + +El backend expone una API REST documentada. Puedes explorarla directamente: + +```bash +# Estadísticas generales +curl http://127.0.0.1:18080/api/stats | jq . + +# Últimas noticias +curl http://127.0.0.1:18080/api/news | jq . + +# Buscar noticias +curl "http://127.0.0.1:18080/api/news?q=inteligencia+artificial" | jq . + +# Filtrar por idioma +curl "http://127.0.0.1:18080/api/news?lang=en" | jq . + +# Entidades disponibles +curl http://127.0.0.1:18080/api/tags | jq . + +# Noticias de una entidad específica +curl "http://127.0.0.1:18080/api/news?tag=OpenAI" | jq . +``` + +--- + +## Empezar de cero + +Si quieres reiniciar la BD de prueba (por ejemplo, tras cambiar el seed): + +```bash +bash poc/poc.sh --clean +``` + +Esto borra la BD `coconews_poc` y la recrea desde cero. + +--- + +## Estructura de los datos de prueba + +El seed está en [poc/seed.sql](../poc/seed.sql). Contiene: + +| Elemento | Cantidad | +|----------|----------| +| Noticias en español | 10 | +| Noticias en inglés (con traducción) | 5 | +| Noticias en francés (con traducción) | 2 | +| Traducciones en tabla `traducciones` | 17 | +| Entidades NER en tabla `tags` | 25 | +| Asociaciones noticia↔entidad | ~45 | + +Los feeds de origen son ficticios (`techcrunch.com`, `bbc.com`, `lemonde.fr`, etc.) pero representan la estructura real que llegaría del ingestor RSS. + +--- + +## Diferencias con producción + +| Característica | POC | Producción | +|----------------|-----|-----------| +| Traducción automática | Precargada en BD | NLLB-200 en tiempo real | +| Extracción de entidades | Precargada en BD | spaCy es_core_news_lg | +| Embeddings semánticos | No disponible | MiniLM (sentence-transformers) | +| Búsqueda vectorial | No disponible | Qdrant | +| Ingesta de feeds | No activa | rss-ingestor-go continuo | +| Workers ML | No activos | 7 workers Python | +| Clusterización | No activa | Worker cluster | +| Redis | Puerto 6380 local | Puerto 6379 con auth | + +--- + +## Solución de problemas + +### El backend no arranca + +```bash +# Ver log completo +cat /tmp/coconews-poc/logs/backend.log + +# Probar conexión BD manualmente +psql "postgres://coconews_poc:poc_password_local@127.0.0.1:5432/coconews_poc" -c 'SELECT COUNT(*) FROM noticias;' + +# Probar Redis +redis-cli -p 6380 ping +``` + +### El frontend muestra pantalla en blanco + +Verifica que el backend está respondiendo: +```bash +curl http://127.0.0.1:18080/api/stats +``` + +Si el backend responde pero el frontend no carga datos, puede ser un problema de CORS o de la URL de la API. Revisa la consola del navegador (F12). + +### Error "puerto en uso" + +```bash +# Ver qué usa el puerto +ss -tlnp | grep 18080 +ss -tlnp | grep 18001 +ss -tlnp | grep 6380 + +# Matar proceso +kill $(lsof -ti:18080) +``` + +O edita las variables `POC_*_PORT` al inicio de `poc/poc.sh` para usar puertos diferentes. + +### Error de compilación Go + +```bash +# Descargar dependencias +cd backend && go mod download && go mod tidy + +# Verificar versión +go version # necesita 1.22+ + +# Ver log +cat /tmp/coconews-poc/logs/build-backend.log +``` + +### Error npm install + +```bash +# Limpiar caché y reintentar +cd frontend && rm -rf node_modules && npm install + +# Ver log +cat /tmp/coconews-poc/logs/npm-install.log +``` + +### PostgreSQL no arranca + +```bash +sudo systemctl status postgresql +sudo journalctl -u postgresql -n 30 +sudo pg_ctlcluster 16 main status +``` + +--- + +## Logs de la POC + +Todos los logs se guardan en `/tmp/coconews-poc/logs/`: + +| Archivo | Contenido | +|---------|-----------| +| `backend.log` | Logs del servidor Go en tiempo real | +| `build-backend.log` | Salida de compilación Go | +| `build-frontend.log` | Salida de `npm run build` | +| `redis.log` | Logs de Redis | +| `psql-schema.log` | Ejecución del schema SQL | +| `psql-seed.log` | Carga de datos de prueba | +| `frontend-serve.log` | Servidor estático `npx serve` | + +Para seguir los logs en tiempo real mientras la POC corre: + +```bash +# En otra terminal +tail -f /tmp/coconews-poc/logs/backend.log +``` + +--- + +## Siguiente paso: despliegue completo + +Cuando quieras probar el sistema completo con los workers ML: + +```bash +# En un servidor Debian +sudo bash deploy/debian/prerequisites.sh +sudo bash deploy/debian/install.sh +``` + +Consulta [DEPLOY_DEBIAN.md](DEPLOY_DEBIAN.md) para la guía completa. diff --git a/poc/poc.sh b/poc/poc.sh index f18cd4c..0f0c813 100755 --- a/poc/poc.sh +++ b/poc/poc.sh @@ -251,7 +251,32 @@ sudo -u postgres psql -q -d "${POC_DB_NAME}" \ NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" \ -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' \n' || echo "?") -info "BD lista: ${NEWS_COUNT} noticias de prueba" +info "BD lista: ${NEWS_COUNT} noticias de prueba (ES/EN/FR con traducciones y entidades)" + +# Crear usuario admin pre-configurado +step "Creando usuario admin para la demo..." +ADMIN_USER="admin" +ADMIN_PASS="admin123" +ADMIN_CREATED=false + +if python3 -c "import bcrypt" 2>/dev/null; then + ADMIN_HASH=$(python3 -c " +import bcrypt +hashed = bcrypt.hashpw(b'admin123', bcrypt.gensalt(rounds=10)) +print(hashed.decode()) +") + sudo -u postgres psql -q -d "${POC_DB_NAME}" -c \ + "INSERT INTO users (username, email, password_hash, is_admin, role) + VALUES ('${ADMIN_USER}', 'admin@coconews.local', '${ADMIN_HASH}', TRUE, 'admin') + ON CONFLICT (username) DO NOTHING;" 2>/dev/null && ADMIN_CREATED=true || true +fi + +if [[ "$ADMIN_CREATED" == "true" ]]; then + info "Admin listo: ${ADMIN_USER} / ${ADMIN_PASS}" +else + warn "python3-bcrypt no disponible — regístrate en la UI (primer usuario será admin)" + warn "Instala bcrypt: pip3 install bcrypt y vuelve a ejecutar --clean" +fi # ============================================================================= # 4. REDIS (instancia temporal en puerto alternativo) @@ -369,7 +394,7 @@ if [[ ! -d node_modules ]]; then fi step " Compilando frontend..." -VITE_API_URL="http://127.0.0.1:${POC_API_PORT}" \ +VITE_API_URL="http://127.0.0.1:${POC_API_PORT}/api" \ npm run build -- --outDir "$TMP_DIR/frontend-dist" \ >"$LOG_DIR/build-frontend.log" 2>&1 || { echo -e " ${RED}Error de compilación del frontend:${NC}" @@ -414,11 +439,16 @@ echo -e " ${BOLD}Endpoints útiles:${NC}" echo -e " API stats: http://127.0.0.1:${POC_API_PORT}/api/stats" echo -e " API noticias: http://127.0.0.1:${POC_API_PORT}/api/news" echo "" -echo -e " ${BOLD}Primer login:${NC}" +echo -e " ${BOLD}Login admin:${NC}" +if [[ "$ADMIN_CREATED" == "true" ]]; then +echo -e " Usuario: ${BOLD}${ADMIN_USER}${NC}" +echo -e " Contraseña: ${BOLD}${ADMIN_PASS}${NC}" +else echo -e " Regístrate en la UI → el primer usuario es admin automáticamente" +fi echo "" -echo -e " ${BOLD}Datos cargados:${NC} ${NEWS_COUNT} noticias de prueba en español" -echo -e " ${YELLOW}Sin workers ML:${NC} no hay traducción ni entidades (normal en POC)" +echo -e " ${BOLD}Datos cargados:${NC} ${NEWS_COUNT} noticias (ES/EN/FR), traducciones y entidades NER" +echo -e " ${YELLOW}Sin workers ML:${NC} sin ingesta en tiempo real (normal en POC)" echo "" echo -e " ${BOLD}Si algo falla:${NC}" echo -e " Logs: $LOG_DIR/" diff --git a/poc/seed.sql b/poc/seed.sql index 44772d7..15aeed3 100644 --- a/poc/seed.sql +++ b/poc/seed.sql @@ -1,94 +1,255 @@ -- ============================================================================= --- COCONEWS POC - Datos de prueba mínimos --- Carga rápida para ver la interfaz funcionando sin workers ML +-- COCONEWS POC — Datos de demostración +-- Cubre: taxonomía, feeds, noticias en 3 idiomas, traducciones, entidades, +-- eventos agrupados y un usuario admin listo para usar -- ============================================================================= --- Taxonomía base +-- --------------------------------------------------------------------------- +-- TAXONOMÍA BASE +-- --------------------------------------------------------------------------- INSERT INTO continentes (id, nombre) VALUES - (1, 'África'), (2, 'América'), (3, 'Asia'), - (4, 'Europa'), (5, 'Oceanía') + (1,'África'),(2,'América'),(3,'Asia'),(4,'Europa'),(5,'Oceanía') ON CONFLICT (id) DO NOTHING; INSERT INTO categorias (nombre) VALUES - ('Ciencia'), ('Cultura'), ('Deportes'), ('Economía'), - ('Internacional'), ('Política'), ('Salud'), ('Tecnología'), ('Sociedad') + ('Ciencia'),('Cultura'),('Deportes'),('Economía'), + ('Internacional'),('Política'),('Salud'),('Tecnología'),('Sociedad') ON CONFLICT DO NOTHING; INSERT INTO paises (nombre, continente_id) VALUES - ('España', 4), - ('Argentina', 2), - ('México', 2), - ('Francia', 4), - ('Estados Unidos', 2) + ('España',4),('Argentina',2),('México',2),('Francia',4), + ('Estados Unidos',2),('Reino Unido',4),('Alemania',4),('China',3), + ('Brasil',2),('Italia',4) ON CONFLICT DO NOTHING; --- Config básica INSERT INTO config (key, value) VALUES - ('translator_type', 'cpu'), - ('translator_workers', '1'), - ('translator_status', 'stopped') + ('translator_type','cpu'), + ('translator_workers','1'), + ('translator_status','stopped') ON CONFLICT (key) DO NOTHING; --- Feeds de muestra (en español, no necesitan traducción) -INSERT INTO feeds (nombre, descripcion, url, idioma, activo, fallos) -VALUES - ('El País', 'Noticias de España y el mundo', 'https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada', 'es', true, 0), - ('El Mundo', 'Diario de información general', 'https://e00-elmundo.uecdn.es/elmundo/rss/portada.xml', 'es', true, 0), - ('La Vanguardia','Noticias de España y Cataluña', 'https://www.lavanguardia.com/mvc/feed/rss/home', 'es', true, 0), - ('BBC Mundo', 'Noticias en español de la BBC', 'https://feeds.bbci.co.uk/mundo/rss.xml', 'es', true, 0), - ('RT Español', 'Russia Today en español', 'https://actualidad.rt.com/rss', 'es', true, 0) +-- --------------------------------------------------------------------------- +-- FEEDS +-- --------------------------------------------------------------------------- +INSERT INTO feeds (nombre, descripcion, url, idioma, activo, fallos) VALUES + ('El País', 'Diario de referencia en español', 'https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada','es',true,0), + ('El Mundo', 'Información general de España', 'https://e00-elmundo.uecdn.es/elmundo/rss/portada.xml', 'es',true,0), + ('La Vanguardia', 'Cataluña y España', 'https://www.lavanguardia.com/mvc/feed/rss/home', 'es',true,0), + ('BBC Mundo', 'BBC en español', 'https://feeds.bbci.co.uk/mundo/rss.xml', 'es',true,0), + ('Le Monde', 'Diario francés de referencia', 'https://www.lemonde.fr/rss/une.xml', 'fr',true,0), + ('The Guardian', 'Periódico británico independiente', 'https://www.theguardian.com/world/rss', 'en',true,0), + ('Reuters', 'Agencia de noticias internacional', 'https://feeds.reuters.com/reuters/topNews', 'en',true,0), + ('El Confidencial','Periodismo de investigación en España', 'https://rss.elconfidencial.com/espana/', 'es',true,0) ON CONFLICT (url) DO NOTHING; --- Noticias de muestra (en español, listas para mostrarse sin traducción) -INSERT INTO noticias (id, titulo, resumen, url, fecha, fuente_nombre, categoria_id, lang, topics_processed) -VALUES - (md5('poc-001'), 'La inteligencia artificial transforma el mercado laboral global', - 'Los modelos de lenguaje de gran escala están redefiniendo sectores enteros de la economía, desde el servicio al cliente hasta el desarrollo de software. Empresas de todo el mundo aceleran su adopción mientras sindicatos y gobiernos debaten marcos regulatorios.', - 'https://example.com/ia-mercado-laboral', NOW() - INTERVAL '2 hours', - 'El País', 8, 'es', false), +-- --------------------------------------------------------------------------- +-- NOTICIAS — En español (no requieren traducción para mostrarse) +-- --------------------------------------------------------------------------- +INSERT INTO noticias (id,titulo,resumen,url,fecha,fuente_nombre,categoria_id,lang,topics_processed) VALUES +(md5('poc-es-01'), + 'La inteligencia artificial supera a médicos en diagnóstico de cáncer de piel', + 'Un modelo de deep learning desarrollado por investigadores del MIT logró detectar melanomas con una precisión del 94,2%, superando en 8 puntos porcentuales al diagnóstico de dermatólogos expertos en un ensayo clínico con 12.000 imágenes.', + 'https://example.com/ia-cancer-piel',NOW()-INTERVAL '1 hour','El País',1,'es',false), - (md5('poc-002'), 'Cumbre climática de la ONU aprueba fondo de 100.000 millones para países vulnerables', - 'Los representantes de 196 países alcanzaron un acuerdo histórico en la última jornada de negociaciones. El fondo estará operativo en 2026 y priorizará adaptación en África subsahariana y pequeñas islas del Pacífico.', - 'https://example.com/cumbre-climatica-onu', NOW() - INTERVAL '4 hours', - 'BBC Mundo', 5, 'es', false), +(md5('poc-es-02'), + 'España aprueba la mayor inversión en energía solar de su historia: 15.000 millones', + 'El Consejo de Ministros ha dado luz verde a un plan nacional que desplegará 40 gigavatios de capacidad fotovoltaica antes de 2030. La medida creará 120.000 empleos directos y situará a España como segundo productor solar de Europa.', + 'https://example.com/solar-espana',NOW()-INTERVAL '2 hours','La Vanguardia',4,'es',false), - (md5('poc-003'), 'España registra el mayor crecimiento económico de la eurozona en el primer trimestre', - 'El PIB español creció un 3,2% interanual en los primeros tres meses del año, impulsado por el turismo, las exportaciones y el consumo interno. El Banco de España revisa al alza sus previsiones para el conjunto del ejercicio.', - 'https://example.com/economia-espana-pib', NOW() - INTERVAL '5 hours', - 'El Mundo', 4, 'es', false), +(md5('poc-es-03'), + 'El Barça remonta ante el City y se clasifica para la final de Champions', + 'Remontada histórica en el Camp Nou. El Barcelona superó al Manchester City (3-1) tras ir perdiendo al descanso, con un Lamine Yamal estratosférico que firmó dos goles y una asistencia. La final se disputará en Wembley el próximo 31 de mayo.', + 'https://example.com/barca-champions',NOW()-INTERVAL '3 hours','El Mundo',3,'es',false), - (md5('poc-004'), 'La selección española de fútbol golea en la fase de clasificación', - 'La Roja aplastó por 4-0 al combinado rival con dos goles de Yamal y otros tantos de Morata en un partido que dejó pocas dudas sobre el potencial del equipo de Luis de la Fuente de cara al próximo gran torneo.', - 'https://example.com/seleccion-espana-futbol', NOW() - INTERVAL '6 hours', - 'La Vanguardia', 3, 'es', false), +(md5('poc-es-04'), + 'Argentina presenta un plan económico de estabilización con el FMI por 40.000 millones', + 'El gobierno de Buenos Aires y el Fondo Monetario Internacional cerraron un acuerdo que incluye una línea de crédito récord, reformas estructurales en el sector energético y un calendario de reducción del déficit fiscal hasta el equilibrio en 2026.', + 'https://example.com/argentina-fmi',NOW()-INTERVAL '4 hours','BBC Mundo',4,'es',false), - (md5('poc-005'), 'Descubrimiento arqueológico en Extremadura revela ciudad romana inédita', - 'Un equipo de la Universidad de Extremadura ha localizado los restos de un asentamiento romano del siglo II d.C. con teatro, termas y foro en perfecto estado de conservación bajo un olivar de la comarca de La Serena.', - 'https://example.com/arqueologia-extremadura', NOW() - INTERVAL '8 hours', - 'El País', 2, 'es', false), +(md5('poc-es-05'), + 'Descubrimiento en Pompeya revela un mercado de esclavos del siglo I d.C.', + 'Arqueólogos italianos desenterraron en el sector norte de Pompeya una estancia con frescos únicos que documentan por primera vez visualmente la venta de esclavos en el mundo romano. El hallazgo reescribe la comprensión del comercio humano en la Antigüedad.', + 'https://example.com/pompeya-esclavos',NOW()-INTERVAL '5 hours','El País',2,'es',false), - (md5('poc-006'), 'Nuevo fármaco contra el Alzheimer obtiene aprobación de la EMA', - 'La Agencia Europea del Medicamento ha dado luz verde al primer tratamiento que demuestra ralentizar significativamente el deterioro cognitivo en fases tempranas. El medicamento llegará a las farmacias europeas antes de final de año.', - 'https://example.com/farmaco-alzheimer-ema', NOW() - INTERVAL '10 hours', - 'BBC Mundo', 7, 'es', false), +(md5('poc-es-06'), + 'La OMS declara la resistencia antimicrobiana como emergencia sanitaria global', + 'La Organización Mundial de la Salud elevó al máximo nivel de alerta la crisis de los antibióticos, estimando 10 millones de muertes anuales para 2050 si no se toman medidas urgentes. Propone un fondo global de 5.000 millones de dólares para investigación.', + 'https://example.com/oms-antibioticos',NOW()-INTERVAL '6 hours','BBC Mundo',7,'es',false), - (md5('poc-007'), 'México anuncia plan de inversión en energías renovables por 50.000 millones', - 'El gobierno mexicano presentó su Estrategia Nacional de Transición Energética que contempla duplicar la capacidad solar y eólica instalada antes de 2030, con fuerte participación de capital privado nacional e internacional.', - 'https://example.com/mexico-renovables', NOW() - INTERVAL '12 hours', - 'RT Español', 5, 'es', false), +(md5('poc-es-07'), + 'Tesla presenta su robotaxi autónomo: sin volante, sin pedales y a 0,19€ el kilómetro', + 'Elon Musk reveló el Cybercab en un evento en Los Ángeles. El vehículo sin controles manuales utilizará visión por computador para la conducción autónoma de nivel 5 y estará disponible en 2026. El precio objetivo es 30.000 dólares por unidad.', + 'https://example.com/tesla-robotaxi',NOW()-INTERVAL '7 hours','El Confidencial',8,'es',false), - (md5('poc-008'), 'OpenAI lanza modelo multimodal capaz de generar video fotorrealista en tiempo real', - 'La empresa californiana presentó Sora 2, capaz de producir secuencias de vídeo de alta definición en menos de 30 segundos. Investigadores advierten sobre los riesgos de desinformación mientras la compañía promete mecanismos de marca de agua.', - 'https://example.com/openai-sora2', NOW() - INTERVAL '14 hours', - 'El Mundo', 8, 'es', false), +(md5('poc-es-08'), + 'México bate récord de remesas: 65.000 millones de dólares enviados desde el exterior', + 'El Banco de México informó que las transferencias de mexicanos en el extranjero alcanzaron un máximo histórico, representando ya el 3,8% del PIB nacional. Los estados de Michoacán, Jalisco y Guanajuato concentran el 45% de los ingresos.', + 'https://example.com/mexico-remesas',NOW()-INTERVAL '8 hours','BBC Mundo',4,'es',false), - (md5('poc-009'), 'Argentina cierra acuerdo comercial con la Unión Europea tras 25 años de negociaciones', - 'El tratado de libre comercio Mercosur-UE entra en vigor de forma provisional tras superar los últimos obstáculos relacionados con protección ambiental y acceso al mercado agrícola europeo para los productos del cono sur.', - 'https://example.com/argentina-acuerdo-ue', NOW() - INTERVAL '18 hours', - 'BBC Mundo', 4, 'es', false), +(md5('poc-es-09'), + 'Científicos españoles desarrollan una vacuna universal contra la gripe', + 'El equipo del CSIC liderado por la doctora Carmen López logró una vacuna que actúa sobre una región conservada del virus influenza, ofreciendo protección cruzada contra todas las cepas conocidas en estudios preclínicos con primates.', + 'https://example.com/vacuna-gripe-csic',NOW()-INTERVAL '9 hours','El País',1,'es',false), + +(md5('poc-es-10'), + 'El Gobierno lanza un bono cultural de 400€ para jóvenes de 18 años', + 'A partir del próximo trimestre, todos los españoles al cumplir la mayoría de edad recibirán un bono digital para gastar en libros, entradas de cine, teatro, museos y plataformas de streaming nacionales. El programa costará 200 millones anuales.', + 'https://example.com/bono-cultural',NOW()-INTERVAL '10 hours','La Vanguardia',2,'es',false), + +-- --------------------------------------------------------------------------- +-- NOTICIAS — En inglés (con traducción al español en tabla traducciones) +-- --------------------------------------------------------------------------- +(md5('poc-en-01'), + 'OpenAI releases GPT-5 with real-time reasoning and 1 million token context', + 'OpenAI announced GPT-5, its most advanced language model, featuring native multimodality, real-time web access and a context window of one million tokens — equivalent to an entire novel. The model outperforms human experts on 87% of professional benchmarks.', + 'https://example.com/gpt5-release',NOW()-INTERVAL '11 hours','The Guardian',8,'en',false), + +(md5('poc-en-02'), + 'NASA confirms water ice deposits at lunar south pole ahead of Artemis mission', + 'New data from the LCROSS mission confirms significant water ice deposits at Shackleton Crater near the lunar south pole. Scientists estimate up to 600 million metric tons of frozen water, enough to sustain a permanent Moon base for decades.', + 'https://example.com/nasa-moon-water',NOW()-INTERVAL '13 hours','Reuters',1,'en',false), + +(md5('poc-en-03'), + 'UK economy grows 3.1% in Q1, strongest performance since 2015', + 'Britain''s economy expanded at its fastest quarterly rate in nearly a decade, driven by a booming services sector, record exports of financial products and a surge in green technology manufacturing. The pound hit a two-year high against the dollar.', + 'https://example.com/uk-economy',NOW()-INTERVAL '15 hours','The Guardian',4,'en',false), + +(md5('poc-en-04'), + 'China launches world''s largest offshore wind farm: 16 gigawatts off Fujian coast', + 'State Grid Corporation of China connected the final turbines of the Fujian Offshore Wind Mega-Farm, generating enough clean electricity to power 20 million homes. The project took four years to build and employs 8,000 workers in ongoing maintenance.', + 'https://example.com/china-wind-farm',NOW()-INTERVAL '17 hours','Reuters',8,'en',false), + +(md5('poc-en-05'), + 'Amazon to acquire healthcare giant Humana for $28 billion in landmark deal', + 'In what would be the second-largest acquisition in Amazon''s history, the e-commerce and cloud giant has agreed to purchase Humana, one of the largest US health insurers. Regulators will review the deal which aims to combine pharmacy, insurance and logistics.', + 'https://example.com/amazon-humana',NOW()-INTERVAL '19 hours','The Guardian',4,'en',false), + +-- --------------------------------------------------------------------------- +-- NOTICIAS — En francés (con traducción al español) +-- --------------------------------------------------------------------------- +(md5('poc-fr-01'), + 'Paris 2028 : la France investit 8 milliards dans les infrastructures sportives', + 'Le gouvernement a présenté son plan d''investissement pour les Jeux Olympiques de Paris 2028, prévoyant la construction de 12 nouvelles arènes, la rénovation du Stade de France et la création d''un village olympique durable en Seine-Saint-Denis.', + 'https://example.com/paris-2028',NOW()-INTERVAL '21 hours','Le Monde',3,'fr',false), + +(md5('poc-fr-02'), + 'Macron annonce une réforme fiscale majeure pour les classes moyennes françaises', + 'Dans un discours à l''Élysée, le président Macron a dévoilé un allègement d''impôts de 15 milliards d''euros pour les foyers gagnant entre 2.000 et 5.000 euros par mois, financé par une taxe exceptionnelle sur les bénéfices des multinationales.', + 'https://example.com/macron-fiscalite',NOW()-INTERVAL '23 hours','Le Monde',6,'fr',false) - (md5('poc-010'), 'Telescopio James Webb detecta atmósfera en exoplaneta a 40 años luz de la Tierra', - 'Astrónomos del Instituto de Tecnología de California confirmaron la presencia de dióxido de carbono y vapor de agua en la atmósfera del exoplaneta K2-18b, abriendo nuevas posibilidades en la búsqueda de condiciones habitables fuera del sistema solar.', - 'https://example.com/james-webb-exoplaneta', NOW() - INTERVAL '22 hours', - 'El País', 1, 'es', false) ON CONFLICT (id) DO NOTHING; + +-- --------------------------------------------------------------------------- +-- TRADUCCIONES — Artículos en inglés traducidos al español +-- --------------------------------------------------------------------------- +INSERT INTO traducciones (noticia_id,lang_from,lang_to,titulo_trad,resumen_trad,status,vectorized) VALUES + +(md5('poc-en-01'),'en','es', + 'OpenAI lanza GPT-5 con razonamiento en tiempo real y contexto de 1 millón de tokens', + 'OpenAI presentó GPT-5, su modelo de lenguaje más avanzado, con multimodalidad nativa, acceso web en tiempo real y una ventana de contexto de un millón de tokens, equivalente a una novela entera. El modelo supera a expertos humanos en el 87% de las pruebas profesionales.', + 'done',false), + +(md5('poc-en-02'),'en','es', + 'La NASA confirma depósitos de hielo de agua en el polo sur lunar antes de la misión Artemis', + 'Nuevos datos de la misión LCROSS confirman importantes depósitos de hielo de agua en el cráter Shackleton, cerca del polo sur de la Luna. Los científicos estiman hasta 600 millones de toneladas métricas de agua congelada, suficiente para sostener una base lunar permanente durante décadas.', + 'done',false), + +(md5('poc-en-03'),'en','es', + 'La economía del Reino Unido crece un 3,1% en el primer trimestre, el mejor dato desde 2015', + 'La economía británica se expandió a su ritmo trimestral más rápido en casi una década, impulsada por un sector servicios en auge, exportaciones récord de productos financieros y un repunte en la fabricación de tecnología verde. La libra alcanzó su máximo en dos años frente al dólar.', + 'done',false), + +(md5('poc-en-04'),'en','es', + 'China inaugura el mayor parque eólico marino del mundo: 16 gigavatios frente a la costa de Fujian', + 'State Grid Corporation of China conectó las últimas turbinas de la Mega-Granja Eólica Offshore de Fujian, generando suficiente electricidad limpia para abastecer a 20 millones de hogares. El proyecto tardó cuatro años en construirse y emplea a 8.000 trabajadores en mantenimiento.', + 'done',false), + +(md5('poc-en-05'),'en','es', + 'Amazon adquirirá el gigante sanitario Humana por 28.000 millones de dólares', + 'En lo que sería la segunda mayor adquisición de la historia de Amazon, el gigante del comercio electrónico y la nube ha acordado comprar Humana, una de las mayores aseguradoras de salud de EE.UU. Los reguladores revisarán el acuerdo que busca combinar farmacia, seguros y logística.', + 'done',false) + +ON CONFLICT (noticia_id, lang_to) DO NOTHING; + +-- Traducciones de artículos en francés +INSERT INTO traducciones (noticia_id,lang_from,lang_to,titulo_trad,resumen_trad,status,vectorized) VALUES + +(md5('poc-fr-01'),'fr','es', + 'París 2028: Francia invierte 8.000 millones en infraestructuras deportivas', + 'El gobierno presentó su plan de inversión para los Juegos Olímpicos de París 2028, que prevé la construcción de 12 nuevos recintos, la renovación del Estadio de Francia y la creación de una villa olímpica sostenible en Seine-Saint-Denis.', + 'done',false), + +(md5('poc-fr-02'),'fr','es', + 'Macron anuncia una reforma fiscal de gran calado para las clases medias francesas', + 'En un discurso en el Elíseo, el presidente Macron presentó una reducción fiscal de 15.000 millones de euros para los hogares que ganan entre 2.000 y 5.000 euros mensuales, financiada por un impuesto excepcional sobre los beneficios de las multinacionales.', + 'done',false) + +ON CONFLICT (noticia_id, lang_to) DO NOTHING; + +-- Traducciones "self" para artículos en español (necesarias para que aparezcan en filtro translated_only) +INSERT INTO traducciones (noticia_id,lang_from,lang_to,titulo_trad,resumen_trad,status,vectorized) +SELECT id,'es','es',titulo,resumen,'done',false +FROM noticias WHERE lang='es' AND id LIKE md5('poc-es-%') +ON CONFLICT (noticia_id, lang_to) DO NOTHING; + +-- --------------------------------------------------------------------------- +-- ENTIDADES (NER tags) para dar vida a los tooltips de Wikipedia +-- --------------------------------------------------------------------------- +INSERT INTO tags (valor, tipo) VALUES + ('Elon Musk', 'persona'), + ('Lamine Yamal', 'persona'), + ('Carmen López', 'persona'), + ('Emmanuel Macron', 'persona'), + ('NASA', 'organizacion'), + ('OpenAI', 'organizacion'), + ('Tesla', 'organizacion'), + ('FMI', 'organizacion'), + ('OMS', 'organizacion'), + ('CSIC', 'organizacion'), + ('Amazon', 'organizacion'), + ('Manchester City', 'organizacion'), + ('FC Barcelona', 'organizacion'), + ('España', 'lugar'), + ('Argentina', 'lugar'), + ('México', 'lugar'), + ('Francia', 'lugar'), + ('China', 'lugar'), + ('Estados Unidos', 'lugar'), + ('Luna', 'lugar'), + ('Pompeya', 'lugar'), + ('inteligencia artificial','tema'), + ('energía solar', 'tema'), + ('Champions League', 'tema'), + ('vacuna', 'tema') +ON CONFLICT (valor, tipo) DO NOTHING; + +-- Asociar entidades a noticias +INSERT INTO tags_noticia (tag_id, noticia_id) +SELECT t.id, n.id FROM tags t, noticias n +WHERE (t.valor='OpenAI' AND n.id=md5('poc-en-01')) + OR (t.valor='inteligencia artificial' AND n.id=md5('poc-en-01')) + OR (t.valor='NASA' AND n.id=md5('poc-en-02')) + OR (t.valor='Luna' AND n.id=md5('poc-en-02')) + OR (t.valor='Tesla' AND n.id=md5('poc-es-07')) + OR (t.valor='Elon Musk' AND n.id=md5('poc-es-07')) + OR (t.valor='Lamine Yamal' AND n.id=md5('poc-es-03')) + OR (t.valor='FC Barcelona' AND n.id=md5('poc-es-03')) + OR (t.valor='Manchester City' AND n.id=md5('poc-es-03')) + OR (t.valor='Champions League' AND n.id=md5('poc-es-03')) + OR (t.valor='FMI' AND n.id=md5('poc-es-04')) + OR (t.valor='Argentina' AND n.id=md5('poc-es-04')) + OR (t.valor='OMS' AND n.id=md5('poc-es-06')) + OR (t.valor='CSIC' AND n.id=md5('poc-es-09')) + OR (t.valor='Carmen López' AND n.id=md5('poc-es-09')) + OR (t.valor='vacuna' AND n.id=md5('poc-es-09')) + OR (t.valor='Emmanuel Macron' AND n.id=md5('poc-fr-02')) + OR (t.valor='Francia' AND n.id=md5('poc-fr-02')) + OR (t.valor='Amazon' AND n.id=md5('poc-en-05')) + OR (t.valor='España' AND n.id=md5('poc-es-02')) + OR (t.valor='energía solar' AND n.id=md5('poc-es-02')) + OR (t.valor='China' AND n.id=md5('poc-en-04')) +ON CONFLICT DO NOTHING; + +-- --------------------------------------------------------------------------- +-- NOTA: El usuario admin se crea en poc.sh con hash bcrypt generado en runtime +-- --------------------------------------------------------------------------- From d9ea78b8a78f13e2120fe501b5da344c409f9cd1 Mon Sep 17 00:00:00 2001 From: SITO Date: Tue, 31 Mar 2026 08:57:01 +0200 Subject: [PATCH 08/21] fix: revision completa de rutas Docker, logica SQL y configuracion Backend Go: - backend/cmd/server/main.go: ruta wiki_images configurable via WIKI_IMAGES_PATH - backend/cmd/wiki_worker/main.go: default /opt/rss2 en lugar de /app, leer env - workers/ctranslator_worker.py: default CT2_MODEL_PATH /opt/rss2 en lugar de /app - workers/llm_categorizer_worker.py: default LLM_MODEL_PATH /opt/rss2 - workers/{langdetect,simple_translator,translation_scheduler}.py: DB_HOST default 'localhost' en lugar de 'db' (hostname Docker) SQL / esquema: - poc/seed.sql: corregir logica de auto-traducciones ES (id LIKE md5() era incorrecto) - init-db/06-tags.sql: eliminar columna wiki_checked duplicada Documentacion y configuracion: - docs/DEPLOY_DEBIAN.md: usar ct2-transformers-converter (lo que usa el worker real) - deploy/debian/env.example: agregar WIKI_IMAGES_PATH - deploy/debian/systemd/rss2-cluster.service: agregar HF_HOME faltante - deploy/debian/install.sh: comparacion numerica correcta de version Go - scripts/generate_secure_credentials.sh: ruta CT2_MODEL_PATH corregida - frontend/nginx.conf: advertencia de que es configuracion Docker legacy - docs/QUICKSTART_LLM.md: nota de deprecacion Docker - README.md: renombrar backend-go a backend en diagrama Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- backend/cmd/server/main.go | 6 +++++- backend/cmd/wiki_worker/main.go | 5 ++++- deploy/debian/env.example | 1 + deploy/debian/install.sh | 12 ++++++++++- deploy/debian/systemd/rss2-cluster.service | 1 + docs/DEPLOY_DEBIAN.md | 23 ++++++++++++++-------- docs/QUICKSTART_LLM.md | 6 +++++- frontend/nginx.conf | 5 +++++ init-db/06-tags.sql | 1 - poc/seed.sql | 2 +- scripts/generate_secure_credentials.sh | 2 +- workers/ctranslator_worker.py | 2 +- workers/langdetect_worker.py | 2 +- workers/llm_categorizer_worker.py | 2 +- workers/simple_translator_worker.py | 2 +- workers/translation_scheduler.py | 2 +- 17 files changed, 55 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f4a8ece..ea6ff06 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Internet (RSS/Atom) │ │ │ qdrant-worker ──→ Qdrant │ - backend-go (API REST :8080) + backend (API REST :8080) │ nginx (:8001) │ diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index cf13d80..4596b11 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -109,7 +109,11 @@ func main() { api := r.Group("/api") { // Serve static images downloaded by wiki_worker - api.StaticFS("/wiki-images", gin.Dir("/app/data/wiki_images", false)) + wikiImagesDir := os.Getenv("WIKI_IMAGES_PATH") + if wikiImagesDir == "" { + wikiImagesDir = "/opt/rss2/data/wiki_images" + } + api.StaticFS("/wiki-images", gin.Dir(wikiImagesDir, false)) api.POST("/auth/login", handlers.Login) api.POST("/auth/register", handlers.Register) diff --git a/backend/cmd/wiki_worker/main.go b/backend/cmd/wiki_worker/main.go index c58e07c..0065c94 100644 --- a/backend/cmd/wiki_worker/main.go +++ b/backend/cmd/wiki_worker/main.go @@ -24,7 +24,7 @@ var ( pool *pgxpool.Pool sleepInterval = 30 batchSize = 50 - imagesDir = "/app/data/wiki_images" + imagesDir = "/opt/rss2/data/wiki_images" ) type WikiSummary struct { @@ -210,6 +210,9 @@ func processTag(ctx context.Context, tag Tag) { } func main() { + if val := os.Getenv("WIKI_IMAGES_PATH"); val != "" { + imagesDir = val + } if val := os.Getenv("WIKI_SLEEP"); val != "" { if sleep, err := fmt.Sscanf(val, "%d", &sleepInterval); err == nil && sleep > 0 { sleepInterval = sleep diff --git a/deploy/debian/env.example b/deploy/debian/env.example index 406f928..728411b 100644 --- a/deploy/debian/env.example +++ b/deploy/debian/env.example @@ -81,6 +81,7 @@ MAX_FEEDS_PER_URL=5 # --- Wiki Worker --- WIKI_SLEEP=10 +WIKI_IMAGES_PATH=/opt/rss2/data/wiki_images # --- Topics --- TOPICS_SLEEP=10 diff --git a/deploy/debian/install.sh b/deploy/debian/install.sh index 128336f..76dfed5 100755 --- a/deploy/debian/install.sh +++ b/deploy/debian/install.sh @@ -33,7 +33,17 @@ apt-get install -y --no-install-recommends \ libpq-dev # Go (rss-ingestor-go requiere Go 1.25) -if ! command -v go &>/dev/null || [[ "$(go version | awk '{print $3}' | tr -d 'go')" < "1.25" ]]; then +_need_go=false +if ! command -v go &>/dev/null; then + _need_go=true +else + _gover=$(go version | awk '{print $3}' | tr -d 'go') + IFS='.' read -ra _gv <<< "$_gover" + if [[ "${_gv[0]:-0}" -lt 1 ]] || [[ "${_gv[0]:-0}" -eq 1 && "${_gv[1]:-0}" -lt 25 ]]; then + _need_go=true + fi +fi +if [[ "$_need_go" == "true" ]]; then info "Instalando Go 1.25..." GO_VERSION="1.25.0" ARCH=$(dpkg --print-architecture) diff --git a/deploy/debian/systemd/rss2-cluster.service b/deploy/debian/systemd/rss2-cluster.service index dd990fb..e39cdb6 100644 --- a/deploy/debian/systemd/rss2-cluster.service +++ b/deploy/debian/systemd/rss2-cluster.service @@ -11,6 +11,7 @@ WorkingDirectory=/opt/rss2/src EnvironmentFile=/opt/rss2/.env Environment=EVENT_DIST_THRESHOLD=0.35 Environment=EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +Environment=HF_HOME=/opt/rss2/hf_cache ExecStart=/opt/rss2/venv/bin/python -m workers.cluster_worker Restart=always RestartSec=10 diff --git a/docs/DEPLOY_DEBIAN.md b/docs/DEPLOY_DEBIAN.md index 9835f0a..b3bad07 100644 --- a/docs/DEPLOY_DEBIAN.md +++ b/docs/DEPLOY_DEBIAN.md @@ -76,16 +76,23 @@ python3 -m venv /opt/rss2/venv /opt/rss2/venv/bin/pip install ctranslate2 transformers sentencepiece # Convertir modelo NLLB-200 a formato CTranslate2 (tarda 10-30 min) -/opt/rss2/venv/bin/python - <<'EOF' -from ctranslate2.converters import OpusMTConverter -converter = OpusMTConverter("facebook/nllb-200-distilled-600M") -converter.convert("/opt/rss2/models/nllb-ct2", quantization="int8", force=True) -print("Modelo convertido OK en /opt/rss2/models/nllb-ct2") -EOF +mkdir -p /opt/rss2/models/nllb-ct2 +HF_HOME=/opt/rss2/hf_cache \ +/opt/rss2/venv/bin/ct2-transformers-converter \ + --model facebook/nllb-200-distilled-600M \ + --output_dir /opt/rss2/models/nllb-ct2 \ + --quantization int8 \ + --force + +# Verificar que se generó correctamente +ls /opt/rss2/models/nllb-ct2/model.bin && echo "Modelo OK" ``` -> El modelo ocupa ~600 MB convertido. Si la descarga de HuggingFace falla, exporta -> `HF_ENDPOINT=https://huggingface.co` o usa un mirror. +> El modelo ocupa ~600 MB convertido. Si la descarga de HuggingFace falla: +> `export HF_ENDPOINT=https://huggingface.co` antes del comando de conversión. + +> **Nota:** El worker convierte el modelo automáticamente si no lo encuentra, +> pero hacerlo a mano evita que el primer arranque tarde 30 minutos. ### 4. Ejecutar el instalador diff --git a/docs/QUICKSTART_LLM.md b/docs/QUICKSTART_LLM.md index 0baced3..61f7bb4 100644 --- a/docs/QUICKSTART_LLM.md +++ b/docs/QUICKSTART_LLM.md @@ -1,4 +1,8 @@ -# 🚀 Guía Rápida: Sistema LLM Categorizer +> **NOTA:** Esta guía está basada en la configuración Docker original. En el despliegue +> Debian nativo, el LLM categorizer se controla con `systemctl start rss2-categorizer` +> y el modelo se coloca en `/opt/rss2/models/llm` (var `LLM_MODEL_PATH`). + +# Guía Rápida: Sistema LLM Categorizer ## ✅ Estado Actual diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 9331418..61a14ba 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -1,3 +1,8 @@ +# ============================================================================= +# NOTA: Este nginx.conf es la configuración del contenedor Docker del frontend. +# NO usar para despliegue nativo Debian — usar deploy/debian/nginx.conf +# ============================================================================= + events { worker_connections 1024; } diff --git a/init-db/06-tags.sql b/init-db/06-tags.sql index dfd8393..5613d97 100644 --- a/init-db/06-tags.sql +++ b/init-db/06-tags.sql @@ -14,4 +14,3 @@ ALTER TABLE tags ADD COLUMN IF NOT EXISTS wiki_summary TEXT; ALTER TABLE tags ADD COLUMN IF NOT EXISTS wiki_url TEXT; ALTER TABLE tags ADD COLUMN IF NOT EXISTS image_path TEXT; ALTER TABLE tags ADD COLUMN IF NOT EXISTS wiki_checked BOOLEAN DEFAULT FALSE; -ALTER TABLE tags ADD COLUMN IF NOT EXISTS wiki_checked BOOLEAN DEFAULT FALSE; diff --git a/poc/seed.sql b/poc/seed.sql index 15aeed3..906ea6d 100644 --- a/poc/seed.sql +++ b/poc/seed.sql @@ -189,7 +189,7 @@ ON CONFLICT (noticia_id, lang_to) DO NOTHING; -- Traducciones "self" para artículos en español (necesarias para que aparezcan en filtro translated_only) INSERT INTO traducciones (noticia_id,lang_from,lang_to,titulo_trad,resumen_trad,status,vectorized) SELECT id,'es','es',titulo,resumen,'done',false -FROM noticias WHERE lang='es' AND id LIKE md5('poc-es-%') +FROM noticias WHERE lang='es' ON CONFLICT (noticia_id, lang_to) DO NOTHING; -- --------------------------------------------------------------------------- diff --git a/scripts/generate_secure_credentials.sh b/scripts/generate_secure_credentials.sh index d345db7..4513a1a 100755 --- a/scripts/generate_secure_credentials.sh +++ b/scripts/generate_secure_credentials.sh @@ -129,7 +129,7 @@ URL_DISCOVERY_BATCH_SIZE=10 MAX_FEEDS_PER_URL=5 # CTranslate2 / AI Model Paths -CT2_MODEL_PATH=/app/models/nllb-ct2 +CT2_MODEL_PATH=/opt/rss2/models/nllb-ct2 CT2_DEVICE=cuda CT2_COMPUTE_TYPE=int8_float16 UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 0dc1126..9b96958 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -62,7 +62,7 @@ BATCH_SIZE = _env_int("TRANSLATOR_BATCH", 8) MAX_SRC_TOKENS = _env_int("MAX_SRC_TOKENS", 512) MAX_NEW_TOKENS = _env_int("MAX_NEW_TOKENS", 512) -CT2_MODEL_PATH = _env_str("CT2_MODEL_PATH", "/app/models/nllb-ct2") +CT2_MODEL_PATH = _env_str("CT2_MODEL_PATH", "/opt/rss2/models/nllb-ct2") CT2_DEVICE = _env_str("CT2_DEVICE", "cpu") CT2_COMPUTE_TYPE = _env_str("CT2_COMPUTE_TYPE", "int8") UNIVERSAL_MODEL = _env_str("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M") diff --git a/workers/langdetect_worker.py b/workers/langdetect_worker.py index 3cc9572..01fbd9b 100644 --- a/workers/langdetect_worker.py +++ b/workers/langdetect_worker.py @@ -22,7 +22,7 @@ logging.basicConfig( LOG = logging.getLogger(__name__) DB_CONFIG = { - 'host': os.getenv('DB_HOST', 'db'), + 'host': os.getenv('DB_HOST', 'localhost'), 'port': int(os.getenv('DB_PORT', 5432)), 'database': os.getenv('DB_NAME', 'rss'), 'user': os.getenv('DB_USER', 'rss'), diff --git a/workers/llm_categorizer_worker.py b/workers/llm_categorizer_worker.py index 7f772c9..17ddc9e 100644 --- a/workers/llm_categorizer_worker.py +++ b/workers/llm_categorizer_worker.py @@ -41,7 +41,7 @@ DB_CONFIG = { # Configuración del worker BATCH_SIZE = int(os.environ.get("LLM_BATCH_SIZE", 10)) # 10 noticias por lote SLEEP_IDLE = int(os.environ.get("LLM_SLEEP_IDLE", 30)) # segundos -MODEL_PATH = os.environ.get("LLM_MODEL_PATH", "/app/models/llm") +MODEL_PATH = os.environ.get("LLM_MODEL_PATH", "/opt/rss2/models/llm") GPU_SPLIT = os.environ.get("LLM_GPU_SPLIT", "auto") MAX_SEQ_LEN = int(os.environ.get("LLM_MAX_SEQ_LEN", 4096)) CACHE_MODE = os.environ.get("LLM_CACHE_MODE", "FP16") diff --git a/workers/simple_translator_worker.py b/workers/simple_translator_worker.py index 0412e55..a779171 100644 --- a/workers/simple_translator_worker.py +++ b/workers/simple_translator_worker.py @@ -22,7 +22,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) DB_CONFIG = { - 'host': os.getenv('DB_HOST', 'db'), + 'host': os.getenv('DB_HOST', 'localhost'), 'port': int(os.getenv('DB_PORT', 5432)), 'database': os.getenv('DB_NAME', 'rss'), 'user': os.getenv('DB_USER', 'rss'), diff --git a/workers/translation_scheduler.py b/workers/translation_scheduler.py index 3fd5e2c..572f71a 100644 --- a/workers/translation_scheduler.py +++ b/workers/translation_scheduler.py @@ -22,7 +22,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) DB_CONFIG = { - 'host': os.getenv('DB_HOST', 'db'), + 'host': os.getenv('DB_HOST', 'localhost'), 'port': int(os.getenv('DB_PORT', 5432)), 'database': os.getenv('DB_NAME', 'rss'), 'user': os.getenv('DB_USER', 'rss'), From 313a7c8b19d369dcc320f406cf5e3355a68db491 Mon Sep 17 00:00:00 2001 From: lordpietre Date: Tue, 31 Mar 2026 22:57:47 +0000 Subject: [PATCH 09/21] scrapper --- Dockerfile.scraper | 3 +- backend/cmd/scraper/main.go | 163 +++++++++++++++++++++++++++++++++--- docker-compose.yml | 3 +- frontend/vite.config.ts | 2 +- 4 files changed, 158 insertions(+), 13 deletions(-) diff --git a/Dockerfile.scraper b/Dockerfile.scraper index 9a32bff..02d380e 100644 --- a/Dockerfile.scraper +++ b/Dockerfile.scraper @@ -27,6 +27,7 @@ ENV DB_HOST=db \ DB_USER=rss \ DB_PASS=rss \ SCRAPER_SLEEP=60 \ - SCRAPER_BATCH=10 + SCRAPER_BATCH=10 \ + SCRAPER_ENRICH_LIMIT=20 ENTRYPOINT ["/bin/scraper"] diff --git a/backend/cmd/scraper/main.go b/backend/cmd/scraper/main.go index 5c80191..bfb04e3 100644 --- a/backend/cmd/scraper/main.go +++ b/backend/cmd/scraper/main.go @@ -24,6 +24,7 @@ var ( pool *pgxpool.Pool sleepInterval = 60 batchSize = 10 + enrichLimit = 20 ) type URLSource struct { @@ -36,6 +37,14 @@ type URLSource struct { Active bool } +type Noticia struct { + ID string + Titulo string + Resumen string + URL string + FuenteNombre string +} + type Article struct { Title string Summary string @@ -53,6 +62,7 @@ func init() { func loadConfig() { sleepInterval = getEnvInt("SCRAPER_SLEEP", 60) batchSize = getEnvInt("SCRAPER_BATCH", 10) + enrichLimit = getEnvInt("SCRAPER_ENRICH_LIMIT", 20) } func getEnvInt(key string, defaultValue int) int { @@ -66,9 +76,9 @@ func getEnvInt(key string, defaultValue int) int { func getActiveURLs(ctx context.Context) ([]URLSource, error) { rows, err := pool.Query(ctx, ` - SELECT id, nombre, url, categoria_id, pais_id, idioma, activo - FROM fuentes_url - WHERE activo = true + SELECT id, nombre, url, categoria_id, pais_id, idioma, active + FROM fuentes_url + WHERE active = true `) if err != nil { return nil, err @@ -89,16 +99,49 @@ func getActiveURLs(ctx context.Context) ([]URLSource, error) { func updateSourceStatus(ctx context.Context, sourceID int64, status, message string, httpCode int) error { _, err := pool.Exec(ctx, ` - UPDATE fuentes_url - SET last_check = NOW(), - last_status = $1, - status_message = $2, - last_http_code = $3 - WHERE id = $4 + UPDATE fuentes_url + SET last_check = NOW(), + last_status = $1, + status_message = $2, + last_http_code = $3 + WHERE id = $4 `, status, message, httpCode, sourceID) return err } +func getNoticiasToEnrich(ctx context.Context, limit int) ([]Noticia, error) { + rows, err := pool.Query(ctx, ` + SELECT id, titulo, COALESCE(resumen, ''), url, fuente_nombre + FROM noticias + WHERE (resumen IS NULL OR LENGTH(resumen) < 200) + ORDER BY fecha DESC + LIMIT $1 + `, limit) + if err != nil { + return nil, err + } + defer rows.Close() + + var noticias []Noticia + for rows.Next() { + var n Noticia + if err := rows.Scan(&n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.FuenteNombre); err != nil { + continue + } + noticias = append(noticias, n) + } + return noticias, nil +} + +func updateNoticiaResumen(ctx context.Context, noticiaID, newResumen string) error { + _, err := pool.Exec(ctx, ` + UPDATE noticias + SET resumen = $1 + WHERE id = $2 + `, newResumen, noticiaID) + return err +} + func extractArticle(source URLSource) (*Article, error) { client := &http.Client{ Timeout: 30 * time.Second, @@ -186,6 +229,94 @@ func extractArticle(source URLSource) (*Article, error) { return article, nil } +func extractContentFromURL(url string) (string, error) { + client := &http.Client{ + Timeout: 30 * time.Second, + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", err + } + + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") + req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + req.Header.Set("Accept-Language", "en-US,en;q=0.5") + + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return "", fmt.Errorf("HTTP %d", resp.StatusCode) + } + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + return "", err + } + + contentSelectors := []string{ + "article", + "[role='main']", + "main", + ".article-content", + ".post-content", + ".entry-content", + ".content", + "#content", + ".story-body", + ".article-body", + } + + var content string + for _, sel := range contentSelectors { + content = doc.Find(sel).First().Text() + if len(content) > 100 { + break + } + } + + if len(content) < 100 { + ps := doc.Find("p") + var paragraphs []string + ps.Each(func(i int, s *goquery.Selection) { + txt := strings.TrimSpace(s.Text()) + if len(txt) > 50 { + paragraphs = append(paragraphs, txt) + } + }) + content = strings.Join(paragraphs, "\n\n") + } + + return strings.TrimSpace(content), nil +} + +func processEnrichment(ctx context.Context, noticia Noticia) bool { + logger.Printf("Enriching: %s", noticia.Titulo) + + content, err := extractContentFromURL(noticia.URL) + if err != nil { + logger.Printf("Error extracting content from %s: %v", noticia.URL, err) + return false + } + + if len(content) < 50 { + logger.Printf("Not enough content extracted from %s", noticia.URL) + return false + } + + if err := updateNoticiaResumen(ctx, noticia.ID, content); err != nil { + logger.Printf("Error updating resumen for %s: %v", noticia.ID, err) + return false + } + + logger.Printf("Enriched: %s (content length: %d)", noticia.Titulo, len(content)) + return true +} + func saveArticle(ctx context.Context, source URLSource, article *Article) (bool, error) { finalURL := article.URL if finalURL == "" { @@ -300,7 +431,7 @@ func main() { os.Exit(0) }() - logger.Printf("Config: sleep=%ds, batch=%d", sleepInterval, batchSize) + logger.Printf("Config: sleep=%ds, batch=%d, enrich=%d", sleepInterval, batchSize, enrichLimit) ticker := time.NewTicker(time.Duration(sleepInterval) * time.Second) defer ticker.Stop() @@ -308,6 +439,18 @@ func main() { for { select { case <-ticker.C: + noticias, err := getNoticiasToEnrich(ctx, enrichLimit) + if err != nil { + logger.Printf("Error fetching noticias to enrich: %v", err) + } else if len(noticias) > 0 { + logger.Printf("Enriching %d noticias", len(noticias)) + for _, noticia := range noticias { + if processEnrichment(ctx, noticia) { + time.Sleep(3 * time.Second) + } + } + } + sources, err := getActiveURLs(ctx) if err != nil { logger.Printf("Error fetching URLs: %v", err) diff --git a/docker-compose.yml b/docker-compose.yml index b126c81..44fe777 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -139,6 +139,7 @@ services: DB_PASS: ${DB_PASS} SCRAPER_SLEEP: 60 SCRAPER_BATCH: 10 + SCRAPER_ENRICH_LIMIT: 20 TZ: Europe/Madrid networks: - backend @@ -262,7 +263,7 @@ services: image: nginx:alpine container_name: rss2_nginx ports: - - "8001:80" + - "8888:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro networks: diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 3e35b47..7a8250f 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ port: 3000, proxy: { '/api': { - target: 'http://localhost:8080', + target: 'http://localhost:8888', changeOrigin: true, }, }, From 0fb90c7635a49f613b25873608173f542b912f58 Mon Sep 17 00:00:00 2001 From: lordpietre Date: Wed, 1 Apr 2026 00:42:19 +0000 Subject: [PATCH 10/21] revision --- AGENT.md | 110 +++++++++++++++++++++++++++++++++++++ docker-compose.yml | 2 +- migrations/reset_feeds.sql | 4 ++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 AGENT.md create mode 100644 migrations/reset_feeds.sql diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..bd296f0 --- /dev/null +++ b/AGENT.md @@ -0,0 +1,110 @@ +# RSS2 - Agente de Mejora de Software + +## Visión General del Proyecto + +Aplicación de agregación y procesamiento de noticias RSS con capacidades de IA. + +### Stack Tecnológico + +| Capa | Tecnología | +|------|-------------| +| Frontend | React 18 + TypeScript + Vite + TailwindCSS + React Query + React Router | +| Backend API | Go 1.25 + Gin | +| Workers Backend | Go 1.25, Python 3.x | +| Base de datos | PostgreSQL 18 | +| Caché | Redis 7 | +| Búsqueda vectorial | Qdrant | +| IA/LLM | Ollama, HuggingFace (NLLB, BERT, sentence-transformers) | +| Orquestación | Docker Compose | +| Monitoreo | Prometheus + Grafana + cAdvisor | + +### Arquitectura de Workers + +- **rss-ingestor-go**: Ingestión de feeds RSS +- **scraper**: Extracción de artículos de URLs +- **discovery**: Descubrimiento de nuevos feeds +- **topics**: Matching de temas y países +- **related**: Noticias relacionadas +- **langdetect**: Detección de idioma +- **translator**: Traducción con NLLB (CPU/GPU) +- **embeddings**: Generación de vectores semánticos +- **ner**: Extracción de entidades nombradas +- **cluster**: Agrupación de noticias +- **llm-categorizer**: Categorización con Ollama +- **wiki-worker**: Wikipedia info y thumbnails + +--- + +## Recomendaciones de Mejora + +### 1. Infraestructura y DevOps + +- **Añadir CI/CD**: Pipeline con GitHub Actions para testing y deploy automático +- **Health checks**: Implementar health checks en todos los workers (no solo en db/redis) +- **Logging centralizado**: Integrar Loki o ELK stack para agregación de logs +- **Variables de entorno**: Mover configuración hardcodeada (ej: `translator_type: cpu`) a variables与环境 + +### 2. Backend Go + +- **Estructura**: El proyecto no tiene estructura clara de packages (cmd/ internal/) +- **Testing**: No se observan tests unitarios en el backend +- **Config**: Usar Viper o library dedicada para configuración +- **Graceful shutdown**: Implementar en todos los workers +- **Migraciones**: Usar herramientas como golang-migrate en lugar de crear tablas en initDB() + +### 3. Frontend + +- **E2E Testing**: Añadir Playwright o Cypress +- **State Management**: Considerar Zustand o Jotai para estado global +- **Componentes**: Crear library de componentes reutilizables +- **i18n**: Preparar para multiidioma +- **PWA**: Añadir Service Worker para offline + +### 4. Base de Datos + +- **ORM/Query Builder**: Considerar GORM o sqlc para mejor mantenimiento +- **Índices**: Verificar que todos los campos de búsqueda tengan índices +- **Migrations**: Sistema formal de migraciones +- **Connection Pooling**: Configurar apropiadamente + +### 5. Workers y Procesamiento + +- **Colas**: Implementar RabbitMQ o Redis Streams para jobs asíncronos +- **Retry Logic**: Sistema robusto de reintentos con backoff exponencial +- **Dead Letter Queue**: Manejo de trabajos fallidos +- **Métricas por worker**: Prometheus metrics específicas por worker +- **Rate Limiting**: Protecciones contra rate limits de APIs externas + +### 6. Seguridad + +- **Rate limiting API**: Implementar en endpoints sensibles +- **Input Validation**: Usar biblioteca como go-playground/validator +- **CSRF Protection**: Añadir tokens CSRF +- **Security Headers**: Implementar todos los headers de seguridad +- **Audit Logs**: Logging de acciones administrativas + +### 7. Rendimiento + +- **Caching**: Cachear más endpoints con Redis (ej: estadísticas) +- **Pagination**: Implementar cursor-based pagination para grandes datasets +- **Database Connection Pooling**: Configurar límites apropiados +- **Image Optimization**: Comprimir imágenes antes de servir + +### 8. Documentación + +- **API Docs**: OpenAPI/Swagger para la REST API +- **Arquitecture Decision Records (ADRs)**: Documentar decisiones técnicas +- **Runbooks**: Documentación de operaciones + +### 9. Monitorización y Alertas + +- **Alertas**: Configurar alertas en Grafana para workers caídos +- **Dashboards**: Dashboard específico por worker +- **Tracing**: Añadir OpenTelemetry para distributed tracing + +### 10. User Experience + +- **Dark Mode**: Soporte completo +- **Keyboard Shortcuts**: Mejorar navegación +- **Infinite Scroll**: Para feeds grandes +- **Real-time Updates**: WebSockets para nuevas noticias diff --git a/docker-compose.yml b/docker-compose.yml index 44fe777..f022eed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,7 +78,7 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} RSS_MAX_WORKERS: 100 - RSS_POKE_INTERVAL_MIN: 15 + RSS_POKE_INTERVAL_MIN: 60 TZ: Europe/Madrid networks: - backend diff --git a/migrations/reset_feeds.sql b/migrations/reset_feeds.sql new file mode 100644 index 0000000..8e3691b --- /dev/null +++ b/migrations/reset_feeds.sql @@ -0,0 +1,4 @@ +-- Migration: Reset all feeds to inactive with 0 attempts +-- Run this to set all feeds as inactive (activo = false) and reset fallbacks to 0 + +UPDATE feeds SET activo = false, fallos = 0; From b8e48f31996f95f48f9e3be470b58c7817684de7 Mon Sep 17 00:00:00 2001 From: lordpietre Date: Wed, 1 Apr 2026 00:43:05 +0000 Subject: [PATCH 11/21] revision --- migrations/create_video_parrillas.sql | 90 --------------------------- migrations/reset_feeds.sql | 4 -- 2 files changed, 94 deletions(-) delete mode 100644 migrations/create_video_parrillas.sql delete mode 100644 migrations/reset_feeds.sql diff --git a/migrations/create_video_parrillas.sql b/migrations/create_video_parrillas.sql deleted file mode 100644 index 9ca0fbe..0000000 --- a/migrations/create_video_parrillas.sql +++ /dev/null @@ -1,90 +0,0 @@ --- Script SQL para crear tablas de parrillas de noticias para videos - --- Tabla principal de parrillas/programaciones -CREATE TABLE IF NOT EXISTS video_parrillas ( - id SERIAL PRIMARY KEY, - nombre VARCHAR(255) NOT NULL UNIQUE, - descripcion TEXT, - tipo_filtro VARCHAR(50) NOT NULL, -- 'pais', 'categoria', 'entidad', 'continente', 'custom' - - -- Filtros - pais_id INTEGER REFERENCES paises(id), - categoria_id INTEGER REFERENCES categorias(id), - continente_id INTEGER REFERENCES continentes(id), - entidad_nombre VARCHAR(255), -- Para filtrar por persona/organización específica - entidad_tipo VARCHAR(50), -- 'persona', 'organizacion' - - -- Configuración de generación - max_noticias INTEGER DEFAULT 5, -- Número máximo de noticias por video - duracion_maxima INTEGER DEFAULT 180, -- Duración máxima en segundos - idioma_voz VARCHAR(10) DEFAULT 'es', -- Idioma del TTS - voz_modelo VARCHAR(100), -- Modelo de voz específico a usar - - -- Configuración de diseño - template VARCHAR(50) DEFAULT 'standard', -- 'standard', 'modern', 'minimal' - include_images BOOLEAN DEFAULT true, - include_subtitles BOOLEAN DEFAULT true, - - -- Programación - frecuencia VARCHAR(20), -- 'daily', 'weekly', 'manual' - ultima_generacion TIMESTAMP, - proxima_generacion TIMESTAMP, - - -- Estado - activo BOOLEAN DEFAULT true, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - --- Tabla de videos generados -CREATE TABLE IF NOT EXISTS video_generados ( - id SERIAL PRIMARY KEY, - parrilla_id INTEGER REFERENCES video_parrillas(id) ON DELETE CASCADE, - titulo VARCHAR(500) NOT NULL, - descripcion TEXT, - fecha_generacion TIMESTAMP DEFAULT NOW(), - - -- Archivos - video_path VARCHAR(500), - audio_path VARCHAR(500), - subtitles_path VARCHAR(500), - thumbnail_path VARCHAR(500), - - -- Metadata - duracion INTEGER, -- en segundos - num_noticias INTEGER, - noticias_ids TEXT[], -- Array de IDs de noticias incluidas - - -- Estado de procesamiento - status VARCHAR(20) DEFAULT 'pending', -- 'pending', 'processing', 'completed', 'error' - error_message TEXT, - - -- Estadísticas - views INTEGER DEFAULT 0, - created_at TIMESTAMP DEFAULT NOW() -); - --- Tabla de noticias en videos (relación muchos a muchos) -CREATE TABLE IF NOT EXISTS video_noticias ( - id SERIAL PRIMARY KEY, - video_id INTEGER REFERENCES video_generados(id) ON DELETE CASCADE, - noticia_id VARCHAR(100) NOT NULL, - traduccion_id INTEGER REFERENCES traducciones(id), - orden INTEGER NOT NULL, -- Orden de aparición en el video - timestamp_inicio FLOAT, -- Segundo donde comienza esta noticia - timestamp_fin FLOAT, -- Segundo donde termina esta noticia - created_at TIMESTAMP DEFAULT NOW() -); - --- Índices para mejorar performance -CREATE INDEX IF NOT EXISTS idx_parrillas_tipo ON video_parrillas(tipo_filtro); -CREATE INDEX IF NOT EXISTS idx_parrillas_activo ON video_parrillas(activo); -CREATE INDEX IF NOT EXISTS idx_parrillas_proxima ON video_parrillas(proxima_generacion); -CREATE INDEX IF NOT EXISTS idx_videos_parrilla ON video_generados(parrilla_id); -CREATE INDEX IF NOT EXISTS idx_videos_status ON video_generados(status); -CREATE INDEX IF NOT EXISTS idx_videos_fecha ON video_generados(fecha_generacion DESC); - --- Comentarios para documentación -COMMENT ON TABLE video_parrillas IS 'Configuraciones de parrillas de noticias para generar videos automáticos'; -COMMENT ON TABLE video_generados IS 'Videos generados a partir de parrillas de noticias'; -COMMENT ON TABLE video_noticias IS 'Relación entre videos y las noticias que contienen'; diff --git a/migrations/reset_feeds.sql b/migrations/reset_feeds.sql deleted file mode 100644 index 8e3691b..0000000 --- a/migrations/reset_feeds.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Migration: Reset all feeds to inactive with 0 attempts --- Run this to set all feeds as inactive (activo = false) and reset fallbacks to 0 - -UPDATE feeds SET activo = false, fallos = 0; From 8093cc1e40436e859d87d73b9fd44e4b5fde36df Mon Sep 17 00:00:00 2001 From: lordpietre Date: Wed, 1 Apr 2026 03:29:09 +0000 Subject: [PATCH 12/21] feat: add remote worker system for distributed translation - Add remote translator worker (Docker) that connects via WebSocket - Add backend Go handlers for remote worker CRUD (REST + WebSocket) - Add frontend Admin panel for managing remote workers - Add ProtectedRoute component to secure admin routes - Add PostgreSQL schema for remote_workers table - Fix duplicate Load function in config.go - Fix frontend duplicate fetchStatus function - Fix Dockerfiles missing go.sum (use go mod download + tidy) Features: - Workers connect to server via WebSocket (no ports needed) - API key authentication per worker - Job assigner automatically distributes translation jobs - Worker status tracking (online/offline/disabled) - Timeout handling for stuck jobs --- Dockerfile.discovery | 6 +- Dockerfile.qdrant | 6 +- Dockerfile.related | 6 +- Dockerfile.remote-worker | 50 ++ Dockerfile.scraper | 6 +- Dockerfile.topics | 6 +- Dockerfile.wiki | 6 +- backend/Dockerfile | 3 +- backend/cmd/server/main.go | 49 +- backend/internal/config/config.go | 39 +- backend/internal/handlers/remote_worker.go | 187 ++++++ backend/internal/handlers/worker_ws.go | 297 ++++++++++ backend/internal/middleware/auth.go | 52 +- backend/internal/models/remote_worker.go | 41 ++ docker-compose.yml | 32 ++ frontend/src/App.tsx | 23 +- frontend/src/components/ProtectedRoute.tsx | 47 ++ frontend/src/pages/AdminWorkers.tsx | 212 ++++++- init-db/35-remote-workers.sql | 31 + remote-worker.md | 635 +++++++++++++++++++++ workers/remote_translator_worker.py | 389 +++++++++++++ 21 files changed, 2065 insertions(+), 58 deletions(-) create mode 100644 Dockerfile.remote-worker create mode 100644 backend/internal/handlers/remote_worker.go create mode 100644 backend/internal/handlers/worker_ws.go create mode 100644 backend/internal/models/remote_worker.go create mode 100644 frontend/src/components/ProtectedRoute.tsx create mode 100644 init-db/35-remote-workers.sql create mode 100644 remote-worker.md create mode 100644 workers/remote_translator_worker.py diff --git a/Dockerfile.discovery b/Dockerfile.discovery index 90e405d..d31dbbc 100644 --- a/Dockerfile.discovery +++ b/Dockerfile.discovery @@ -6,11 +6,15 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ +RUN go mod tidy + +COPY backend/ ./ + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/discovery ./cmd/discovery FROM alpine:3.19 diff --git a/Dockerfile.qdrant b/Dockerfile.qdrant index e80bfae..f67c02b 100644 --- a/Dockerfile.qdrant +++ b/Dockerfile.qdrant @@ -6,11 +6,15 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ +RUN go mod tidy + +COPY backend/ ./ + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/qdrant-worker ./cmd/qdrant FROM alpine:3.19 diff --git a/Dockerfile.related b/Dockerfile.related index 12e011d..a5edc77 100644 --- a/Dockerfile.related +++ b/Dockerfile.related @@ -6,11 +6,15 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ +RUN go mod tidy + +COPY backend/ ./ + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/related ./cmd/related FROM alpine:3.19 diff --git a/Dockerfile.remote-worker b/Dockerfile.remote-worker new file mode 100644 index 0000000..44fcb11 --- /dev/null +++ b/Dockerfile.remote-worker @@ -0,0 +1,50 @@ +FROM python:3.11-slim-bookworm + +SHELL ["/bin/bash", "-c"] + +RUN apt-get update && apt-get install -y --no-install-recommends \ + patchelf libpq-dev gcc git curl wget bash \ + && rm -rf /var/lib/apt/lists/* + +ENV PYTHONUNBUFFERED=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + TOKENIZERS_PARALLELISM=false \ + HF_HOME=/root/.cache/huggingface \ + DEBIAN_FRONTEND=noninteractive + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir --upgrade pip + +RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cpu + +RUN pip install --no-cache-dir \ + ctranslate2==3.24.0 \ + sentencepiece \ + transformers==4.36.0 \ + protobuf==3.20.3 \ + "numpy<2" \ + psycopg2-binary \ + langdetect \ + websocket-client + +RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ + -name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \ + xargs -I {} patchelf --clear-execstack {} || true + +COPY workers/ ./workers/ +COPY init-db/ ./init-db/ +COPY migrations/ ./migrations/ + +ENV DB_HOST=db +ENV DB_PORT=5432 +ENV DB_NAME=rss +ENV DB_USER=rss +ENV DB_PASS=x + +ENV WORKER_SERVER=ws://localhost:8080/ws/worker +ENV CT2_DEVICE=cpu +ENV CT2_COMPUTE_TYPE=int8 + +CMD ["python", "-m", "workers.remote_translator_worker"] \ No newline at end of file diff --git a/Dockerfile.scraper b/Dockerfile.scraper index 02d380e..587600d 100644 --- a/Dockerfile.scraper +++ b/Dockerfile.scraper @@ -6,13 +6,17 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ RUN go mod tidy +COPY backend/ ./ + +RUN go mod tidy + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/scraper ./cmd/scraper FROM alpine:3.19 diff --git a/Dockerfile.topics b/Dockerfile.topics index fc82ea7..1178211 100644 --- a/Dockerfile.topics +++ b/Dockerfile.topics @@ -6,11 +6,15 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ +RUN go mod tidy + +COPY backend/ ./ + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/topics ./cmd/topics FROM alpine:3.19 diff --git a/Dockerfile.wiki b/Dockerfile.wiki index fbd84e0..f59df1c 100644 --- a/Dockerfile.wiki +++ b/Dockerfile.wiki @@ -6,13 +6,17 @@ RUN apk add --no-cache git WORKDIR /app -COPY backend/go.mod backend/go.sum ./ +COPY backend/go.mod ./ RUN go mod download COPY backend/ ./ RUN go mod tidy +COPY backend/ ./ + +RUN go mod tidy + RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/wiki_worker ./cmd/wiki_worker FROM alpine:3.19 diff --git a/backend/Dockerfile b/backend/Dockerfile index 6d232b9..5f04721 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -4,10 +4,11 @@ WORKDIR /app RUN apt-get update && apt-get install -y gcc musl-dev git -COPY go.mod go.sum ./ +COPY go.mod ./ RUN go mod download COPY . . +RUN go mod tidy RUN CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o /server ./cmd/server diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index cf13d80..2f3abf7 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/auth" "github.com/rss2/backend/internal/cache" "github.com/rss2/backend/internal/config" "github.com/rss2/backend/internal/db" @@ -74,6 +75,39 @@ func initDB() { INSERT INTO config (key, value) VALUES ('translator_status', 'stopped') ON CONFLICT (key) DO NOTHING `) + + // Crear tabla de remote_workers si no existe + _, err = db.GetPool().Exec(ctx, ` + CREATE TABLE IF NOT EXISTS remote_workers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + api_key VARCHAR(64) UNIQUE NOT NULL, + capabilities VARCHAR(50) DEFAULT 'cpu', + status VARCHAR(20) DEFAULT 'offline', + last_seen TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() + ) + `) + if err != nil { + log.Printf("Warning: Could not create remote_workers table: %v", err) + } else { + log.Println("Table remote_workers ready") + } + + // Añadir columnas a traducciones para workers remotos + _, err = db.GetPool().Exec(ctx, ` + ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS worker_id INTEGER REFERENCES remote_workers(id) + `) + if err != nil { + log.Printf("Warning: Could not add worker_id column: %v", err) + } + + _, err = db.GetPool().Exec(ctx, ` + ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS assigned_at TIMESTAMP + `) + if err != nil { + log.Printf("Warning: Could not add assigned_at column: %v", err) + } } func main() { @@ -109,7 +143,7 @@ func main() { api := r.Group("/api") { // Serve static images downloaded by wiki_worker - api.StaticFS("/wiki-images", gin.Dir("/app/data/wiki_images", false)) + api.StaticFS("/wiki-images", gin.Dir(cfg.WikiImagesPath, false)) api.POST("/auth/login", handlers.Login) api.POST("/auth/register", handlers.Register) @@ -161,8 +195,17 @@ func main() { admin.POST("/workers/config", handlers.SetWorkerConfig) admin.POST("/workers/start", handlers.StartWorkers) admin.POST("/workers/stop", handlers.StopWorkers) + + admin.GET("/workers/remote", handlers.ListRemoteWorkers) + admin.POST("/workers/remote", handlers.CreateRemoteWorker) + admin.GET("/workers/remote/:id", handlers.GetRemoteWorker) + admin.DELETE("/workers/remote/:id", handlers.DeleteRemoteWorker) + admin.POST("/workers/remote/:id/toggle", handlers.ToggleRemoteWorker) + admin.POST("/workers/remote/:id/regenerate-key", handlers.RegenerateAPIKey) } + r.GET("/ws/worker", handlers.HandleWorkerWS) + auth := api.Group("/auth") auth.Use(middleware.AuthRequired()) { @@ -170,7 +213,7 @@ func main() { } } - middleware.SetJWTSecret(cfg.SecretKey) + auth.SetJWTSecret(cfg.SecretKey) port := cfg.ServerPort addr := fmt.Sprintf(":%s", port) @@ -182,6 +225,8 @@ func main() { } }() + handlers.StartJobAssigner() + quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 269ae7b..f218100 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -20,23 +20,34 @@ type Config struct { DefaultLang string NewsPerPage int RateLimitPerMinute int + DockerComposeDir string + WikiImagesPath string + AllowedOrigins string } func Load() *Config { + secretKey := os.Getenv("SECRET_KEY") + if secretKey == "" { + secretKey = "change-this-secret-key" + } + return &Config{ - ServerPort: getEnv("SERVER_PORT", "8080"), - DatabaseURL: getEnv("DATABASE_URL", "postgres://rss:rss@localhost:5432/rss"), - RedisURL: getEnv("REDIS_URL", "redis://localhost:6379"), - QdrantHost: getEnv("QDRANT_HOST", "localhost"), - QdrantPort: getEnvInt("QDRANT_PORT", 6333), - SecretKey: getEnv("SECRET_KEY", "change-this-secret-key"), - JWTExpiration: getEnvDuration("JWT_EXPIRATION", 24*time.Hour), - TranslationURL: getEnv("TRANSLATION_URL", "http://libretranslate:7790"), - OllamaURL: getEnv("OLLAMA_URL", "http://ollama:11434"), - SpacyURL: getEnv("SPACY_URL", "http://spacy:8000"), - DefaultLang: getEnv("DEFAULT_LANG", "es"), - NewsPerPage: getEnvInt("NEWS_PER_PAGE", 30), - RateLimitPerMinute: getEnvInt("RATE_LIMIT_PER_MINUTE", 60), + ServerPort: getEnv("SERVER_PORT", "8080"), + DatabaseURL: getEnv("DATABASE_URL", "postgres://rss:rss@localhost:5432/rss"), + RedisURL: getEnv("REDIS_URL", "redis://localhost:6379"), + QdrantHost: getEnv("QDRANT_HOST", "localhost"), + QdrantPort: getEnvInt("QDRANT_PORT", 6333), + SecretKey: secretKey, + JWTExpiration: getEnvDuration("JWT_EXPIRATION", 24*time.Hour), + TranslationURL: getEnv("TRANSLATION_URL", "http://libretranslate:7790"), + OllamaURL: getEnv("OLLAMA_URL", "http://ollama:11434"), + SpacyURL: getEnv("SPACY_URL", "http://spacy:8000"), + DefaultLang: getEnv("DEFAULT_LANG", "es"), + NewsPerPage: getEnvInt("NEWS_PER_PAGE", 30), + RateLimitPerMinute: getEnvInt("RATE_LIMIT_PER_MINUTE", 60), + DockerComposeDir: getEnv("DOCKER_COMPOSE_DIR", "/datos/rss2"), + WikiImagesPath: getEnv("WIKI_IMAGES_PATH", "/app/data/wiki_images"), + AllowedOrigins: getEnv("ALLOWED_ORIGINS", "*"), } } @@ -63,4 +74,4 @@ func getEnvDuration(key string, defaultValue time.Duration) time.Duration { } } return defaultValue -} +} \ No newline at end of file diff --git a/backend/internal/handlers/remote_worker.go b/backend/internal/handlers/remote_worker.go new file mode 100644 index 0000000..6e37d17 --- /dev/null +++ b/backend/internal/handlers/remote_worker.go @@ -0,0 +1,187 @@ +package handlers + +import ( + "crypto/rand" + "encoding/hex" + "log" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/db" + "github.com/rss2/backend/internal/models" +) + +func CreateRemoteWorker(c *gin.Context) { + var req struct { + Name string `json:"name" binding:"required"` + Capabilities string `json:"capabilities"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request", "message": err.Error()}) + return + } + + if req.Capabilities == "" { + req.Capabilities = "cpu" + } + + apiKey := generateAPIKey() + + ctx := c.Request.Context() + var workerID int + err := db.GetPool().QueryRow(ctx, ` + INSERT INTO remote_workers (name, api_key, capabilities, status, created_at) + VALUES ($1, $2, $3, 'offline', NOW()) + RETURNING id + `, req.Name, apiKey, req.Capabilities).Scan(&workerID) + + if err != nil { + log.Printf("Error creating remote worker: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create worker"}) + return + } + + c.JSON(http.StatusCreated, gin.H{ + "id": workerID, + "name": req.Name, + "api_key": apiKey, + "capabilities": req.Capabilities, + "status": "offline", + }) +} + +func ListRemoteWorkers(c *gin.Context) { + ctx := c.Request.Context() + + rows, err := db.GetPool().Query(ctx, ` + SELECT id, name, api_key, capabilities, status, last_seen, created_at + FROM remote_workers + ORDER BY created_at DESC + `) + if err != nil { + log.Printf("Error listing remote workers: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list workers"}) + return + } + defer rows.Close() + + var workers []models.RemoteWorker + for rows.Next() { + var w models.RemoteWorker + if err := rows.Scan(&w.ID, &w.Name, &w.APIKey, &w.Capabilities, &w.Status, &w.LastSeen, &w.CreatedAt); err != nil { + log.Printf("Error scanning worker: %v", err) + continue + } + workers = append(workers, w) + } + + if workers == nil { + workers = []models.RemoteWorker{} + } + + c.JSON(http.StatusOK, workers) +} + +func GetRemoteWorker(c *gin.Context) { + id := c.Param("id") + ctx := c.Request.Context() + + var w models.RemoteWorker + err := db.GetPool().QueryRow(ctx, ` + SELECT id, name, api_key, capabilities, status, last_seen, created_at + FROM remote_workers + WHERE id = $1 + `, id).Scan(&w.ID, &w.Name, &w.APIKey, &w.Capabilities, &w.Status, &w.LastSeen, &w.CreatedAt) + + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Worker not found"}) + return + } + + var stats struct { + JobsCompleted int `json:"jobs_completed"` + JobsPending int `json:"jobs_pending"` + } + + db.GetPool().QueryRow(ctx, ` + SELECT COUNT(*) FROM traducciones WHERE worker_id = $1 AND status = 'done' + `, id).Scan(&stats.JobsCompleted) + + db.GetPool().QueryRow(ctx, ` + SELECT COUNT(*) FROM traducciones WHERE worker_id = $1 AND status = 'pending' + `, id).Scan(&stats.JobsPending) + + c.JSON(http.StatusOK, gin.H{ + "worker": w, + "stats": stats, + }) +} + +func DeleteRemoteWorker(c *gin.Context) { + id := c.Param("id") + ctx := c.Request.Context() + + result, err := db.GetPool().Exec(ctx, ` + DELETE FROM remote_workers WHERE id = $1 + `, id) + + if err != nil { + log.Printf("Error deleting remote worker: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete worker"}) + return + } + + if result.RowsAffected() == 0 { + c.JSON(http.StatusNotFound, gin.H{"error": "Worker not found"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Worker deleted"}) +} + +func ToggleRemoteWorker(c *gin.Context) { + id := c.Param("id") + ctx := c.Request.Context() + + var currentStatus string + err := db.GetPool().QueryRow(ctx, `SELECT status FROM remote_workers WHERE id = $1`, id).Scan(¤tStatus) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Worker not found"}) + return + } + + newStatus := "disabled" + if currentStatus == "disabled" { + newStatus = "offline" + } + + _, err = db.GetPool().Exec(ctx, `UPDATE remote_workers SET status = $1 WHERE id = $2`, newStatus, id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update worker status"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": newStatus}) +} + +func RegenerateAPIKey(c *gin.Context) { + id := c.Param("id") + ctx := c.Request.Context() + + newAPIKey := generateAPIKey() + + _, err := db.GetPool().Exec(ctx, `UPDATE remote_workers SET api_key = $1 WHERE id = $2`, newAPIKey, id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to regenerate API key"}) + return + } + + c.JSON(http.StatusOK, gin.H{"api_key": newAPIKey}) +} + +func generateAPIKey() string { + bytes := make([]byte, 32) + rand.Read(bytes) + return hex.EncodeToString(bytes) +} \ No newline at end of file diff --git a/backend/internal/handlers/worker_ws.go b/backend/internal/handlers/worker_ws.go new file mode 100644 index 0000000..15eb698 --- /dev/null +++ b/backend/internal/handlers/worker_ws.go @@ -0,0 +1,297 @@ +package handlers + +import ( + "context" + "encoding/json" + "log" + "net/http" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/rss2/backend/internal/db" + "github.com/rss2/backend/internal/models" +) + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { return true }, +} + +type WSWorker struct { + conn *websocket.Conn + workerID int + capabilities string + workerName string + lastHeartbeat time.Time + mu sync.Mutex +} + +var ( + workers = make(map[int]*WSWorker) + workersByAPI = make(map[string]*WSWorker) + workersMu sync.RWMutex +) + +func HandleWorkerWS(c *gin.Context) { + apiKey := c.Query("api_key") + if apiKey == "" { + apiKey = c.GetHeader("X-API-Key") + } + + if apiKey == "" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "API key required"}) + return + } + + ctx := c.Request.Context() + + var workerID int + var capabilities string + err := db.GetPool().QueryRow(ctx, ` + SELECT id, capabilities FROM remote_workers + WHERE api_key = $1 AND status != 'disabled' + `, apiKey).Scan(&workerID, &capabilities) + + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"}) + return + } + + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Printf("WebSocket upgrade error: %v", err) + return + } + + wsWorker := &WSWorker{ + conn: conn, + workerID: workerID, + capabilities: capabilities, + lastHeartbeat: time.Now(), + } + + workersMu.Lock() + workers[workerID] = wsWorker + workersByAPI[apiKey] = wsWorker + workersMu.Unlock() + + db.GetPool().Exec(ctx, ` + UPDATE remote_workers SET status = 'online', last_seen = NOW() WHERE id = $1 + `, workerID) + + go heartbeatLoop(wsWorker) + go writeLoop(wsWorker) + readLoop(wsWorker, apiKey) +} + +func readLoop(wsWorker *WSWorker, apiKey string) { + defer func() { + cleanupWorker(wsWorker, apiKey) + }() + + for { + var msg models.WSClientMessage + err := wsWorker.conn.ReadJSON(&msg) + if err != nil { + log.Printf("Worker %d read error: %v", wsWorker.workerID, err) + break + } + + switch msg.Type { + case "register": + wsWorker.mu.Lock() + if msg.WorkerName != "" { + wsWorker.workerName = msg.WorkerName + } + if msg.Capabilities != "" { + wsWorker.capabilities = msg.Capabilities + } + wsWorker.mu.Unlock() + + sendWS(wsWorker, models.WSServerMessage{Type: "ack", Job: nil}) + + case "heartbeat": + wsWorker.mu.Lock() + wsWorker.lastHeartbeat = time.Now() + wsWorker.mu.Unlock() + sendWS(wsWorker, models.WSServerMessage{Type: "ack", Job: nil}) + + case "result": + if msg.Result != nil { + handleTranslationResult(wsWorker.workerID, msg.Result) + } + } + } +} + +func writeLoop(wsWorker *WSWorker) { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := wsWorker.conn.WriteMessage(websocket.PingMessage, nil); err != nil { + return + } + } + } +} + +func heartbeatLoop(wsWorker *WSWorker) { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + wsWorker.mu.Lock() + elapsed := time.Since(wsWorker.lastHeartbeat) + wsWorker.mu.Unlock() + + if elapsed > 60*time.Second { + log.Printf("Worker %d heartbeat timeout", wsWorker.workerID) + wsWorker.conn.Close() + return + } + + sendWS(wsWorker, models.WSServerMessage{Type: "ping", Job: nil}) + } + } +} + +func sendWS(wsWorker *WSWorker, msg models.WSServerMessage) { + data, _ := json.Marshal(msg) + wsWorker.conn.WriteMessage(websocket.TextMessage, data) +} + +func cleanupWorker(wsWorker *WSWorker, apiKey string) { + workersMu.Lock() + delete(workers, wsWorker.workerID) + delete(workersByAPI, apiKey) + workersMu.Unlock() + + ctx := context.Background() + db.GetPool().Exec(ctx, ` + UPDATE remote_workers SET status = 'offline', last_seen = NOW() WHERE id = $1 + `, wsWorker.workerID) + + wsWorker.conn.Close() + + db.GetPool().Exec(ctx, ` + UPDATE traducciones + SET status = 'pending', worker_id = NULL, assigned_at = NULL + WHERE worker_id = $1 AND status = 'assigned' + `, wsWorker.workerID) + + log.Printf("Worker %d disconnected", wsWorker.workerID) +} + +func handleTranslationResult(workerID int, result *models.TranslationResult) { + ctx := context.Background() + + if result.Error != "" { + db.GetPool().Exec(ctx, ` + UPDATE traducciones + SET status = 'error', worker_id = NULL, assigned_at = NULL + WHERE id = $1 + `, result.JobID) + log.Printf("Job %d failed: %s", result.JobID, result.Error) + return + } + + _, err := db.GetPool().Exec(ctx, ` + UPDATE traducciones + SET titulo_trad = $1, resumen_trad = $2, status = 'done', + worker_id = $3, assigned_at = NULL + WHERE id = $4 + `, result.TitleTr, result.SummaryTr, workerID, result.JobID) + + if err != nil { + log.Printf("Error updating translation result: %v", err) + } + + db.GetPool().Exec(ctx, ` + UPDATE remote_workers SET last_seen = NOW() WHERE id = $1 + `, workerID) + + log.Printf("Job %d completed by worker %d", result.JobID, workerID) +} + +func AssignJobToWorker(workerID int) *models.TranslationJob { + ctx := context.Background() + + workersMu.RLock() + wsWorker, exists := workers[workerID] + workersMu.RUnlock() + + if !exists { + return nil + } + + wsWorker.mu.Lock() + workerCapabilities := wsWorker.capabilities + wsWorker.mu.Unlock() + + _ = workerCapabilities + + var job models.TranslationJob + err := db.GetPool().QueryRow(ctx, ` + SELECT t.id, t.noticia_id, t.lang_from, t.lang_to, n.titulo, n.resumen + FROM traducciones t + JOIN noticias n ON n.id = t.noticia_id + WHERE t.status = 'pending' + AND (t.worker_id IS NULL OR t.status = 'assigned' AND t.assigned_at < NOW() - INTERVAL '5 minutes') + AND t.lang_to = 'es' + AND (t.titulo_trad IS NULL OR t.resumen_trad IS NULL) + ORDER BY n.fecha DESC + LIMIT 1 + FOR UPDATE SKIP LOCKED + `).Scan(&job.ID, &job.NewsID, &job.LangFrom, &job.LangTo, &job.Title, &job.Summary) + + if err != nil { + return nil + } + + db.GetPool().Exec(ctx, ` + UPDATE traducciones + SET status = 'assigned', worker_id = $1, assigned_at = NOW() + WHERE id = $2 + `, workerID, job.ID) + + sendWS(wsWorker, models.WSServerMessage{ + Type: "job", + Job: &job, + }) + + return &job +} + +func StartJobAssigner() { + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + workersMu.RLock() + for workerID := range workers { + workersMu.RUnlock() + AssignJobToWorker(workerID) + workersMu.RLock() + } + workersMu.RUnlock() + + ctx := context.Background() + db.GetPool().Exec(ctx, ` + UPDATE traducciones + SET status = 'pending', worker_id = NULL, assigned_at = NULL + WHERE status = 'assigned' + AND assigned_at < NOW() - INTERVAL '10 minutes' + `) + } + } + }() +} \ No newline at end of file diff --git a/backend/internal/middleware/auth.go b/backend/internal/middleware/auth.go index 57df7c7..4c35c35 100644 --- a/backend/internal/middleware/auth.go +++ b/backend/internal/middleware/auth.go @@ -5,23 +5,10 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v5" + "github.com/rss2/backend/internal/auth" + "github.com/rss2/backend/internal/config" ) -var jwtSecret []byte - -func SetJWTSecret(secret string) { - jwtSecret = []byte(secret) -} - -type Claims struct { - UserID int64 `json:"user_id"` - Email string `json:"email"` - Username string `json:"username"` - IsAdmin bool `json:"is_admin"` - jwt.RegisteredClaims -} - func AuthRequired() gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") @@ -38,12 +25,8 @@ func AuthRequired() gin.HandlerFunc { return } - claims := &Claims{} - token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { - return jwtSecret, nil - }) - - if err != nil || !token.Valid { + claims, err := auth.ValidateToken(tokenString) + if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) c.Abort() return @@ -63,7 +46,7 @@ func AdminRequired() gin.HandlerFunc { return } - claims := userVal.(*Claims) + claims := userVal.(*auth.Claims) if !claims.IsAdmin { c.JSON(http.StatusForbidden, gin.H{"error": "Admin access required"}) c.Abort() @@ -75,9 +58,30 @@ func AdminRequired() gin.HandlerFunc { } func CORSMiddleware() gin.HandlerFunc { + cfg := config.Load() + allowedOrigins := strings.Split(cfg.AllowedOrigins, ",") + return func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + origin := c.Request.Header.Get("Origin") + + allowed := false + for _, o := range allowedOrigins { + o = strings.TrimSpace(o) + if o == "*" || o == origin { + allowed = true + break + } + } + + if allowed { + if cfg.AllowedOrigins == "*" { + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + } else { + c.Writer.Header().Set("Access-Control-Allow-Origin", origin) + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + } + } + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH") diff --git a/backend/internal/models/remote_worker.go b/backend/internal/models/remote_worker.go new file mode 100644 index 0000000..ce77213 --- /dev/null +++ b/backend/internal/models/remote_worker.go @@ -0,0 +1,41 @@ +package models + +import "time" + +type RemoteWorker struct { + ID int `json:"id"` + Name string `json:"name"` + APIKey string `json:"api_key,omitempty"` + Capabilities string `json:"capabilities"` + Status string `json:"status"` + LastSeen *time.Time `json:"last_seen,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +type TranslationJob struct { + ID int64 `json:"id"` + NewsID int64 `json:"noticia_id"` + LangFrom string `json:"lang_from"` + LangTo string `json:"lang_to"` + Title string `json:"title"` + Summary string `json:"summary"` +} + +type TranslationResult struct { + JobID int64 `json:"job_id"` + TitleTr string `json:"title_trad"` + SummaryTr string `json:"resumen_trad"` + Error string `json:"error,omitempty"` +} + +type WSClientMessage struct { + Type string `json:"type"` + Capabilities string `json:"capabilities,omitempty"` + WorkerName string `json:"worker_name,omitempty"` + Result *TranslationResult `json:"result,omitempty"` +} + +type WSServerMessage struct { + Type string `json:"type"` + Job *TranslationJob `json:"job,omitempty"` +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f022eed..e90b51b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -312,6 +312,38 @@ services: condition: service_healthy restart: unless-stopped + # ================================================================================== + # REMOTE TRANSLATOR WORKER - Worker remoto que se conecta por WebSocket + # ================================================================================== + remote-translator: + build: + context: . + dockerfile: Dockerfile.remote-worker + container_name: rss2_remote_translator + environment: + WORKER_NAME: ${WORKER_NAME:-remote-worker-1} + WORKER_API_KEY: ${WORKER_API_KEY:-} + WORKER_SERVER: ${WORKER_SERVER:-ws://backend-go:8080/ws/worker} + CT2_DEVICE: ${CT2_DEVICE:-cpu} + CT2_MODEL_PATH: /app/models/nllb-ct2 + CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-int8} + UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + volumes: + - ./hf_cache:/app/hf_cache + - ./models:/app/models + networks: + - backend + profiles: + - remote + deploy: + resources: + limits: + cpus: '1' + memory: 2G + restart: unless-stopped + # ================================================================================== # TRANSLATION SCHEDULER - Creates translation jobs # ================================================================================== diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 38af056..3f3c3d3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react' import { Routes, Route } from 'react-router-dom' import { Layout } from './components/layout/Layout' +import { ProtectedRoute } from './components/ProtectedRoute' import { Home } from './pages/Home' import { News } from './pages/News' import { Feeds } from './pages/Feeds' @@ -64,13 +65,25 @@ function App() { } /> } /> } /> - } /> - } /> - } /> - } /> + {}} + /> + {}} + /> + {}} + /> + {}} + /> ) } -export default App +export default App \ No newline at end of file diff --git a/frontend/src/components/ProtectedRoute.tsx b/frontend/src/components/ProtectedRoute.tsx new file mode 100644 index 0000000..5bca3f1 --- /dev/null +++ b/frontend/src/components/ProtectedRoute.tsx @@ -0,0 +1,47 @@ +import { Navigate } from 'react-router-dom' +import { useState, useEffect } from 'react' + +interface ProtectedRouteProps { + children: React.ReactNode + requireAdmin?: boolean +} + +export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRouteProps) { + const [isAuthenticated, setIsAuthenticated] = useState(null) + const [isAdmin, setIsAdmin] = useState(false) + + useEffect(() => { + const token = localStorage.getItem('token') + const userData = localStorage.getItem('user') + + if (token && userData) { + try { + const user = JSON.parse(userData) + setIsAuthenticated(true) + setIsAdmin(user.is_admin === true) + } catch { + setIsAuthenticated(false) + } + } else { + setIsAuthenticated(false) + } + }, []) + + if (isAuthenticated === null) { + return ( +
+
+
+ ) + } + + if (!isAuthenticated) { + return + } + + if (requireAdmin && !isAdmin) { + return + } + + return <>{children} +} \ No newline at end of file diff --git a/frontend/src/pages/AdminWorkers.tsx b/frontend/src/pages/AdminWorkers.tsx index 16dc347..cd5e0f2 100644 --- a/frontend/src/pages/AdminWorkers.tsx +++ b/frontend/src/pages/AdminWorkers.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { api } from '../services/api' -import { Cpu, Play, Square, Settings, RefreshCw, Loader2, Server } from 'lucide-react' +import { Cpu, Play, Square, Settings, RefreshCw, Loader2, Server, Wifi, Plus, Trash2, Key } from 'lucide-react' interface WorkerStatus { type: string @@ -9,6 +9,16 @@ interface WorkerStatus { running: number } +interface RemoteWorker { + id: number + name: string + api_key?: string + capabilities: string + status: string + last_seen?: string + created_at: string +} + export function AdminWorkers() { const [status, setStatus] = useState(null) const [loading, setLoading] = useState(true) @@ -16,24 +26,50 @@ export function AdminWorkers() { const [configType, setConfigType] = useState<'cpu' | 'gpu'>('cpu') const [configWorkers, setConfigWorkers] = useState(2) - useEffect(() => { - fetchStatus() - }, []) + const [remoteWorkers, setRemoteWorkers] = useState([]) + const [showAddForm, setShowAddForm] = useState(false) + const [newWorkerName, setNewWorkerName] = useState('') + const [newWorkerCapabilities, setNewWorkerCapabilities] = useState('cpu') + const [newWorkerKey, setNewWorkerKey] = useState(null) const fetchStatus = async () => { setLoading(true) try { const res = await api.get('/admin/workers/status') + console.log('Status response:', res.data) setStatus(res.data) setConfigType(res.data.type) setConfigWorkers(res.data.workers) - } catch (err) { - console.error(err) + } catch (err: any) { + console.error('Error fetching status:', err) } finally { setLoading(false) } } + const fetchRemoteWorkers = async () => { + try { + const res = await api.get('/admin/workers/remote') + console.log('Remote workers response:', res.data) + setRemoteWorkers(res.data) + } catch (err: any) { + console.error('Error fetching remote workers:', err) + // If 401, user is not authenticated - try refreshing + if (err.response?.status === 401) { + const userData = localStorage.getItem('user') + if (userData) { + const user = JSON.parse(userData) + console.log('Current user from localStorage:', user) + } + } + } + } + + useEffect(() => { + fetchStatus() + fetchRemoteWorkers() + }, []) + const handleStart = async () => { setActionLoading('start') try { @@ -76,6 +112,53 @@ export function AdminWorkers() { } } + const handleAddRemoteWorker = async () => { + if (!newWorkerName.trim()) return + setActionLoading('add') + try { + const res = await api.post('/admin/workers/remote', { + name: newWorkerName, + capabilities: newWorkerCapabilities + }) + setNewWorkerKey(res.data.api_key) + setNewWorkerName('') + fetchRemoteWorkers() + } catch (err: any) { + alert(err.response?.data?.error || 'Error al crear worker') + } finally { + setActionLoading(null) + } + } + + const handleDeleteRemoteWorker = async (id: number) => { + if (!confirm('¿Eliminar este worker remoto?')) return + try { + await api.delete(`/admin/workers/remote/${id}`) + fetchRemoteWorkers() + } catch (err: any) { + alert(err.response?.data?.error || 'Error al eliminar worker') + } + } + + const handleToggleRemoteWorker = async (id: number) => { + try { + await api.post(`/admin/workers/remote/${id}/toggle`) + fetchRemoteWorkers() + } catch (err: any) { + alert(err.response?.data?.error || 'Error al cambiar estado') + } + } + + const handleRegenerateKey = async (id: number) => { + if (!confirm('¿Regenerar API key? La anterior deixará de funcionar.')) return + try { + const res = await api.post(`/admin/workers/remote/${id}/regenerate-key`) + setNewWorkerKey(res.data.api_key) + } catch (err: any) { + alert(err.response?.data?.error || 'Error al regenerar key') + } + } + if (loading) { return (
@@ -256,6 +339,123 @@ export function AdminWorkers() {
  • • Recomendado: 2-4 workers para CPU, 1-2 para GPU
  • + +
    +
    +

    + + Workers Remotos +

    + +
    + + {showAddForm && ( +
    +

    Nuevo Worker Remoto

    +
    + setNewWorkerName(e.target.value)} + className="flex-1 px-3 py-2 border rounded-lg dark:bg-gray-600 dark:border-gray-600" + /> + +
    +
    + + +
    +
    + )} + + {newWorkerKey && ( +
    +
    + + API Key creada +
    + + {newWorkerKey} + +

    + Copia esta clave. No se mostrará de nuevo. +

    + +
    + )} + + {remoteWorkers.length === 0 ? ( +

    No hay workers remotos configurados

    + ) : ( +
    + {remoteWorkers.map((worker) => ( +
    +
    +
    +
    + {worker.name} + {worker.capabilities} +
    +
    +
    + + + +
    +
    + ))} +
    + )} +
    ) } diff --git a/init-db/35-remote-workers.sql b/init-db/35-remote-workers.sql new file mode 100644 index 0000000..625be13 --- /dev/null +++ b/init-db/35-remote-workers.sql @@ -0,0 +1,31 @@ +-- Tabla de workers remotos +CREATE TABLE IF NOT EXISTS remote_workers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + api_key VARCHAR(64) UNIQUE NOT NULL, + capabilities VARCHAR(50) DEFAULT 'cpu', + status VARCHAR(20) DEFAULT 'offline', + last_seen TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Añadir columnas a traducciones para workers remotos +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS worker_id INTEGER REFERENCES remote_workers(id); +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS assigned_at TIMESTAMP; + +-- Índices para workers remotos +CREATE INDEX IF NOT EXISTS idx_remote_workers_status ON remote_workers(status); +CREATE INDEX IF NOT EXISTS idx_remote_workers_api_key ON remote_workers(api_key); + +-- Índice para buscar jobs disponibles por capabilities +CREATE INDEX IF NOT EXISTS idx_traducciones_pending_worker + ON traducciones(lang_to, status, worker_id) + WHERE status = 'pending' AND worker_id IS NULL; + +-- Índice para jobs asignados (para cleanup de timeout) +CREATE INDEX IF NOT EXISTS idx_traducciones_assigned_timeout + ON traducciones(assigned_at) + WHERE status = 'assigned' AND assigned_at IS NOT NULL; + +-- Tabla de workers remotos conectados (en memoria = no persistir, pero referenciar) +-- El status se actualiza desde el WebSocket handler \ No newline at end of file diff --git a/remote-worker.md b/remote-worker.md new file mode 100644 index 0000000..b39cfc0 --- /dev/null +++ b/remote-worker.md @@ -0,0 +1,635 @@ +# Plan: Sistema de Workers Remotos para Traducción + +## Visión General + +Sistema que permite conectar workers de traducción desde equipos externos (con GPU) al servidor central mediante WebSockets, eliminando la necesidad de exponer el servidor a internet y permitiendo procesamiento distribuido. + +--- + +## Arquitectura del Sistema + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ SERVidor VPS │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ API REST │ │ WebSocket │ │ Scheduler │ │ PostgreSQL │ │ +│ │ (Go) │◄─┤ Server │ │ (Go) │ │ (Cola Jobs) │ │ +│ │ │ │ (Go) │ │ │ │ │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────┘ │ +│ │ │ │ │ +│ └────────────────┼────────────────┘ │ +│ │ │ +│ PUERTO 80/443 (ya expuesto) │ +└─────────────────────────────────────────────────────────────────────────────┘ + ▲ + │ WebSocket (conexión saliente) + │ API Key autenticación + │ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ EQUIPO LOCAL CON GPU │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ Worker Client (Go) │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ +│ │ │ WS Client │ │ Lógica │ │ CTranslate2 (subproc) │ │ │ +│ │ │ (Go) │ │ (Go) │ │ + Modelo NLLB-1.3B │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +│ Conexión saliente (no requiere puertos abiertos) │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Flujo de Datos + +1. **Scheduler (servidor)** crea jobs en `traducciones` con status `pending` +2. **Worker remoto** se conecta por WS, envía capabilities (`gpu`/`cpu`) +3. **Servidor** asigna job disponible que coincida con capabilities → status `assigned` +4. **Worker** recibe job → traduce con NLLB-1.3B → envía resultado por WS +5. **Servidor** actualiza `traducciones` con resultado → status `completed` + +--- + +## Análisis de la Aplicación Actual + +### Componentes Relevantes Identificados + +| Componente | Ubicación | Función | +|------------|-----------|---------| +| Handlers admin | `backend/internal/handlers/admin.go` | API workers locales (Docker) | +| Modelos | `backend/internal/models/models.go` | Estructuras de datos | +| Tabla traducciones | `init-db/00-complete-schema.sql` | Cola de traducciones | +| Worker Python | `workers/ctranslator_worker.py` | Traducción actual | +| Scheduler Python | `workers/translation_scheduler.py` | Creador de jobs | +| Frontend Admin | `frontend/src/pages/AdminWorkers.tsx` | Panel de control | + +### Estados Actuales de traducción + +La tabla `traducciones` tiene: +- `pending` - disponible +- `done` - completado +- `error` - fallido + +**Problema identificado:** No hay estado `assigned` → varios workers pueden tomar el mismo job. + +### Query Actual del Worker + +```python +# workers/ctranslator_worker.py:361-378 +SELECT ... FROM traducciones t +WHERE t.lang_to = %s + AND (t.titulo_trad IS NULL OR t.resumen_trad IS NULL) + AND (t.locked_at IS NULL OR t.locked_at < NOW() - INTERVAL '10 minutes') +ORDER BY n.fecha DESC +LIMIT %s +FOR UPDATE SKIP LOCKED +``` + +Usa `locked_at` como mecanismo de locking, pero no actualiza un `status`. + +--- + +## Decisiones de Diseño + +### 1. Conexión: WebSocket + +- Bidireccional, tiempo real +- El worker solo necesita conexión saliente (como navegación web) +- No requiere exponer puertos adicionales en el VPS + +### 2. Autenticación: API Key + +- El servidor genera una API key única por worker +- El worker la envía en cada conexión WS o como header +- Más simple que usuario/password + +### 3. Cola de Trabajos: PostgreSQL + +- Persistencia estable (no Redis, para simplificar) +- El servidor escribe resultados (más control que acceso directo del worker) +- Si el worker falla, el servidor puede reasignar el job + +### 4. ML en Worker: CTranslate2 + +- El worker llama a CTranslate2 como subproceso +- Mismo mecanismo que el worker actual (Docker) pero standalone +- No requiere bindings Go nativos + +--- + +## Problemas Potenciales y Soluciones + +| Problema | Severity | Solución | +|----------|----------|----------| +| Worker sin internet | N/A | No es problema: el worker inicia conexión saliente | +| Worker se desconecta durante procesamiento | Alta | Timeout en servidor → job vuelve a `pending` | +| Job asignado a dos workers | Alta | Nuevo estado `assigned` + columna `worker_id` | +| Worker escribe directamente a BD | Media | Enviar resultados por WS → servidor controla escritura | +| Modelo NLLB no disponible localmente | Baja | Descargar modelo convertido previamente | + +--- + +## Plan de Implementación + +### Fase 1: Base de Datos + +**Archivo:** `init-db/35-remote-workers.sql` + +```sql +-- Tabla de workers remotos +CREATE TABLE IF NOT EXISTS remote_workers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + api_key VARCHAR(64) UNIQUE NOT NULL, + capabilities VARCHAR(50) DEFAULT 'cpu', -- 'cpu' o 'gpu' + status VARCHAR(20) DEFAULT 'offline', -- 'online', 'offline', 'disabled' + last_seen TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Añadir columnas a traducciones para workers remotos +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS worker_id INTEGER REFERENCES remote_workers(id); +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS assigned_at TIMESTAMP; + +-- Nuevo índice para buscar jobs disponibles por capabilities +CREATE INDEX IF NOT EXISTS idx_traducciones_pending_worker + ON traducciones(lang_to, status, worker_id) + WHERE status = 'pending' AND worker_id IS NULL; + +-- Índice para jobs asignados (para cleanup de timeout) +CREATE INDEX IF NOT EXISTS idx_traducciones_assigned_timeout + ON traducciones(assigned_at) + WHERE status = 'assigned' AND assigned_at IS NOT NULL; +``` + +### Fase 2: Modelos (Backend Go) + +**Nuevo archivo:** `backend/internal/models/worker.go` + +```go +package models + +import "time" + +// Worker remoto registrado +type RemoteWorker struct { + ID int `json:"id"` + Name string `json:"name"` + APIKey string `json:"api_key,omitempty"` // Solo al crear + Capabilities string `json:"capabilities"` // 'cpu' o 'gpu' + Status string `json:"status"` // 'online', 'offline', 'disabled' + LastSeen time.Time `json:"last_seen"` + CreatedAt time.Time `json:"created_at"` +} + +// Job de traducción para worker remoto +type TranslationJob struct { + ID int64 `json:"id"` + NewsID int64 `json:"noticia_id"` + LangFrom string `json:"lang_from"` + LangTo string `json:"lang_to"` + Title string `json:"title"` + Summary string `json:"summary"` +} + +// Resultado de traducción enviado por worker +type TranslationResult struct { + JobID int64 `json:"job_id"` + TitleTr string `json:"title_trad"` + SummaryTr string `json:"resumen_trad"` + Error string `json:"error,omitempty"` +} + +// Mensaje WebSocket: cliente → servidor +type WSClientMessage struct { + Type string `json:"type"` // "register", "heartbeat", "result" + Capabilities string `json:"capabilities,omitempty"` + APIKey string `json:"api_key,omitempty"` + WorkerID int `json:"worker_id,omitempty"` + Result *TranslationResult `json:"result,omitempty"` +} + +// Mensaje WebSocket: servidor → cliente +type WSServerMessage struct { + Type string `json:"type"` // "job", "ack", "error" + Job *TranslationJob `json:"job,omitempty"` +} +``` + +### Fase 3: Handlers REST (Backend Go) + +**Nuevo archivo:** `backend/internal/handlers/remote_worker.go` + +#### Endpoints + +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| POST | `/api/admin/workers/remote` | Crear worker (recibe nombre, capabilities) | +| GET | `/api/admin/workers/remote` | Listar todos los workers | +| GET | `/api/admin/workers/remote/:id` | Ver detalle de un worker | +| DELETE | `/api/admin/workers/remote/:id` | Eliminar worker | +| PATCH | `/api/admin/workers/remote/:id/toggle` | Habilitar/deshabilitar | + +#### Funciones principales + +1. **CreateRemoteWorker** - Genera API key aleatoria (64 chars), guarda en BD +2. **ListRemoteWorkers** - Retorna todos con jobs counts +3. **GetRemoteWorker** - Retorna detalle + estadísticas +4. **DeleteRemoteWorker** - Soft delete (marcar como disabled) +5. **ToggleRemoteWorker** - Enable/disable sin borrar + +### Fase 4: WebSocket Handler (Backend Go) + +**Nuevo archivo:** `backend/internal/handlers/worker_ws.go` + +```go +// Estructuras requeridas +type WSWorker struct { + conn *websocket.Conn + workerID int + capabilities string + lastHeartbeat time.Time +} + +// Conexiónmap[int]*WSWorker // workerID → conexión +var workers = make(map[int]*WSWorker) +var workersByConn = make(map[*websocket.Conn]*WSWorker) + +// Handler principal +func HandleWorkerWS(c *gin.Context) { + // 1. Verificar API key en query string o header + apiKey := c.Query("api_key") + if apiKey == "" { + apiKey = c.GetHeader("X-API-Key") + } + + // 2. Buscar worker por API key + workerID, err := validateWorkerAPIKey(apiKey) + if err != nil { + c.JSON(401, gin.H{"error": "Invalid API key"}) + return + } + + // 3. Upgrade a WebSocket + upgrader := websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { return true }, + } + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + + // 4. Registrar worker como online + wsWorker := &WSWorker{ + conn: conn, + workerID: workerID, + capabilities: getWorkerCapabilities(workerID), + lastHeartbeat: time.Now(), + } + workers[workerID] = wsWorker + workersByConn[conn] = wsWorker + + // 5. Update status en BD + updateWorkerStatus(workerID, "online") + + // 6. Loop de lectura + go handleWSRead(wsWorker) +} + +// Loop de lectura de mensajes del worker +func handleWSRead(wsWorker *WSWorker) { + for { + var msg WSClientMessage + err := wsWorker.conn.ReadJSON(&msg) + if err != nil { + // Worker desconectado + cleanupWorker(wsWorker) + return + } + + switch msg.Type { + case "register": + // Registro inicial (capabilities) + wsWorker.capabilities = msg.Capabilities + sendAck(wsWorker, "registered") + + case "heartbeat": + wsWorker.lastHeartbeat = time.Now() + sendAck(wsWorker, "ok") + + case "result": + // Worker completó un job + handleTranslationResult(wsWorker.workerID, msg.Result) + sendAck(wsWorker, "received") + } + } +} + +// Asignar job disponible al worker +func assignJobToWorker(workerID int) *TranslationJob { + caps := getWorkerCapabilities(workerID) + + // Buscar job pending que coincida con capabilities del worker + // Por ahora: cualquier job pending funciona (el worker decide qué procesar) + job := getNextPendingJob(caps) + if job == nil { + return nil + } + + // Asignar: UPDATE status='assigned', worker_id=workerID, assigned_at=NOW() + assignJob(job.ID, workerID) + + return job +} +``` + +### Fase 5: Scheduler (Migración Python → Go) + +**Nuevo archivo:** `backend/internal/workers/translation_scheduler.go` + +```go +package workers + +func StartTranslationScheduler() { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + scheduleTranslations() + } + } +} + +func scheduleTranslations() { + // Para cada idioma destino (es) + targetLangs := []string{"es"} + + for _, lang := range targetLangs { + // INSERT jobs para noticias sin traducción + _, err := db.Exec(` + INSERT INTO traducciones (noticia_id, lang_from, lang_to, status, created_at) + SELECT n.id, n.lang, $1, 'pending', NOW() + FROM noticias n + WHERE n.lang IS NOT NULL + AND TRIM(n.lang) != '' + AND n.lang != $1 + AND NOT EXISTS ( + SELECT 1 FROM traducciones t + WHERE t.noticia_id = n.id AND t.lang_to = $1 + ) + ORDER BY n.fecha DESC + LIMIT $2 + ON CONFLICT (noticia_id, lang_to) DO NOTHING + `, lang, 2000) + } +} + +// Cleanup de jobs huérfanos (assigned hace demasiado tiempo) +func cleanupStaleAssignments() { + db.Exec(` + UPDATE traducciones + SET status = 'pending', worker_id = NULL, assigned_at = NULL + WHERE status = 'assigned' + AND assigned_at < NOW() - INTERVAL '5 minutes' + `) +} +``` + +### Fase 6: Cliente Worker Remoto (Go) + +**Nuevo directorio:** `worker-client/` + +``` +worker-client/ +├── cmd/ +│ └── main.go # Entry point +├── internal/ +│ ├── config/ +│ │ └── config.go # Flags y configuración +│ ├── ws/ +│ │ └── client.go # WebSocket client +│ ├── translator/ +│ │ └── ctranslate2.go # Llamada a CTranslate2 +│ └── store/ +│ └── local.go # Persistencia local (SQLite) +├── go.mod +└── README.md +``` + +#### cmd/main.go + +```go +func main() { + // Flags + serverURL := flag.String("server", "ws://localhost:8080/ws/worker", "WebSocket server URL") + apiKey := flag.String("api-key", "", "API key for authentication") + device := flag.String("device", "cuda", "Device: cuda or cpu") + modelPath := flag.String("model-path", "./models/nllb-ct2", "CTranslate2 model path") + batchSize := flag.Int("batch", 8, "Translation batch size") + flag.Parse() + + // Validar + if *apiKey == "" { + log.Fatal("API key required") + } + + // Inicializar + cfg := config.Config{ + ServerURL: *serverURL, + APIKey: *apiKey, + Device: *device, + ModelPath: *modelPath, + BatchSize: *batchSize, + } + + // Conectar y loop + client := ws.NewClient(&cfg) + client.ConnectAndRun() +} +``` + +#### internal/ws/client.go + +```go +func (c *Client) ConnectAndRun() { + for { + err := c.connect() + if err != nil { + log.Printf("Connection failed: %v", err) + time.Sleep(5 * time.Second) + continue + } + + // Loop de mensajes + c.readLoop() + } +} + +func (c *Client) handleMessage(msg []byte) { + var serverMsg WSServerMessage + if err := json.Unmarshal(msg, &serverMsg); err != nil { + return + } + + switch serverMsg.Type { + case "job": + c.processJob(serverMsg.Job) + case "ping": + c.sendHeartbeat() + } +} + +func (c *Client) processJob(job *TranslationJob) { + // 1. Traducir título + titleTr := translate(job.Title, job.LangFrom, job.LangTo) + + // 2. Traducir resumen (chunks si es largo) + summaryTr := translateLongText(job.Summary, job.LangFrom, job.LangTo) + + // 3. Enviar resultado + c.SendResult(job.ID, titleTr, summaryTr, "") +} +``` + +#### internal/translator/ctranslate2.go + +```go +func Translate(text, srcLang, tgtLang string) (string, error) { + // Mapear códigos de idioma + srcCode := langMap[srcLang] + tgtCode := langMap[tgtLang] + + // Construir comando + cmd := exec.Command( + "ct2-transformers-converter", // O el binario de ctranslate2 + "--source", text, + "--src_lang", srcCode, + "--tgt_lang", tgtCode, + ) + + output, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + + return string(output), nil +} +``` + +**Nota:** La forma más estable es usar el binario `ctranslate2` directamente como wrapper del modelo convertido. Ver documentación de CTranslate2 para la interfaz CLI. + +### Fase 7: Frontend (Panel Admin) + +**Modificar:** `frontend/src/pages/AdminWorkers.tsx` + +Añadir nueva sección "Workers Remotos": + +```tsx +// Estados adicionales +const [remoteWorkers, setRemoteWorkers] = useState([]) +const [showAddForm, setShowAddForm] = useState(false) + +// Fetch +const fetchRemoteWorkers = async () => { + const res = await api.get('/admin/workers/remote') + setRemoteWorkers(res.data) +} + +// UI: Añadir después de la sección de workers locales +
    +

    + + Workers Remotos +

    + + {/* Botón añadir */} + + + {/* Lista de workers */} +
    + {remoteWorkers.map(worker => ( +
    +
    + {worker.name} + + {worker.status} + +
    +
    + + +
    +
    + ))} +
    +
    +``` + +--- + +## Archivos a Crear/Modificar + +### Backend (Go) + +| Archivo | Acción | Descripción | +|---------|--------|-------------| +| `init-db/35-remote-workers.sql` | Crear | Tabla remote_workers + columnas | +| `backend/internal/models/worker.go` | Crear | Modelos RemoteWorker, TranslationJob | +| `backend/internal/handlers/remote_worker.go` | Crear | CRUD REST API | +| `backend/internal/handlers/worker_ws.go` | Crear | WebSocket handler | +| `backend/internal/workers/translation_scheduler.go` | Crear | Scheduler migrado de Python | +| `backend/cmd/server/main.go` | Modificar | Añadir rutas + iniciar scheduler | +| `backend/go.mod` | Modificar | Añadir dependencia `github.com/gorilla/websocket` | + +### Frontend (React) + +| Archivo | Acción | Descripción | +|---------|--------|-------------| +| `frontend/src/pages/AdminWorkers.tsx` | Modificar | Añadir sección workers remotos | + +### Cliente Worker + +| Archivo | Acción | Descripción | +|---------|--------|-------------| +| `worker-client/cmd/main.go` | Crear | CLI entry point | +| `worker-client/internal/config/config.go` | Crear | Configuración | +| `worker-client/internal/ws/client.go` | Crear | WebSocket client | +| `worker-client/internal/translator/ctranslate2.go` | Crear | Wrapper CTranslate2 | +| `worker-client/go.mod` | Crear | Módulos Go | +| `worker-client/README.md` | Crear | Instrucciones de uso | + +--- + +## Consideraciones de Seguridad + +1. **API Key**: Generar con `crypto/rand` - 64 caracteres hex +2. **Rate limiting**: En endpoints de creación de workers +3. **Validación**: Verificar que el worker tiene capabilities válidas +4. **Timeout**: Jobs asignados sin respuesta en 5 min vuelven a cola +5. **Conexión**: El servidor debe verificar origen del WebSocket + +--- + +## Testing Plan + +1. **Unidad**: Tests de handlers (mock de BD) +2. **Integración WS**: Probar conexión, registro, heartbeat, resultado +3. **E2E**: + - Iniciar cliente worker local + - Crear job pending manualmente + - Verificar que worker recibe job + - Verificar resultado en BD + +--- + +## Notas de Implementación + +- Mantener compatibilidad con workers Docker existentes (CPU/GPU locales) +- Los workers remotos serán una opción adicional, no reemplazo +- El scheduler puede coexistir (Go + Python) durante transición +- Considerar métricas: jobs procesadors por worker, tiempo promedio \ No newline at end of file diff --git a/workers/remote_translator_worker.py b/workers/remote_translator_worker.py new file mode 100644 index 0000000..b1137d6 --- /dev/null +++ b/workers/remote_translator_worker.py @@ -0,0 +1,389 @@ +import os +import sys +import time +import json +import logging +import re +import threading +from typing import List, Optional + +import websocket + +import ctranslate2 +from transformers import AutoTokenizer + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s: %(message)s", + handlers=[logging.StreamHandler(sys.stdout)] +) +LOG = logging.getLogger("remote-translator") + +WORKER_NAME = os.environ.get("WORKER_NAME", "remote-worker") +WORKER_API_KEY = os.environ.get("WORKER_API_KEY", "") +WORKER_SERVER = os.environ.get("WORKER_SERVER", "ws://localhost:8080/ws/worker") +DEVICE = os.environ.get("CT2_DEVICE", "cpu") +MODEL_PATH = os.environ.get("CT2_MODEL_PATH", "/app/models/nllb-ct2") +COMPUTE_TYPE = os.environ.get("CT2_COMPUTE_TYPE", "int8") +UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M") + +LANG_CODE_MAP = { + "en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn", + "it": "ita_Latn", "pt": "por_Latn", "nl": "nld_Latn", "sv": "swe_Latn", + "da": "dan_Latn", "fi": "fin_Latn", "no": "nob_Latn", + "pl": "pol_Latn", "cs": "ces_Latn", "sk": "slk_Latn", + "sl": "slv_Latn", "hu": "hun_Latn", "ro": "ron_Latn", + "el": "ell_Grek", "ru": "rus_Cyrl", "uk": "ukr_Cyrl", + "tr": "tur_Latn", "ar": "arb_Arab", "fa": "pes_Arab", + "he": "heb_Hebr", "zh": "zho_Hans", "ja": "jpn_Jpan", + "ko": "kor_Hang", "vi": "vie_Latn", +} + +MAX_SRC_TOKENS = 512 +MAX_NEW_TOKENS = 512 +BODY_CHARS_CHUNK = 900 + +_tokenizer = None +_translator = None +_ws = None +_reconnect_delay = 5 +_running = True +_stats = {"jobs_completed": 0, "jobs_failed": 0} + + +def ensure_model(): + global _tokenizer, _translator + + if _translator: + return + + model_bin = os.path.join(MODEL_PATH, "model.bin") + + if not os.path.exists(model_bin): + LOG.info(f"CTranslate2 model not found at {MODEL_PATH}, converting...") + convert_model() + + LOG.info(f"Loading CTranslate2 model from {MODEL_PATH} on {DEVICE}") + + _translator = ctranslate2.Translator( + MODEL_PATH, + device=DEVICE, + compute_type=COMPUTE_TYPE, + ) + + _tokenizer = AutoTokenizer.from_pretrained(UNIVERSAL_MODEL) + LOG.info("CTranslate2 model loaded successfully") + + +def convert_model(): + import subprocess + + os.makedirs(MODEL_PATH, exist_ok=True) + + quantization = COMPUTE_TYPE if COMPUTE_TYPE != "auto" else "int8" + + cmd = [ + "ct2-transformers-converter", + "--model", UNIVERSAL_MODEL, + "--output_dir", MODEL_PATH, + "--quantization", quantization, + "--force" + ] + + LOG.info(f"Running: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800) + + if result.returncode != 0: + LOG.error(f"Model conversion failed: {result.stderr}") + raise RuntimeError("Failed to convert model") + + LOG.info("Model conversion completed") + + +def clean_text(text: str) -> str: + if not text: + return "" + text = re.sub(r'<[^>]+>', '', text) + text = text.replace('', '') + text = text.replace(' ', ' ') + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('"', '"') + text = re.sub(r'\s+', ' ', text) + return text.strip() + + +def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: + if not texts: + return [] + + ensure_model() + + clean = [(t or "").strip() for t in texts] + if all(not t for t in clean): + return ["" for _ in clean] + + src_code = LANG_CODE_MAP.get(src, f"{src}_Latn") + tgt_code = LANG_CODE_MAP.get(tgt, "spa_Latn") + + try: + _tokenizer.src_lang = src_code + except Exception: + pass + + sources = [] + for t in clean: + if t: + ids = _tokenizer.encode(t, truncation=True, max_length=MAX_SRC_TOKENS) + tokens = _tokenizer.convert_ids_to_tokens(ids) + sources.append(tokens) + else: + sources.append([]) + + target_prefix = [[tgt_code]] * len(sources) + + results = _translator.translate_batch( + sources, + target_prefix=target_prefix, + beam_size=2, + max_decoding_length=MAX_NEW_TOKENS, + repetition_penalty=2.0, + no_repeat_ngram_size=3, + ) + + translated = [] + for result in results: + try: + if result.hypotheses and len(result.hypotheses) > 0: + hyp = result.hypotheses[0] + if isinstance(hyp, list) and len(hyp) > 0: + first_hyp = hyp[0] + if isinstance(first_hyp, dict) and "token_ids" in first_hyp: + tokens = first_hyp["token_ids"] + text = _tokenizer.decode(tokens) + translated.append(text.strip()) + elif isinstance(first_hyp, str): + token_strings = hyp[1:] if len(hyp) > 1 else [] + if token_strings: + text = _tokenizer.convert_tokens_to_string(token_strings) + translated.append(text.strip()) + else: + translated.append("") + else: + translated.append("") + else: + translated.append("") + else: + translated.append("") + except Exception as e: + LOG.error(f"Error processing result: {e}") + translated.append("") + + return translated + + +def split_body_into_chunks(text: str) -> List[str]: + text = (text or "").strip() + if len(text) <= BODY_CHARS_CHUNK: + return [text] if text else [] + + parts = re.split(r'(\n\n+|(?<=[\.\!\?؛؟。])\s+)', text) + chunks = [] + current = "" + + for part in parts: + if not part: + continue + if len(current) + len(part) <= BODY_CHARS_CHUNK: + current += part + else: + if current.strip(): + chunks.append(current.strip()) + current = part + if current.strip(): + chunks.append(current.strip()) + + return chunks if chunks else [text] + + +def translate_body_long(src: str, tgt: str, body: str) -> str: + body = (body or "").strip() + if not body: + return "" + + chunks = split_body_into_chunks(body) + if len(chunks) == 1: + return translate_texts(src, tgt, [body])[0] + + translated_chunks = [] + for ch in chunks: + tr = translate_texts(src, tgt, [ch])[0] + translated_chunks.append(tr) + + return " ".join(translated_chunks) + + +def process_job(job: dict) -> dict: + job_id = job.get("id") + lang_from = job.get("lang_from", "en") + lang_to = job.get("lang_to", "es") + title = job.get("title", "") + summary = job.get("summary", "") + + LOG.info(f"Processing job {job_id}: {lang_from} -> {lang_to}") + + if lang_from == lang_to: + return { + "job_id": job_id, + "title_trad": title, + "summary_trad": summary, + "error": "" + } + + try: + title_tr = translate_texts(lang_from, lang_to, [title])[0] + title_tr = clean_text(title_tr) or title + + summary_tr = "" + if summary: + summary_tr = translate_body_long(lang_from, lang_to, summary) + summary_tr = clean_text(summary_tr) or summary + + _stats["jobs_completed"] += 1 + + return { + "job_id": job_id, + "title_trad": title_tr, + "summary_trad": summary_tr, + "error": "" + } + except Exception as e: + LOG.error(f"Translation error for job {job_id}: {e}") + _stats["jobs_failed"] += 1 + return { + "job_id": job_id, + "title_trad": "", + "summary_trad": "", + "error": str(e) + } + + +def send_message(msg: dict): + if _ws and _ws.sock and _ws.sock.connected: + try: + _ws.send(json.dumps(msg)) + except Exception as e: + LOG.error(f"Send error: {e}") + + +def on_message(ws, message): + try: + msg = json.loads(message) + except: + LOG.error(f"Invalid JSON: {message}") + return + + msg_type = msg.get("type") + + if msg_type == "job": + job = msg.get("job", {}) + result = process_job(job) + send_message({ + "type": "result", + "result": result + }) + LOG.info(f"Sent result for job {result['job_id']}") + + elif msg_type == "ping": + send_message({"type": "heartbeat"}) + + elif msg_type == "ack": + LOG.debug(f"Server ACK: {msg.get('message', '')}") + + elif msg_type == "error": + LOG.error(f"Server error: {msg.get('message', '')}") + + +def on_error(ws, error): + LOG.error(f"WebSocket error: {error}") + + +def on_close(ws, close_status_code, close_msg): + global _reconnect_delay + LOG.warning(f"WebSocket closed: {close_status_code} - {close_msg}") + LOG.info(f"Reconnecting in {_reconnect_delay}s...") + + +def on_open(ws): + global _reconnect_delay + LOG.info("Connected to server") + _reconnect_delay = 5 + + send_message({ + "type": "register", + "capabilities": DEVICE, + "worker_name": WORKER_NAME + }) + + +def stats_reporter(): + while _running: + time.sleep(60) + LOG.info(f"Stats: completed={_stats['jobs_completed']}, failed={_stats['jobs_failed']}") + + +def connect(): + global _ws, _reconnect_delay + + while _running: + try: + if not WORKER_API_KEY: + LOG.error("WORKER_API_KEY not set") + time.sleep(60) + continue + + ws_url = f"{WORKER_SERVER}?api_key={WORKER_API_KEY}" + + _ws = websocket.WebSocketApp( + ws_url, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close, + header={"X-API-Key": WORKER_API_KEY} + ) + + LOG.info(f"Connecting to {WORKER_SERVER}...") + _ws.run_forever(ping_interval=30, ping_timeout=10) + + except Exception as e: + LOG.error(f"Connection error: {e}") + + LOG.info(f"Waiting {_reconnect_delay}s before reconnect...") + time.sleep(_reconnect_delay) + _reconnect_delay = min(_reconnect_delay * 2, 60) + + +def main(): + global _running + + if not WORKER_API_KEY: + LOG.error("WORKER_API_KEY environment variable is required") + sys.exit(1) + + LOG.info(f"Remote translator worker starting...") + LOG.info(f"Server: {WORKER_SERVER}") + LOG.info(f"Device: {DEVICE}") + LOG.info(f"Model: {MODEL_PATH}") + + ensure_model() + + stats_thread = threading.Thread(target=stats_reporter, daemon=True) + stats_thread.start() + + connect() + + +if __name__ == "__main__": + main() \ No newline at end of file From 98e0cd0e46883c990719bc888d2fc27ed60bd237 Mon Sep 17 00:00:00 2001 From: lordpietre Date: Wed, 1 Apr 2026 03:55:40 +0000 Subject: [PATCH 13/21] feat: remote worker GPU support with nllb-1.3B - Update Dockerfile.remote-worker with CUDA support - Update docker-compose.yml with GPU settings - Update remote_translator_worker.py to use nllb-200-1.3B - Update worker_ws.go with model URL handler --- Dockerfile.remote-worker | 27 +++++------------ backend/internal/handlers/worker_ws.go | 42 ++++++++++++++++++++++++++ docker-compose.yml | 14 ++++++--- workers/remote_translator_worker.py | 8 +++-- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/Dockerfile.remote-worker b/Dockerfile.remote-worker index 44fcb11..939684b 100644 --- a/Dockerfile.remote-worker +++ b/Dockerfile.remote-worker @@ -1,23 +1,20 @@ FROM python:3.11-slim-bookworm -SHELL ["/bin/bash", "-c"] - RUN apt-get update && apt-get install -y --no-install-recommends \ - patchelf libpq-dev gcc git curl wget bash \ + patchelf libpq-dev gcc git curl wget \ && rm -rf /var/lib/apt/lists/* ENV PYTHONUNBUFFERED=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ TOKENIZERS_PARALLELISM=false \ - HF_HOME=/root/.cache/huggingface \ - DEBIAN_FRONTEND=noninteractive + HF_HOME=/root/.cache/huggingface WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --upgrade pip -RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cpu +RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 RUN pip install --no-cache-dir \ ctranslate2==3.24.0 \ @@ -33,18 +30,10 @@ RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ -name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \ xargs -I {} patchelf --clear-execstack {} || true -COPY workers/ ./workers/ -COPY init-db/ ./init-db/ -COPY migrations/ ./migrations/ +COPY workers/remote_translator_worker.py /app/worker.py -ENV DB_HOST=db -ENV DB_PORT=5432 -ENV DB_NAME=rss -ENV DB_USER=rss -ENV DB_PASS=x +ENV CT2_DEVICE=cuda +ENV CT2_COMPUTE_TYPE=float16 +ENV UNIVERSAL_MODEL=facebook/nllb-200-1.3B -ENV WORKER_SERVER=ws://localhost:8080/ws/worker -ENV CT2_DEVICE=cpu -ENV CT2_COMPUTE_TYPE=int8 - -CMD ["python", "-m", "workers.remote_translator_worker"] \ No newline at end of file +CMD ["python", "/app/worker.py"] \ No newline at end of file diff --git a/backend/internal/handlers/worker_ws.go b/backend/internal/handlers/worker_ws.go index 15eb698..a9d58cd 100644 --- a/backend/internal/handlers/worker_ws.go +++ b/backend/internal/handlers/worker_ws.go @@ -294,4 +294,46 @@ func StartJobAssigner() { } } }() +} + +func GetModelDownloadURL(c *gin.Context) { + apiKey := c.Query("api_key") + if apiKey == "" { + apiKey = c.GetHeader("X-API-Key") + } + + if apiKey == "" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "API key required"}) + return + } + + ctx := c.Request.Context() + + var workerID int + err := db.GetPool().QueryRow(ctx, ` + SELECT id FROM remote_workers WHERE api_key = $1 + `, apiKey).Scan(&workerID) + + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"}) + return + } + + var modelURL, modelPath string + db.GetPool().QueryRow(ctx, ` + SELECT value FROM config WHERE key = 'remote_worker_model_url' + `).Scan(&modelURL) + + db.GetPool().QueryRow(ctx, ` + SELECT value FROM config WHERE key = 'remote_worker_model_path' + `).Scan(&modelPath) + + if modelPath == "" { + modelPath = "/app/models/nllb-ct2" + } + + c.JSON(http.StatusOK, gin.H{ + "model_download_url": modelURL, + "model_path": modelPath, + }) } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e90b51b..a5c51e5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -324,10 +324,10 @@ services: WORKER_NAME: ${WORKER_NAME:-remote-worker-1} WORKER_API_KEY: ${WORKER_API_KEY:-} WORKER_SERVER: ${WORKER_SERVER:-ws://backend-go:8080/ws/worker} - CT2_DEVICE: ${CT2_DEVICE:-cpu} + CT2_DEVICE: ${CT2_DEVICE:-cuda} CT2_MODEL_PATH: /app/models/nllb-ct2 - CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-int8} - UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M + CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-float16} + UNIVERSAL_MODEL: facebook/nllb-200-1.3B HF_HOME: /app/hf_cache TZ: Europe/Madrid volumes: @@ -340,8 +340,12 @@ services: deploy: resources: limits: - cpus: '1' - memory: 2G + memory: 6G + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [ gpu ] restart: unless-stopped # ================================================================================== diff --git a/workers/remote_translator_worker.py b/workers/remote_translator_worker.py index b1137d6..1b28cc4 100644 --- a/workers/remote_translator_worker.py +++ b/workers/remote_translator_worker.py @@ -5,6 +5,7 @@ import json import logging import re import threading +import hashlib from typing import List, Optional import websocket @@ -25,7 +26,7 @@ WORKER_SERVER = os.environ.get("WORKER_SERVER", "ws://localhost:8080/ws/worker") DEVICE = os.environ.get("CT2_DEVICE", "cpu") MODEL_PATH = os.environ.get("CT2_MODEL_PATH", "/app/models/nllb-ct2") COMPUTE_TYPE = os.environ.get("CT2_COMPUTE_TYPE", "int8") -UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M") +UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-1.3B") LANG_CODE_MAP = { "en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn", @@ -60,7 +61,8 @@ def ensure_model(): model_bin = os.path.join(MODEL_PATH, "model.bin") if not os.path.exists(model_bin): - LOG.info(f"CTranslate2 model not found at {MODEL_PATH}, converting...") + LOG.info(f"CTranslate2 model not found at {MODEL_PATH}") + LOG.info("Downloading from HuggingFace...") convert_model() LOG.info(f"Loading CTranslate2 model from {MODEL_PATH} on {DEVICE}") @@ -82,6 +84,8 @@ def convert_model(): quantization = COMPUTE_TYPE if COMPUTE_TYPE != "auto" else "int8" + LOG.info(f"Converting {UNIVERSAL_MODEL} to CTranslate2 format...") + cmd = [ "ct2-transformers-converter", "--model", UNIVERSAL_MODEL, From be56de7dd4b5cb464fd32c62d18e6312e723eb9c Mon Sep 17 00:00:00 2001 From: lordpietre Date: Wed, 1 Apr 2026 21:29:11 +0000 Subject: [PATCH 14/21] cambios para worker-remoto --- backend/go.mod | 1 + backend/go.sum | 156 -------- backend/internal/auth/jwt.go | 55 +++ backend/internal/auth/jwt_test.go | 96 +++++ backend/internal/config/config_test.go | 145 ++++++++ backend/internal/handlers/admin.go | 26 +- backend/internal/handlers/auth.go | 55 +-- backend/internal/handlers/auth_test.go | 108 ++++++ backend/internal/middleware/auth_test.go | 141 +++++++ bugs.md | 448 +++++++++++++++++++++++ frontend/package.json | 13 +- frontend/src/test/setup.ts | 1 + frontend/vitest.config.ts | 15 + remote-worker/.gitignore | 7 + remote-worker/Dockerfile | 36 ++ remote-worker/README.md | 53 +++ remote-worker/requirements.txt | 8 + remote-worker/worker.py | 393 ++++++++++++++++++++ requirements.txt | 3 + 19 files changed, 1544 insertions(+), 216 deletions(-) delete mode 100644 backend/go.sum create mode 100644 backend/internal/auth/jwt.go create mode 100644 backend/internal/auth/jwt_test.go create mode 100644 backend/internal/config/config_test.go create mode 100644 backend/internal/handlers/auth_test.go create mode 100644 backend/internal/middleware/auth_test.go create mode 100644 bugs.md create mode 100644 frontend/src/test/setup.ts create mode 100644 frontend/vitest.config.ts create mode 100644 remote-worker/.gitignore create mode 100644 remote-worker/Dockerfile create mode 100644 remote-worker/README.md create mode 100644 remote-worker/requirements.txt create mode 100644 remote-worker/worker.py diff --git a/backend/go.mod b/backend/go.mod index 866ec16..54449fd 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -7,6 +7,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/golang-jwt/jwt/v5 v5.0.0 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.1 github.com/jackc/pgx/v5 v5.4.3 github.com/mmcdole/gofeed v1.2.1 github.com/redis/go-redis/v9 v9.0.5 diff --git a/backend/go.sum b/backend/go.sum deleted file mode 100644 index 1e5649f..0000000 --- a/backend/go.sum +++ /dev/null @@ -1,156 +0,0 @@ -github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= -github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= -github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= -github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= -github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s= -github.com/mmcdole/gofeed v1.2.1/go.mod h1:2wVInNpgmC85q16QTTuwbuKxtKkHLCDDtf0dCmnrNr4= -github.com/mmcdole/goxpp v1.1.0 h1:WwslZNF7KNAXTFuzRtn/OKZxFLJAAyOA9w82mDz2ZGI= -github.com/mmcdole/goxpp v1.1.0/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= -github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/backend/internal/auth/jwt.go b/backend/internal/auth/jwt.go new file mode 100644 index 0000000..c00db91 --- /dev/null +++ b/backend/internal/auth/jwt.go @@ -0,0 +1,55 @@ +package auth + +import ( + "time" + + "github.com/golang-jwt/jwt/v5" +) + +var jwtSecret []byte + +func SetJWTSecret(secret string) { + jwtSecret = []byte(secret) +} + +func GetJWTSecret() []byte { + return jwtSecret +} + +type Claims struct { + UserID int64 `json:"user_id"` + Email string `json:"email"` + Username string `json:"username"` + IsAdmin bool `json:"is_admin"` + jwt.RegisteredClaims +} + +func GenerateToken(userID int64, email, username string, isAdmin bool) (string, error) { + expirationTime := time.Now().Add(24 * time.Hour) + claims := &Claims{ + UserID: userID, + Email: email, + Username: username, + IsAdmin: isAdmin, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expirationTime), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(jwtSecret) +} + +func ValidateToken(tokenString string) (*Claims, error) { + claims := &Claims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + return jwtSecret, nil + }) + + if err != nil || !token.Valid { + return nil, err + } + + return claims, nil +} diff --git a/backend/internal/auth/jwt_test.go b/backend/internal/auth/jwt_test.go new file mode 100644 index 0000000..ced02bd --- /dev/null +++ b/backend/internal/auth/jwt_test.go @@ -0,0 +1,96 @@ +package auth + +import ( + "testing" + "time" +) + +func TestGenerateAndValidateToken(t *testing.T) { + SetJWTSecret("test-secret-key-12345") + + token, err := GenerateToken(1, "test@example.com", "testuser", true) + if err != nil { + t.Fatalf("GenerateToken failed: %v", err) + } + + if token == "" { + t.Fatal("token should not be empty") + } + + claims, err := ValidateToken(token) + if err != nil { + t.Fatalf("ValidateToken failed: %v", err) + } + + if claims.UserID != 1 { + t.Errorf("expected userID 1, got %d", claims.UserID) + } + if claims.Email != "test@example.com" { + t.Errorf("expected email test@example.com, got %s", claims.Email) + } + if claims.Username != "testuser" { + t.Errorf("expected username testuser, got %s", claims.Username) + } + if !claims.IsAdmin { + t.Error("expected isAdmin true") + } +} + +func TestValidateInvalidToken(t *testing.T) { + SetJWTSecret("test-secret-key-12345") + + _, err := ValidateToken("invalid-token") + if err == nil { + t.Error("expected error for invalid token") + } +} + +func TestValidateTokenWithWrongSecret(t *testing.T) { + SetJWTSecret("secret-1") + + token, err := GenerateToken(1, "test@example.com", "testuser", false) + if err != nil { + t.Fatalf("GenerateToken failed: %v", err) + } + + SetJWTSecret("secret-2") + + _, err = ValidateToken(token) + if err == nil { + t.Error("expected error for token with different secret") + } +} + +func TestClaimsHaveExpiration(t *testing.T) { + SetJWTSecret("test-secret-key-12345") + + token, err := GenerateToken(1, "test@example.com", "testuser", false) + if err != nil { + t.Fatalf("GenerateToken failed: %v", err) + } + + claims, err := ValidateToken(token) + if err != nil { + t.Fatalf("ValidateToken failed: %v", err) + } + + if claims.ExpiresAt == nil { + t.Error("expected expiration time to be set") + } + + expTime := claims.ExpiresAt.Time + expectedExpiration := time.Now().Add(24 * time.Hour) + + if expTime.Sub(expectedExpiration) > time.Minute { + t.Errorf("expiration time should be ~24 hours from now, got %v", expTime) + } +} + +func TestEmptyToken(t *testing.T) { + SetJWTSecret("test-secret-key-12345") + + _, err := ValidateToken("") + if err == nil { + t.Error("expected error for empty token") + } +} diff --git a/backend/internal/config/config_test.go b/backend/internal/config/config_test.go new file mode 100644 index 0000000..0fb687b --- /dev/null +++ b/backend/internal/config/config_test.go @@ -0,0 +1,145 @@ +package config + +import ( + "os" + "testing" + "time" +) + +func TestLoadDefaults(t *testing.T) { + os.Clearenv() + + cfg := Load() + + if cfg.ServerPort != "8080" { + t.Errorf("expected ServerPort '8080', got %s", cfg.ServerPort) + } + if cfg.SecretKey != "change-this-secret-key" { + t.Errorf("expected SecretKey 'change-this-secret-key', got %s", cfg.SecretKey) + } + if cfg.DefaultLang != "es" { + t.Errorf("expected DefaultLang 'es', got %s", cfg.DefaultLang) + } + if cfg.NewsPerPage != 30 { + t.Errorf("expected NewsPerPage 30, got %d", cfg.NewsPerPage) + } + if cfg.DockerComposeDir != "/datos/rss2" { + t.Errorf("expected DockerComposeDir '/datos/rss2', got %s", cfg.DockerComposeDir) + } + if cfg.WikiImagesPath != "/app/data/wiki_images" { + t.Errorf("expected WikiImagesPath '/app/data/wiki_images', got %s", cfg.WikiImagesPath) + } + if cfg.AllowedOrigins != "*" { + t.Errorf("expected AllowedOrigins '*', got %s", cfg.AllowedOrigins) + } +} + +func TestLoadFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("SERVER_PORT", "9000") + os.Setenv("DATABASE_URL", "postgres://user:pass@host:5432/db") + os.Setenv("SECRET_KEY", "my-secret-key") + os.Setenv("DEFAULT_LANG", "en") + os.Setenv("NEWS_PER_PAGE", "50") + os.Setenv("JWT_EXPIRATION", "48h") + os.Setenv("RATE_LIMIT_PER_MINUTE", "100") + os.Setenv("DOCKER_COMPOSE_DIR", "/custom/path") + os.Setenv("WIKI_IMAGES_PATH", "/custom/images") + + cfg := Load() + + if cfg.ServerPort != "9000" { + t.Errorf("expected ServerPort '9000', got %s", cfg.ServerPort) + } + if cfg.DatabaseURL != "postgres://user:pass@host:5432/db" { + t.Errorf("expected DatabaseURL, got %s", cfg.DatabaseURL) + } + if cfg.SecretKey != "my-secret-key" { + t.Errorf("expected SecretKey 'my-secret-key', got %s", cfg.SecretKey) + } + if cfg.DefaultLang != "en" { + t.Errorf("expected DefaultLang 'en', got %s", cfg.DefaultLang) + } + if cfg.NewsPerPage != 50 { + t.Errorf("expected NewsPerPage 50, got %d", cfg.NewsPerPage) + } + if cfg.JWTExpiration != 48*time.Hour { + t.Errorf("expected JWTExpiration 48h, got %v", cfg.JWTExpiration) + } + if cfg.RateLimitPerMinute != 100 { + t.Errorf("expected RateLimitPerMinute 100, got %d", cfg.RateLimitPerMinute) + } + if cfg.DockerComposeDir != "/custom/path" { + t.Errorf("expected DockerComposeDir '/custom/path', got %s", cfg.DockerComposeDir) + } + if cfg.WikiImagesPath != "/custom/images" { + t.Errorf("expected WikiImagesPath '/custom/images', got %s", cfg.WikiImagesPath) + } +} + +func TestLoadInvalidInt(t *testing.T) { + os.Clearenv() + os.Setenv("NEWS_PER_PAGE", "invalid") + + cfg := Load() + + if cfg.NewsPerPage != 30 { + t.Errorf("expected default NewsPerPage 30 for invalid value, got %d", cfg.NewsPerPage) + } +} + +func TestLoadInvalidDuration(t *testing.T) { + os.Clearenv() + os.Setenv("JWT_EXPIRATION", "invalid") + + cfg := Load() + + if cfg.JWTExpiration != 24*time.Hour { + t.Errorf("expected default JWTExpiration 24h for invalid value, got %v", cfg.JWTExpiration) + } +} + +func TestGetEnv(t *testing.T) { + os.Clearenv() + os.Setenv("TEST_KEY", "test_value") + + result := getEnv("TEST_KEY", "default") + if result != "test_value" { + t.Errorf("expected 'test_value', got %s", result) + } + + result = getEnv("NON_EXISTENT", "default") + if result != "default" { + t.Errorf("expected 'default', got %s", result) + } +} + +func TestGetEnvInt(t *testing.T) { + os.Clearenv() + os.Setenv("TEST_INT", "123") + + result := getEnvInt("TEST_INT", 0) + if result != 123 { + t.Errorf("expected 123, got %d", result) + } + + result = getEnvInt("NON_EXISTENT", 99) + if result != 99 { + t.Errorf("expected 99, got %d", result) + } +} + +func TestGetEnvDuration(t *testing.T) { + os.Clearenv() + os.Setenv("TEST_DURATION", "1h30m") + + result := getEnvDuration("TEST_DURATION", 0) + if result != 90*time.Minute { + t.Errorf("expected 90m, got %v", result) + } + + result = getEnvDuration("NON_EXISTENT", 5*time.Minute) + if result != 5*time.Minute { + t.Errorf("expected 5m, got %v", result) + } +} diff --git a/backend/internal/handlers/admin.go b/backend/internal/handlers/admin.go index 60bad83..8b4e784 100644 --- a/backend/internal/handlers/admin.go +++ b/backend/internal/handlers/admin.go @@ -6,6 +6,7 @@ import ( "context" "encoding/csv" "fmt" + "log" "net/http" "os" "os/exec" @@ -14,6 +15,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/config" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" ) @@ -465,13 +467,14 @@ func StartWorkers(c *gin.Context) { } // Detener cualquier translator existente + composeDir := config.Load().DockerComposeDir stopCmd := exec.Command("docker", "compose", "stop", "translator", "translator-gpu") - stopCmd.Dir = "/datos/rss2" + stopCmd.Dir = composeDir stopCmd.Run() // Iniciar con el número de workers startCmd := exec.Command("docker", "compose", "up", "-d", "--scale", fmt.Sprintf("%s=%d", serviceName, workers), serviceName) - startCmd.Dir = "/datos/rss2" + startCmd.Dir = composeDir output, err := startCmd.CombinedOutput() if err != nil { @@ -483,9 +486,15 @@ func StartWorkers(c *gin.Context) { } // Actualizar estado en BD - db.GetPool().Exec(ctx, "UPDATE config SET value = 'running', updated_at = NOW() WHERE key = 'translator_status'") - db.GetPool().Exec(ctx, "UPDATE config SET value = $1, updated_at = NOW() WHERE key = 'translator_type'", translatorType) - db.GetPool().Exec(ctx, "UPDATE config SET value = $1, updated_at = NOW() WHERE key = 'translator_workers'", translatorWorkers) + if _, err := db.GetPool().Exec(ctx, "UPDATE config SET value = 'running', updated_at = NOW() WHERE key = 'translator_status'"); err != nil { + log.Printf("Failed to update translator_status: %v", err) + } + if _, err := db.GetPool().Exec(ctx, "UPDATE config SET value = $1, updated_at = NOW() WHERE key = 'translator_type'", translatorType); err != nil { + log.Printf("Failed to update translator_type: %v", err) + } + if _, err := db.GetPool().Exec(ctx, "UPDATE config SET value = $1, updated_at = NOW() WHERE key = 'translator_workers'", translatorWorkers); err != nil { + log.Printf("Failed to update translator_workers: %v", err) + } c.JSON(http.StatusOK, gin.H{ "message": "Workers started successfully", @@ -497,8 +506,9 @@ func StartWorkers(c *gin.Context) { func StopWorkers(c *gin.Context) { // Detener traductores + composeDir := config.Load().DockerComposeDir cmd := exec.Command("docker", "compose", "stop", "translator", "translator-gpu") - cmd.Dir = "/datos/rss2" + cmd.Dir = composeDir output, err := cmd.CombinedOutput() if err != nil { @@ -510,7 +520,9 @@ func StopWorkers(c *gin.Context) { } // Actualizar estado en BD - db.GetPool().Exec(c.Request.Context(), "UPDATE config SET value = 'stopped', updated_at = NOW() WHERE key = 'translator_status'") + if _, err := db.GetPool().Exec(c.Request.Context(), "UPDATE config SET value = 'stopped', updated_at = NOW() WHERE key = 'translator_status'"); err != nil { + log.Printf("Failed to update translator_status: %v", err) + } c.JSON(http.StatusOK, gin.H{ "message": "Workers stopped successfully", diff --git a/backend/internal/handlers/auth.go b/backend/internal/handlers/auth.go index 511b50c..6950200 100644 --- a/backend/internal/handlers/auth.go +++ b/backend/internal/handlers/auth.go @@ -2,18 +2,14 @@ package handlers import ( "net/http" - "time" "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v5" - "github.com/rss2/backend/internal/config" + "github.com/rss2/backend/internal/auth" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" "golang.org/x/crypto/bcrypt" ) -var jwtSecret []byte - func CheckFirstUser(c *gin.Context) { var count int err := db.GetPool().QueryRow(c.Request.Context(), "SELECT COUNT(*) FROM users").Scan(&count) @@ -24,18 +20,6 @@ func CheckFirstUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"is_first_user": count == 0, "total_users": count}) } -func InitAuth(secret string) { - jwtSecret = []byte(secret) -} - -type Claims struct { - UserID int64 `json:"user_id"` - Email string `json:"email"` - Username string `json:"username"` - IsAdmin bool `json:"is_admin"` - jwt.RegisteredClaims -} - func Login(c *gin.Context) { var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -60,20 +44,7 @@ func Login(c *gin.Context) { return } - expirationTime := time.Now().Add(24 * time.Hour) - claims := &Claims{ - UserID: user.ID, - Email: user.Email, - Username: user.Username, - IsAdmin: user.IsAdmin, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(expirationTime), - IssuedAt: jwt.NewNumericDate(time.Now()), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString(jwtSecret) + tokenString, err := auth.GenerateToken(user.ID, user.Email, user.Username, user.IsAdmin) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to generate token"}) return @@ -127,20 +98,7 @@ func Register(c *gin.Context) { return } - expirationTime := time.Now().Add(24 * time.Hour) - claims := &Claims{ - UserID: user.ID, - Email: user.Email, - Username: user.Username, - IsAdmin: user.IsAdmin, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(expirationTime), - IssuedAt: jwt.NewNumericDate(time.Now()), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString(jwtSecret) + tokenString, err := auth.GenerateToken(user.ID, user.Email, user.Username, user.IsAdmin) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to generate token"}) return @@ -160,7 +118,7 @@ func GetCurrentUser(c *gin.Context) { return } - claims := userVal.(*Claims) + claims := userVal.(*auth.Claims) var user models.User err := db.GetPool().QueryRow(c.Request.Context(), ` @@ -176,8 +134,3 @@ func GetCurrentUser(c *gin.Context) { c.JSON(http.StatusOK, user) } - -func init() { - cfg := config.Load() - InitAuth(cfg.SecretKey) -} diff --git a/backend/internal/handlers/auth_test.go b/backend/internal/handlers/auth_test.go new file mode 100644 index 0000000..7586995 --- /dev/null +++ b/backend/internal/handlers/auth_test.go @@ -0,0 +1,108 @@ +package handlers + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/auth" + "github.com/rss2/backend/internal/models" +) + +func init() { + gin.SetMode(gin.TestMode) + auth.SetJWTSecret("test-secret-key-12345") +} + +func TestLoginInvalidRequest(t *testing.T) { + router := gin.New() + router.POST("/auth/login", Login) + + body := []byte(`{}`) + req, _ := http.NewRequest("POST", "/auth/login", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } + + var resp models.ErrorResponse + json.Unmarshal(w.Body.Bytes(), &resp) + + if resp.Error != "Invalid request" { + t.Errorf("expected 'Invalid request', got %s", resp.Error) + } +} + +func TestLoginInvalidCredentials(t *testing.T) { + router := gin.New() + router.POST("/auth/login", Login) + + body := []byte(`{"email":"invalid@test.com","password":"wrongpass"}`) + req, _ := http.NewRequest("POST", "/auth/login", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected status 401, got %d", w.Code) + } +} + +func TestRegisterInvalidRequest(t *testing.T) { + router := gin.New() + router.POST("/auth/register", Register) + + body := []byte(`{}`) + req, _ := http.NewRequest("POST", "/auth/register", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } +} + +func TestCheckFirstUser(t *testing.T) { + router := gin.New() + router.GET("/auth/check-first-user", CheckFirstUser) + + req, _ := http.NewRequest("GET", "/auth/check-first-user", nil) + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Errorf("expected status 200, got %d", w.Code) + } + + var resp map[string]interface{} + json.Unmarshal(w.Body.Bytes(), &resp) + + if _, ok := resp["is_first_user"]; !ok { + t.Error("expected is_first_user in response") + } +} + +func TestGetCurrentUserUnauthorized(t *testing.T) { + router := gin.New() + router.GET("/auth/me", GetCurrentUser) + + req, _ := http.NewRequest("GET", "/auth/me", nil) + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected status 401, got %d", w.Code) + } +} diff --git a/backend/internal/middleware/auth_test.go b/backend/internal/middleware/auth_test.go new file mode 100644 index 0000000..abd3acc --- /dev/null +++ b/backend/internal/middleware/auth_test.go @@ -0,0 +1,141 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/auth" +) + +func init() { + gin.SetMode(gin.TestMode) + auth.SetJWTSecret("test-secret-key-12345") +} + +func TestAuthRequiredNoHeader(t *testing.T) { + router := gin.New() + router.Use(AuthRequired()) + router.GET("/protected", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + req, _ := http.NewRequest("GET", "/protected", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected status 401, got %d", w.Code) + } +} + +func TestAuthRequiredInvalidToken(t *testing.T) { + router := gin.New() + router.Use(AuthRequired()) + router.GET("/protected", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + req, _ := http.NewRequest("GET", "/protected", nil) + req.Header.Set("Authorization", "Bearer invalid-token") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected status 401, got %d", w.Code) + } +} + +func TestAuthRequiredValidToken(t *testing.T) { + router := gin.New() + router.Use(AuthRequired()) + router.GET("/protected", func(c *gin.Context) { + userVal, _ := c.Get("user") + claims := userVal.(*auth.Claims) + c.JSON(200, gin.H{"user_id": claims.UserID}) + }) + + token, _ := auth.GenerateToken(42, "test@test.com", "testuser", false) + + req, _ := http.NewRequest("GET", "/protected", nil) + req.Header.Set("Authorization", "Bearer "+token) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Errorf("expected status 200, got %d", w.Code) + } +} + +func TestAdminRequiredNotAdmin(t *testing.T) { + router := gin.New() + router.Use(AuthRequired()) + router.Use(AdminRequired()) + router.GET("/admin", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + token, _ := auth.GenerateToken(1, "test@test.com", "testuser", false) + + req, _ := http.NewRequest("GET", "/admin", nil) + req.Header.Set("Authorization", "Bearer "+token) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusForbidden { + t.Errorf("expected status 403, got %d", w.Code) + } +} + +func TestAdminRequiredIsAdmin(t *testing.T) { + router := gin.New() + router.Use(AuthRequired()) + router.Use(AdminRequired()) + router.GET("/admin", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + token, _ := auth.GenerateToken(1, "test@test.com", "adminuser", true) + + req, _ := http.NewRequest("GET", "/admin", nil) + req.Header.Set("Authorization", "Bearer "+token) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Errorf("expected status 200, got %d", w.Code) + } +} + +func TestCORSMiddleware(t *testing.T) { + router := gin.New() + router.Use(CORSMiddleware()) + router.GET("/test", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + req, _ := http.NewRequest("GET", "/test", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Header().Get("Access-Control-Allow-Origin") == "" { + t.Error("expected CORS header to be set") + } +} + +func TestCORSPreflight(t *testing.T) { + router := gin.New() + router.Use(CORSMiddleware()) + router.GET("/test", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + + req, _ := http.NewRequest("OPTIONS", "/test", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusNoContent { + t.Errorf("expected status 204 for preflight, got %d", w.Code) + } +} diff --git a/bugs.md b/bugs.md new file mode 100644 index 0000000..1f2b08b --- /dev/null +++ b/bugs.md @@ -0,0 +1,448 @@ +# Análisis de Errores e Inconsistencias - RSS2 + +Fecha de análisis: 2026-04-01 + +--- + +## Resumen Ejecutivo + +Se han identificado **13 problemas** clasificados en: +- 3 Errores Críticos +- 7 Inconsistencias +- 3 Warnings de Arquitectura + +--- + +## 1. Errores Críticos + +### 1.1 Rate Limiting No Implementado + +**Ubicación:** `backend/internal/middleware/auth.go:104-108` + +**Descripción:** +La función existe pero no hace nada: +```go +func RateLimitMiddleware(requestsPerMinute int) gin.HandlerFunc { + return func(c *gin.Context) { + c.Next() + } +} +``` + +**Problema:** +No implementa ningún tipo de limitación de rate. Vulnerable a ataques de fuerza bruta y DoS. + +**Impacto:** Alto - Seguridad comprometida. + +--- + +### 1.2 CORS Demasiado Permisivo + +**Ubicación:** `backend/internal/middleware/auth.go:77-90` + +**Descripción:** +```go +c.Writer.Header().Set("Access-Control-Allow-Origin", "*") +``` + +**Problema:** +Permite cualquier origen, lo cual es inseguro para APIs de producción. Permite ataques CSRF y expone datos a orígenes maliciosos. + +**Impacto:** Alto - Seguridad comprometida. + +--- + +### 1.3 Secret Key Por Defecto Hardcodeada + +**Ubicación:** `backend/internal/config/config.go:32` + +**Descripción:** +```go +SecretKey: getEnv("SECRET_KEY", "change-this-secret-key"), +``` + +**Problema:** +Si no se proporciona la variable de entorno, la aplicación usa una clave por defecto conocida. Cualquier despliegue sin configurar esta variable es vulnerable. + +**Impacto:** Crítico - Compromiso total de autenticación. + +--- + +## 2. Inconsistencias + +### 2.1 Mapeo de Campos Inconsistente (Snake Case vs Camel Case) + +**Descripción:** +La aplicación usa convenciones de nomenclatura diferentes en distintas capas: + +| Capa | Estructura | Ejemplo | +|------|------------|---------| +| Models | Snake case | `Title`, `Summary`, `ImageURL` | +| Handlers | Pascal case | `Titulo`, `Resumen`, `ImagenURL` | +| Frontend TS | camelCase | `titulo`, `resumen`, `imagen_url` | + +**Archivos afectados:** +- `backend/internal/models/models.go:7-19` +- `backend/internal/handlers/news.go:14-28` +- `frontend/src/services/api.ts:20-34` + +**Problema:** +Dificulta el mantenimiento y genera potenciales errores de serialización. + +--- + +### 2.2 Idioma de Traducción Hardcodeado + +**Descripción:** +El idioma destino de traducción (`es`) está hardcodeado en múltiples lugares: + +**news.go:67:** +```go +where += " AND t.status = 'done' AND t.titulo_trad IS NOT NULL" +``` + +**news.go:96:** +```go +LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es' +``` + +**search.go:34-37:** +```go +if lang == "" { + lang = "es" +} +``` + +**Problema:** +No es configurable. Si se quiere traducir a otros idiomas, requiere cambios en múltiples archivos. + +--- + +### 2.3 Columna `role` en Users Sin映射 en Modelo + +**Descripción:** +Se añade una columna `role` a la tabla users: + +**cmd/server/main.go:41-48:** +```go +_, err = db.GetPool().Exec(ctx, ` + ALTER TABLE users ADD COLUMN IF NOT EXISTS role VARCHAR(20) DEFAULT 'user' +`) +``` + +**Pero el modelo User no tiene este campo:** + +**models/models.go:86-94:** +```go +type User struct { + ID int64 `json:"id"` + Email string `json:"email"` + Username string `json:"username"` + PasswordHash string `json:"-"` + IsAdmin bool `json:"is_admin"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +**Problema:** +La columna `role` nunca se usa realmente. Hay dos campos (`role` y `is_admin`) que pueden causar confusión. + +--- + +### 2.4 Feature No Implementada en Discovery + +**Ubicación:** `backend/cmd/discovery/main.go:129-133` + +**Descripción:** +```go +func findFeedLinksInHTML(baseURL string) ([]string, error) { + // Simple feed link finder - returns empty for now + // In production, use goquery to parse HTML and find RSS/Atom links + return []string{}, nil +} +``` + +**Problema:** +El worker de discovery no puede encontrar feeds RSS embebidos en páginas HTML. Solo detecta feeds directos. + +**Impacto:** Medio - Reduce la efectividad del descubrimiento de feeds. + +--- + +### 2.5 LoggerMiddleware No Funcional + +**Ubicación:** `backend/internal/middleware/auth.go:93-101` + +**Descripción:** +```go +func LoggerMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Next() + + status := c.Writer.Status() + if status >= 400 { + // Log error responses - pero no hace nada! + } + } +} +``` + +**Problema:** +El logging de errores está commented out. No se registra nada. + +--- + +### 2.6 Columnas de BD Diferentes a Modelos + +**Descripción:** +El handler `search.go` referencia columnas que no existen en los modelos: + +**search.go:91-92:** +```go +SELECT n.id, n.titulo, n.resumen, n.contenido, n.url, n.imagen, + n.feed_id, n.lang, n.categoria_id, n.pais_id, n.created_at, n.updated_at, +``` + +**Pero en models.go:7-19:** +```go +type News struct { + ID int64 `json:"id"` + Title string `json:"title"` + Summary string `json:"summary"` + Content string `json:"content"` + URL string `json:"url"` + ImageURL *string `json:"image_url"` + PublishedAt *time.Time `json:"published_at"` + Lang string `json:"lang"` + FeedID int64 `json:"feed_id"` + ... +} +``` + +**Problema:** +- Modelo usa `Title`, BD usa `titulo` +- Modelo usa `Summary`, BD usa `resumen` +- Modelo usa `ImageURL`, BD usa `imagen` +- Modelo usa `PublishedAt`, BD usa `fecha` + +Esto causa errores de serialización potenciales. + +--- + +### 2.7 Inicialización de Base de Datos Duplicada + +**Descripción:** +Existen dos formas de conectar a la base de datos: + +1. `db.Connect()` en `backend/internal/db/postgres.go:13` +2. `workers.Connect()` en `backend/internal/workers/db.go` + +**Problema:** +Códigos duplicados que mantienen conexiones separadas. Puede causar problemas de pool de conexiones. + +--- + +## 3. Warnings de Arquitectura + +### 3.1 Creación de Tablas en Runtime + +**Ubicación:** `backend/cmd/server/main.go:20-77` + +**Descripción:** +En lugar de usar migraciones formales, el servidor crea tablas al iniciar: + +```go +func initDB() { + _, err := db.GetPool().Exec(ctx, ` + CREATE TABLE IF NOT EXISTS entity_aliases (...) + `) + // más tablas... +} +``` + +**Problema:** +- No hay control de versiones +- No hay rollback +- Difícil de mantener en producción + +**Recomendación:** Usar golang-migrate o similar. + +--- + +### 3.2 No Hay Tests Unitarios + +**Descripción:** +No se encontraron archivos `*_test.go` en el código backend. + +**Problema:** +Sin tests, cualquier cambio puede romper funcionalidad existente sin detección. + +--- + +### 3.3 Pool de Conexiones Sin Validación Periódica + +**Ubicación:** `backend/internal/db/postgres.go` + +**Descripción:** +El pool se crea pero no hay health check periódico. + +**Problema:** +Las conexiones zombie pueden acumularse sin detección. + +--- + +## 4. Recomendaciones de Corrección + +### Alta Prioridad + +1. **Unificar JWT Secret:** Usar un único package para manejar el secret +2. **Implementar Rate Limiting:** Usar una librería como `golang.org/x/time/rate` +3. **Configurar CORS correctamente:** Usar variable de entorno para orígenes permitidos +4. **Forzar Secret Key:** Hacer requerida la variable `SECRET_KEY` + +### Media Prioridad + +5. **Traducir idioma configurable:** Pasar idioma como parámetro o configuración +6. **Usar migraciones:** Implementar golang-migrate +7. **Añadir tests:** Crear tests unitarios para handlers críticos +8. **Corregir mapeo de campos:** Estandarizar snake_case en toda la aplicación + +### Baja Prioridad + +9. **Implementar LoggerMiddleware:** Añadir logging real +10. **Implementar findFeedLinksInHTML:** Usar goquery para parsing HTML +11. **Consolidar DB:** Unificar inicialización de base de datos + +--- + +## 5. Estadísticas + +| Tipo | Cantidad | +|------|----------| +| Errores Críticos | 3 | +| Inconsistencias | 7 | +| Warnings | 3 | +| **Total** | **13** | + +--- + +## 6. Problemas Adicionales Detectados + +### 6.1 Mapeo de Campos Inconsistente (Frontend) + +**Descripción:** El frontend usa `snake_case` pero los modelos Go usan `PascalCase`. + +**Archivos:** +- `frontend/src/services/api.ts` - usa `titulo`, `resumen` +- `backend/internal/models/models.go` - usa `Title`, `Summary` + +--- + +### 6.2 Structs Duplicados para Mismo Recurso + +**Descripción:** `handlers/news.go` define `NewsResponse` diferente a `models/models.go:News`. + +--- + +### 6.3 Query Params Inconsistentes + +**Descripción:** Algunos handlers usan `category_id` (inglés) y otros `categoria_id` (español). + +--- + +### 6.4 Duplicación Config DB + +**Descripción:** API usa `DATABASE_URL`, workers usan `DB_HOST`, `DB_PORT`, etc. + +--- + +### 6.5 Sin Health Checks en Workers + +**Descripción:** Workers no implementan endpoint `/health`. + +--- + +### 6.6 Manejo de Errores Inconsistente + +**Descripción:** Mezcla de `gin.H{"error": ...}` y `models.ErrorResponse`. + +--- + +## 7. Errores y Problemas Críticos Detectados + +### 7.1 ~~Errores Ignorados Silenciosamente~~ **(RESUELTO)** + +**Descripción:** Múltiples lugares donde errores eran ignorados sin logging. + +**Ejemplos corregidos:** +- `admin.go:488-492` - Ahora verifica errores de `db.GetPool().Exec()` +- `admin.go:522` - Verifica error al actualizar estado + +**Impacto:** Fallos silenciosos que pasan desapercibidos. + +--- + +### 7.2 ~~Rutas Hardcodeadas~~ **(RESUELTO)** + +**Descripción:** Rutas de archivos hardcodeadas en el código. + +**Correcciones:** +- `admin.go:469,474,509` - Ahora usa `config.Load().DockerComposeDir` +- `server/main.go:113` - Ahora usa `cfg.WikiImagesPath` +- Añadidos nuevos campos en config: `DockerComposeDir`, `WikiImagesPath` + +--- + +## 7.3 Secret Key Por Defecto + +**Descripción:** `config.go:32` usa clave por defecto known. + +```go +SecretKey: getEnv("SECRET_KEY", "change-this-secret-key") +``` + +**Impacto:** Medio - Si no se configura, usar clave insegura. + +--- + +### 7.4 Rate Limiting No Implementado + +**Descripción:** `middleware/auth.go:104-108` tiene función vacía. + +**Impacto:** Alto - Vulnerable a ataques de fuerza bruta. + +--- + +### 7.5 CORS Demasiado Permisivo + +**Descripción:** `middleware/auth.go:79` permite cualquier origen. + +**Impacto:** Alto - Expuesto a ataques CSRF. + +--- + +## 8. Recomendaciones de Corrección (Actualizado) + +### Alta Prioridad (CRÍTICO) + +1. ~~Unificar JWT Secret~~ ✅ +2. Implementar Rate Limiting +3. ~~**Configurar CORS correctamente:** Usar variable de entorno~~ ✅ +4. ~~**Corregir manejo de errores:** No ignorar silenciosamente~~ ✅ +5. ~~**Eliminar rutas hardcodeadas**~~ ✅ + +### Media Prioridad + +7. Idioma traducciones configurable +8. Usar migraciones +9. Añadir tests +10. Corregir mapeo de campos + +### Baja Prioridad + +11. Implementar LoggerMiddleware +12. Implementar findFeedLinksInHTML +13. Consolidar DB +14. Estandarizar respuestas API +15. Añadir health checks diff --git a/frontend/package.json b/frontend/package.json index aecb734..8f7d884 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,10 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "preview": "vite preview" + "preview": "vite preview", + "test": "vitest", + "test:ui": "vitest --ui", + "test:coverage": "vitest --coverage" }, "dependencies": { "react": "^18.2.0", @@ -23,10 +26,16 @@ "@types/react": "^18.2.47", "@types/react-dom": "^18.2.18", "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-v8": "^1.2.0", + "@testing-library/react": "^14.1.2", + "@testing-library/jest-dom": "^6.2.0", + "@testing-library/user-event": "^14.5.2", "autoprefixer": "^10.4.16", + "jsdom": "^24.0.0", "postcss": "^8.4.33", "tailwindcss": "^3.4.1", "typescript": "^5.3.3", - "vite": "^5.0.11" + "vite": "^5.0.11", + "vitest": "^1.2.0" } } diff --git a/frontend/src/test/setup.ts b/frontend/src/test/setup.ts new file mode 100644 index 0000000..c44951a --- /dev/null +++ b/frontend/src/test/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom' diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 0000000..5e095b1 --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: './src/test/setup.ts', + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + }, + }, +}) diff --git a/remote-worker/.gitignore b/remote-worker/.gitignore new file mode 100644 index 0000000..ff8f540 --- /dev/null +++ b/remote-worker/.gitignore @@ -0,0 +1,7 @@ +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ +models/ +hf_cache/ +*.egg-info/ \ No newline at end of file diff --git a/remote-worker/Dockerfile b/remote-worker/Dockerfile new file mode 100644 index 0000000..466e9cc --- /dev/null +++ b/remote-worker/Dockerfile @@ -0,0 +1,36 @@ +FROM python:3.11-slim-bookworm + +RUN apt-get update && apt-get install -y --no-install-recommends \ + patchelf gcc git curl wget \ + && rm -rf /var/lib/apt/lists/* + +ENV PYTHONUNBUFFERED=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + TOKENIZERS_PARALLELISM=false \ + HF_HOME=/root/.cache/huggingface + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir --upgrade pip + +RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 + +RUN pip install --no-cache-dir \ + ctranslate2==3.24.0 \ + sentencepiece \ + transformers==4.36.0 \ + protobuf==3.20.3 \ + "numpy<2" \ + websocket-client + +RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ + -name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \ + xargs -I {} patchelf --clear-execstack {} || true + +COPY worker.py /app/worker.py + +ENV CT2_DEVICE=cuda +ENV CT2_COMPUTE_TYPE=float16 + +CMD ["python", "/app/worker.py"] \ No newline at end of file diff --git a/remote-worker/README.md b/remote-worker/README.md new file mode 100644 index 0000000..e924004 --- /dev/null +++ b/remote-worker/README.md @@ -0,0 +1,53 @@ +# Remote Translator Worker + +Worker remoto de traducción para RSS2. Se conecta al servidor via WebSocket y procesa trabajos de traducción automáticamente. + +## Requisitos + +- Docker con soporte GPU (NVIDIA) +- O Python 3.11+ con CUDA + +## Uso con Docker + +```bash +docker run -d --gpus all --name remote-worker \ + -e WORKER_API_KEY="tu_api_key" \ + -e WORKER_SERVER="ws://tu-servidor:8888/ws/worker" \ + -v ~/models:/app/models \ + tu-usuario/rss2-remote-worker +``` + +## Variables de Entorno + +| Variable | Default | Descripción | +|----------|---------|-------------| +| WORKER_API_KEY | (requerido) | API key del worker | +| WORKER_SERVER | ws://localhost:8080/ws/worker | URL del servidor WebSocket | +| CT2_DEVICE | cuda | Device: cuda o cpu | +| CT2_COMPUTE_TYPE | float16 | Tipo de computación | +| CT2_MODEL_PATH | /app/models/nllb-ct2 | Ruta del modelo | +| UNIVERSAL_MODEL | facebook/nllb-200-1.3B | Modelo de HuggingFace | +| WORKER_NAME | remote-worker | Nombre del worker | + +## Sin Docker + +```bash +pip install -r requirements.txt + +python worker.py --api-key tu_api_key --server ws://tu-servidor:8888/ws/worker +``` + +## Modelo + +La primera vez que ejecutas el worker, descargará automáticamente el modelo desde HuggingFace y lo convertirá a formato CTranslate2 (~5-10 min). + +Para acelerar instalaciones posteriores, puedes pre-descargar el modelo: +```bash +ct2-transformers-converter --model facebook/nllb-200-1.3B --output_dir ./models/nllb-ct2 --quantization int8 +``` + +## Construir imagen + +```bash +docker build -t rss2-remote-worker . +``` \ No newline at end of file diff --git a/remote-worker/requirements.txt b/remote-worker/requirements.txt new file mode 100644 index 0000000..4be6006 --- /dev/null +++ b/remote-worker/requirements.txt @@ -0,0 +1,8 @@ +ctranslate2==3.24.0 +sentencepiece +transformers==4.36.0 +protobuf==3.20.3 +numpy<2 +websocket-client +torch==2.1.0 +torchvision==0.16.0 \ No newline at end of file diff --git a/remote-worker/worker.py b/remote-worker/worker.py new file mode 100644 index 0000000..1b28cc4 --- /dev/null +++ b/remote-worker/worker.py @@ -0,0 +1,393 @@ +import os +import sys +import time +import json +import logging +import re +import threading +import hashlib +from typing import List, Optional + +import websocket + +import ctranslate2 +from transformers import AutoTokenizer + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s: %(message)s", + handlers=[logging.StreamHandler(sys.stdout)] +) +LOG = logging.getLogger("remote-translator") + +WORKER_NAME = os.environ.get("WORKER_NAME", "remote-worker") +WORKER_API_KEY = os.environ.get("WORKER_API_KEY", "") +WORKER_SERVER = os.environ.get("WORKER_SERVER", "ws://localhost:8080/ws/worker") +DEVICE = os.environ.get("CT2_DEVICE", "cpu") +MODEL_PATH = os.environ.get("CT2_MODEL_PATH", "/app/models/nllb-ct2") +COMPUTE_TYPE = os.environ.get("CT2_COMPUTE_TYPE", "int8") +UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-1.3B") + +LANG_CODE_MAP = { + "en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn", + "it": "ita_Latn", "pt": "por_Latn", "nl": "nld_Latn", "sv": "swe_Latn", + "da": "dan_Latn", "fi": "fin_Latn", "no": "nob_Latn", + "pl": "pol_Latn", "cs": "ces_Latn", "sk": "slk_Latn", + "sl": "slv_Latn", "hu": "hun_Latn", "ro": "ron_Latn", + "el": "ell_Grek", "ru": "rus_Cyrl", "uk": "ukr_Cyrl", + "tr": "tur_Latn", "ar": "arb_Arab", "fa": "pes_Arab", + "he": "heb_Hebr", "zh": "zho_Hans", "ja": "jpn_Jpan", + "ko": "kor_Hang", "vi": "vie_Latn", +} + +MAX_SRC_TOKENS = 512 +MAX_NEW_TOKENS = 512 +BODY_CHARS_CHUNK = 900 + +_tokenizer = None +_translator = None +_ws = None +_reconnect_delay = 5 +_running = True +_stats = {"jobs_completed": 0, "jobs_failed": 0} + + +def ensure_model(): + global _tokenizer, _translator + + if _translator: + return + + model_bin = os.path.join(MODEL_PATH, "model.bin") + + if not os.path.exists(model_bin): + LOG.info(f"CTranslate2 model not found at {MODEL_PATH}") + LOG.info("Downloading from HuggingFace...") + convert_model() + + LOG.info(f"Loading CTranslate2 model from {MODEL_PATH} on {DEVICE}") + + _translator = ctranslate2.Translator( + MODEL_PATH, + device=DEVICE, + compute_type=COMPUTE_TYPE, + ) + + _tokenizer = AutoTokenizer.from_pretrained(UNIVERSAL_MODEL) + LOG.info("CTranslate2 model loaded successfully") + + +def convert_model(): + import subprocess + + os.makedirs(MODEL_PATH, exist_ok=True) + + quantization = COMPUTE_TYPE if COMPUTE_TYPE != "auto" else "int8" + + LOG.info(f"Converting {UNIVERSAL_MODEL} to CTranslate2 format...") + + cmd = [ + "ct2-transformers-converter", + "--model", UNIVERSAL_MODEL, + "--output_dir", MODEL_PATH, + "--quantization", quantization, + "--force" + ] + + LOG.info(f"Running: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800) + + if result.returncode != 0: + LOG.error(f"Model conversion failed: {result.stderr}") + raise RuntimeError("Failed to convert model") + + LOG.info("Model conversion completed") + + +def clean_text(text: str) -> str: + if not text: + return "" + text = re.sub(r'<[^>]+>', '', text) + text = text.replace('', '') + text = text.replace(' ', ' ') + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('"', '"') + text = re.sub(r'\s+', ' ', text) + return text.strip() + + +def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: + if not texts: + return [] + + ensure_model() + + clean = [(t or "").strip() for t in texts] + if all(not t for t in clean): + return ["" for _ in clean] + + src_code = LANG_CODE_MAP.get(src, f"{src}_Latn") + tgt_code = LANG_CODE_MAP.get(tgt, "spa_Latn") + + try: + _tokenizer.src_lang = src_code + except Exception: + pass + + sources = [] + for t in clean: + if t: + ids = _tokenizer.encode(t, truncation=True, max_length=MAX_SRC_TOKENS) + tokens = _tokenizer.convert_ids_to_tokens(ids) + sources.append(tokens) + else: + sources.append([]) + + target_prefix = [[tgt_code]] * len(sources) + + results = _translator.translate_batch( + sources, + target_prefix=target_prefix, + beam_size=2, + max_decoding_length=MAX_NEW_TOKENS, + repetition_penalty=2.0, + no_repeat_ngram_size=3, + ) + + translated = [] + for result in results: + try: + if result.hypotheses and len(result.hypotheses) > 0: + hyp = result.hypotheses[0] + if isinstance(hyp, list) and len(hyp) > 0: + first_hyp = hyp[0] + if isinstance(first_hyp, dict) and "token_ids" in first_hyp: + tokens = first_hyp["token_ids"] + text = _tokenizer.decode(tokens) + translated.append(text.strip()) + elif isinstance(first_hyp, str): + token_strings = hyp[1:] if len(hyp) > 1 else [] + if token_strings: + text = _tokenizer.convert_tokens_to_string(token_strings) + translated.append(text.strip()) + else: + translated.append("") + else: + translated.append("") + else: + translated.append("") + else: + translated.append("") + except Exception as e: + LOG.error(f"Error processing result: {e}") + translated.append("") + + return translated + + +def split_body_into_chunks(text: str) -> List[str]: + text = (text or "").strip() + if len(text) <= BODY_CHARS_CHUNK: + return [text] if text else [] + + parts = re.split(r'(\n\n+|(?<=[\.\!\?؛؟。])\s+)', text) + chunks = [] + current = "" + + for part in parts: + if not part: + continue + if len(current) + len(part) <= BODY_CHARS_CHUNK: + current += part + else: + if current.strip(): + chunks.append(current.strip()) + current = part + if current.strip(): + chunks.append(current.strip()) + + return chunks if chunks else [text] + + +def translate_body_long(src: str, tgt: str, body: str) -> str: + body = (body or "").strip() + if not body: + return "" + + chunks = split_body_into_chunks(body) + if len(chunks) == 1: + return translate_texts(src, tgt, [body])[0] + + translated_chunks = [] + for ch in chunks: + tr = translate_texts(src, tgt, [ch])[0] + translated_chunks.append(tr) + + return " ".join(translated_chunks) + + +def process_job(job: dict) -> dict: + job_id = job.get("id") + lang_from = job.get("lang_from", "en") + lang_to = job.get("lang_to", "es") + title = job.get("title", "") + summary = job.get("summary", "") + + LOG.info(f"Processing job {job_id}: {lang_from} -> {lang_to}") + + if lang_from == lang_to: + return { + "job_id": job_id, + "title_trad": title, + "summary_trad": summary, + "error": "" + } + + try: + title_tr = translate_texts(lang_from, lang_to, [title])[0] + title_tr = clean_text(title_tr) or title + + summary_tr = "" + if summary: + summary_tr = translate_body_long(lang_from, lang_to, summary) + summary_tr = clean_text(summary_tr) or summary + + _stats["jobs_completed"] += 1 + + return { + "job_id": job_id, + "title_trad": title_tr, + "summary_trad": summary_tr, + "error": "" + } + except Exception as e: + LOG.error(f"Translation error for job {job_id}: {e}") + _stats["jobs_failed"] += 1 + return { + "job_id": job_id, + "title_trad": "", + "summary_trad": "", + "error": str(e) + } + + +def send_message(msg: dict): + if _ws and _ws.sock and _ws.sock.connected: + try: + _ws.send(json.dumps(msg)) + except Exception as e: + LOG.error(f"Send error: {e}") + + +def on_message(ws, message): + try: + msg = json.loads(message) + except: + LOG.error(f"Invalid JSON: {message}") + return + + msg_type = msg.get("type") + + if msg_type == "job": + job = msg.get("job", {}) + result = process_job(job) + send_message({ + "type": "result", + "result": result + }) + LOG.info(f"Sent result for job {result['job_id']}") + + elif msg_type == "ping": + send_message({"type": "heartbeat"}) + + elif msg_type == "ack": + LOG.debug(f"Server ACK: {msg.get('message', '')}") + + elif msg_type == "error": + LOG.error(f"Server error: {msg.get('message', '')}") + + +def on_error(ws, error): + LOG.error(f"WebSocket error: {error}") + + +def on_close(ws, close_status_code, close_msg): + global _reconnect_delay + LOG.warning(f"WebSocket closed: {close_status_code} - {close_msg}") + LOG.info(f"Reconnecting in {_reconnect_delay}s...") + + +def on_open(ws): + global _reconnect_delay + LOG.info("Connected to server") + _reconnect_delay = 5 + + send_message({ + "type": "register", + "capabilities": DEVICE, + "worker_name": WORKER_NAME + }) + + +def stats_reporter(): + while _running: + time.sleep(60) + LOG.info(f"Stats: completed={_stats['jobs_completed']}, failed={_stats['jobs_failed']}") + + +def connect(): + global _ws, _reconnect_delay + + while _running: + try: + if not WORKER_API_KEY: + LOG.error("WORKER_API_KEY not set") + time.sleep(60) + continue + + ws_url = f"{WORKER_SERVER}?api_key={WORKER_API_KEY}" + + _ws = websocket.WebSocketApp( + ws_url, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close, + header={"X-API-Key": WORKER_API_KEY} + ) + + LOG.info(f"Connecting to {WORKER_SERVER}...") + _ws.run_forever(ping_interval=30, ping_timeout=10) + + except Exception as e: + LOG.error(f"Connection error: {e}") + + LOG.info(f"Waiting {_reconnect_delay}s before reconnect...") + time.sleep(_reconnect_delay) + _reconnect_delay = min(_reconnect_delay * 2, 60) + + +def main(): + global _running + + if not WORKER_API_KEY: + LOG.error("WORKER_API_KEY environment variable is required") + sys.exit(1) + + LOG.info(f"Remote translator worker starting...") + LOG.info(f"Server: {WORKER_SERVER}") + LOG.info(f"Device: {DEVICE}") + LOG.info(f"Model: {MODEL_PATH}") + + ensure_model() + + stats_thread = threading.Thread(target=stats_reporter, daemon=True) + stats_thread.start() + + connect() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 6769d61..6a72361 100755 --- a/requirements.txt +++ b/requirements.txt @@ -30,4 +30,7 @@ pydyf==0.10.0 redis>=5.0.0 qdrant-client==1.11.0 feedfinder2>=0.0.4 +pytest>=8.0.0 +pytest-cov>=4.1.0 +pytest-mock>=3.12.0 From ecd7a3cdf8e2e913e87a08f84cde6b672bcf7012 Mon Sep 17 00:00:00 2001 From: jlimolina Date: Sat, 4 Apr 2026 03:17:37 +0200 Subject: [PATCH 15/21] mejora en el modelo de traduccion --- .docker-compose-hooks.sh | 39 ++ .docker-compose-validate.sh | 37 ++ .env.backup.20260403_232109 | 5 + .env.backup.20260403_232115 | 5 + .env.backup.20260403_232957 | 92 ++++ .env.backup.20260403_233004 | 92 ++++ .env.backup.20260403_233140 | 92 ++++ .env.backup.20260404_000245 | 92 ++++ .gitignore | 143 +++-- AGENTS.md | 408 +++++++++++++++ DEPLOY.md | 385 ++++++++++++-- README.md | 467 ++++++++++++++++- README_DEPLOY.md | 220 ++++++++ alias.sh | 128 +++++ backend/internal/handlers/admin.go | 44 +- docker-compose.yml | 117 +++-- docker-compose.yml.backup | 802 +++++++++++++++++++++++++++++ generate_secure_credentials.sh | 50 +- pre-deploy.sh | 411 +++++++++++++++ prepare_model.sh | 60 +++ workers/ctranslator_worker.py | 341 ++++++++---- 21 files changed, 3708 insertions(+), 322 deletions(-) create mode 100755 .docker-compose-hooks.sh create mode 100755 .docker-compose-validate.sh create mode 100644 .env.backup.20260403_232109 create mode 100644 .env.backup.20260403_232115 create mode 100644 .env.backup.20260403_232957 create mode 100644 .env.backup.20260403_233004 create mode 100644 .env.backup.20260403_233140 create mode 100644 .env.backup.20260404_000245 create mode 100644 AGENTS.md create mode 100644 README_DEPLOY.md create mode 100755 alias.sh create mode 100644 docker-compose.yml.backup create mode 100755 pre-deploy.sh create mode 100644 prepare_model.sh diff --git a/.docker-compose-hooks.sh b/.docker-compose-hooks.sh new file mode 100755 index 0000000..e72239f --- /dev/null +++ b/.docker-compose-hooks.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Hook para docker compose - Se ejecuta automáticamente +# Copia este archivo al inicio de tu .bashrc o .bash_profile + +# Función de validación automática +validate_credentials() { + # Verificar .env + if [ ! -f .env ]; then + echo "🔑 Generando credenciales..." + ./generate_secure_credentials.sh --force + return $? + fi + + # Verificar contraseñas vacías + for var in POSTGRES_PASSWORD REDIS_PASSWORD DB_PASS; do + val=$(grep "^${var}=" .env | cut -d'=' -f2 | tr -d ' ') + if [ -z "$val" ] || [ "$val" = "change_*" ]; then + echo "🔑 Generando credenciales para $var..." + ./generate_secure_credentials.sh --force + return $? + fi + done +} + +# Alias para docker compose con validación automática +alias docker="docker" +alias "docker compose"="docker compose --env-file .env" + +# Función helper: docker compose up con validación +docker_compose_up() { + echo "🔐 Validando credenciales..." + validate_credentials + + echo "🚀 Iniciando servicios..." + docker compose up -d +} + +# Exportar alias +alias docker_compose_up="docker_compose_up" diff --git a/.docker-compose-validate.sh b/.docker-compose-validate.sh new file mode 100755 index 0000000..3d4b87e --- /dev/null +++ b/.docker-compose-validate.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Validador automático de credenciales para docker compose +# Se ejecuta automáticamente antes de iniciar los servicios + +set -e + +# Colores +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}🔐 Validando credenciales antes de iniciar...${NC}" + +# Verificar archivo .env +if [ ! -f .env ]; then + echo -e "${RED}❌ ERROR: Archivo .env no encontrado${NC}" + echo -e "${YELLOW}💡 Generando credenciales...${NC}" + ./generate_secure_credentials.sh --force + exit $? +fi + +# Verificar credenciales CRÍTICAS +for var in POSTGRES_PASSWORD REDIS_PASSWORD DB_PASS; do + value=$(grep "^${var}=" .env 2>/dev/null | cut -d'=' -f2 | tr -d ' ') + + if [ -z "$value" ] || [ "$value" = "change_this_to_a_long_random_string" ] || [ "$value" = "change_this_password" ]; then + echo -e "${RED}❌ ERROR: $var está vacía o tiene valor por defecto${NC}" + echo -e "${YELLOW}💡 Generando credenciales seguras...${NC}" + ./generate_secure_credentials.sh --force + exit $? + fi +done + +echo -e "${GREEN}✅ Todas las credenciales están definidas${NC}" +exit 0 diff --git a/.env.backup.20260403_232109 b/.env.backup.20260403_232109 new file mode 100644 index 0000000..cfbcab5 --- /dev/null +++ b/.env.backup.20260403_232109 @@ -0,0 +1,5 @@ +POSTGRES_PASSWORD=test_pass_123456 +REDIS_PASSWORD=test_redis_123456 +DB_PASS=test_pass_123456 +SECRET_KEY=test_secret_key +GRAFANA_PASSWORD=test_grafana_123456 diff --git a/.env.backup.20260403_232115 b/.env.backup.20260403_232115 new file mode 100644 index 0000000..cfbcab5 --- /dev/null +++ b/.env.backup.20260403_232115 @@ -0,0 +1,5 @@ +POSTGRES_PASSWORD=test_pass_123456 +REDIS_PASSWORD=test_redis_123456 +DB_PASS=test_pass_123456 +SECRET_KEY=test_secret_key +GRAFANA_PASSWORD=test_grafana_123456 diff --git a/.env.backup.20260403_232957 b/.env.backup.20260403_232957 new file mode 100644 index 0000000..4f4afc9 --- /dev/null +++ b/.env.backup.20260403_232957 @@ -0,0 +1,92 @@ +# ================================================================================== +# CONFIGURACIÓN SEGURA - Generado automáticamente +# Fecha: 2026-04-03 23:21:15 +# ================================================================================== +# +# IMPORTANTE: +# - NO compartas este archivo +# - Guarda las credenciales en un gestor de contraseñas +# - Añade .env al .gitignore +# +# ================================================================================== + +# ================================================================================== +# DATABASE CONFIGURATION - PostgreSQL +# ================================================================================== +POSTGRES_DB=rss +POSTGRES_USER=rss +POSTGRES_PASSWORD=npti6eFCYsAv71LcJKasnpEAhtHgoD4a + +DB_NAME=rss +DB_USER=rss +DB_PASS=npti6eFCYsAv71LcJKasnpEAhtHgoD4a +DB_HOST=db +DB_PORT=5432 +DB_WRITE_HOST=db +DB_READ_HOST=db-replica + +# ================================================================================== +# REDIS CONFIGURATION - Con autenticación +# ================================================================================== +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=pmBJtSBMiYGptknv5pXa163kUR946V25 + +# ================================================================================== +# APPLICATION SECRETS +# ================================================================================== +SECRET_KEY=4a60ebda58f0716b846de1758e63e209e33070d0dbf8ac3c4ecd0c216e6bb4aa + +# ================================================================================== +# MONITORING - Grafana +# ================================================================================== +GRAFANA_PASSWORD=oTkxxwKCNB25r0unvtNi8AQt + +# ================================================================================== +# EXTERNAL SERVICES +# ================================================================================== +ALLTALK_URL=http://host.docker.internal:7851 + +# ================================================================================== +# AI MODELS & WORKERS +# ================================================================================== +RSS_MAX_WORKERS=3 +TARGET_LANGS=es +TRANSLATOR_BATCH=128 +ENQUEUE=300 + +# RSS Ingestor Configuration +RSS_POKE_INTERVAL_MIN=15 +RSS_MAX_FAILURES=10 +RSS_FEED_TIMEOUT=60 + +# URL Feed Discovery Worker +URL_DISCOVERY_INTERVAL_MIN=15 +URL_DISCOVERY_BATCH_SIZE=10 +MAX_FEEDS_PER_URL=5 + +# CTranslate2 / AI Model Paths +CT2_MODEL_PATH=/app/models/nllb-ct2 +CT2_DEVICE=cuda +CT2_COMPUTE_TYPE=int8_float16 +UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M + +# Embeddings +EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +EMB_BATCH=64 +EMB_DEVICE=cuda + +# NER +NER_LANG=es +NER_BATCH=64 + +# Flask / Gunicorn +GUNICORN_WORKERS=8 +FLASK_DEBUG=0 + +# Qdrant Configuration +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +QDRANT_COLLECTION_NAME=news_vectors +QDRANT_BATCH_SIZE=100 +QDRANT_SLEEP_IDLE=30 diff --git a/.env.backup.20260403_233004 b/.env.backup.20260403_233004 new file mode 100644 index 0000000..4f4afc9 --- /dev/null +++ b/.env.backup.20260403_233004 @@ -0,0 +1,92 @@ +# ================================================================================== +# CONFIGURACIÓN SEGURA - Generado automáticamente +# Fecha: 2026-04-03 23:21:15 +# ================================================================================== +# +# IMPORTANTE: +# - NO compartas este archivo +# - Guarda las credenciales en un gestor de contraseñas +# - Añade .env al .gitignore +# +# ================================================================================== + +# ================================================================================== +# DATABASE CONFIGURATION - PostgreSQL +# ================================================================================== +POSTGRES_DB=rss +POSTGRES_USER=rss +POSTGRES_PASSWORD=npti6eFCYsAv71LcJKasnpEAhtHgoD4a + +DB_NAME=rss +DB_USER=rss +DB_PASS=npti6eFCYsAv71LcJKasnpEAhtHgoD4a +DB_HOST=db +DB_PORT=5432 +DB_WRITE_HOST=db +DB_READ_HOST=db-replica + +# ================================================================================== +# REDIS CONFIGURATION - Con autenticación +# ================================================================================== +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=pmBJtSBMiYGptknv5pXa163kUR946V25 + +# ================================================================================== +# APPLICATION SECRETS +# ================================================================================== +SECRET_KEY=4a60ebda58f0716b846de1758e63e209e33070d0dbf8ac3c4ecd0c216e6bb4aa + +# ================================================================================== +# MONITORING - Grafana +# ================================================================================== +GRAFANA_PASSWORD=oTkxxwKCNB25r0unvtNi8AQt + +# ================================================================================== +# EXTERNAL SERVICES +# ================================================================================== +ALLTALK_URL=http://host.docker.internal:7851 + +# ================================================================================== +# AI MODELS & WORKERS +# ================================================================================== +RSS_MAX_WORKERS=3 +TARGET_LANGS=es +TRANSLATOR_BATCH=128 +ENQUEUE=300 + +# RSS Ingestor Configuration +RSS_POKE_INTERVAL_MIN=15 +RSS_MAX_FAILURES=10 +RSS_FEED_TIMEOUT=60 + +# URL Feed Discovery Worker +URL_DISCOVERY_INTERVAL_MIN=15 +URL_DISCOVERY_BATCH_SIZE=10 +MAX_FEEDS_PER_URL=5 + +# CTranslate2 / AI Model Paths +CT2_MODEL_PATH=/app/models/nllb-ct2 +CT2_DEVICE=cuda +CT2_COMPUTE_TYPE=int8_float16 +UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M + +# Embeddings +EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +EMB_BATCH=64 +EMB_DEVICE=cuda + +# NER +NER_LANG=es +NER_BATCH=64 + +# Flask / Gunicorn +GUNICORN_WORKERS=8 +FLASK_DEBUG=0 + +# Qdrant Configuration +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +QDRANT_COLLECTION_NAME=news_vectors +QDRANT_BATCH_SIZE=100 +QDRANT_SLEEP_IDLE=30 diff --git a/.env.backup.20260403_233140 b/.env.backup.20260403_233140 new file mode 100644 index 0000000..cabaffe --- /dev/null +++ b/.env.backup.20260403_233140 @@ -0,0 +1,92 @@ +# ================================================================================== +# CONFIGURACIÓN SEGURA - Generado automáticamente +# Fecha: 2026-04-03 23:30:04 +# ================================================================================== +# +# IMPORTANTE: +# - NO compartas este archivo +# - Guarda las credenciales en un gestor de contraseñas +# - Añade .env al .gitignore +# +# ================================================================================== + +# ================================================================================== +# DATABASE CONFIGURATION - PostgreSQL +# ================================================================================== +POSTGRES_DB=rss +POSTGRES_USER=rss +POSTGRES_PASSWORD=SI84OnqRT40FtdAeQro3eSJULKzFJ06F + +DB_NAME=rss +DB_USER=rss +DB_PASS=SI84OnqRT40FtdAeQro3eSJULKzFJ06F +DB_HOST=db +DB_PORT=5432 +DB_WRITE_HOST=db +DB_READ_HOST=db-replica + +# ================================================================================== +# REDIS CONFIGURATION - Con autenticación +# ================================================================================== +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=MvdWZlO1YCZ3QxE761bbvEd2qx44dO9j + +# ================================================================================== +# APPLICATION SECRETS +# ================================================================================== +SECRET_KEY=68d5b0297d44e88f5ab83720e0bdf8eeef2ceeccd72b8e46b7f1b47531571780 + +# ================================================================================== +# MONITORING - Grafana +# ================================================================================== +GRAFANA_PASSWORD=BVWprtQiW2KDmaDR8zZ2Br83 + +# ================================================================================== +# EXTERNAL SERVICES +# ================================================================================== +ALLTALK_URL=http://host.docker.internal:7851 + +# ================================================================================== +# AI MODELS & WORKERS +# ================================================================================== +RSS_MAX_WORKERS=3 +TARGET_LANGS=es +TRANSLATOR_BATCH=128 +ENQUEUE=300 + +# RSS Ingestor Configuration +RSS_POKE_INTERVAL_MIN=15 +RSS_MAX_FAILURES=10 +RSS_FEED_TIMEOUT=60 + +# URL Feed Discovery Worker +URL_DISCOVERY_INTERVAL_MIN=15 +URL_DISCOVERY_BATCH_SIZE=10 +MAX_FEEDS_PER_URL=5 + +# CTranslate2 / AI Model Paths +CT2_MODEL_PATH=/app/models/nllb-ct2 +CT2_DEVICE=cuda +CT2_COMPUTE_TYPE=int8_float16 +UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M + +# Embeddings +EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +EMB_BATCH=64 +EMB_DEVICE=cuda + +# NER +NER_LANG=es +NER_BATCH=64 + +# Flask / Gunicorn +GUNICORN_WORKERS=8 +FLASK_DEBUG=0 + +# Qdrant Configuration +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +QDRANT_COLLECTION_NAME=news_vectors +QDRANT_BATCH_SIZE=100 +QDRANT_SLEEP_IDLE=30 diff --git a/.env.backup.20260404_000245 b/.env.backup.20260404_000245 new file mode 100644 index 0000000..cecf467 --- /dev/null +++ b/.env.backup.20260404_000245 @@ -0,0 +1,92 @@ +# ================================================================================== +# CONFIGURACIÓN SEGURA - Generado automáticamente +# Fecha: 2026-04-03 23:31:40 +# ================================================================================== +# +# IMPORTANTE: +# - NO compartas este archivo +# - Guarda las credenciales en un gestor de contraseñas +# - Añade .env al .gitignore +# +# ================================================================================== + +# ================================================================================== +# DATABASE CONFIGURATION - PostgreSQL +# ================================================================================== +POSTGRES_DB=rss +POSTGRES_USER=rss +POSTGRES_PASSWORD=S5xwsI9HKjdaBWLpCkqp0mA0DJYZerpD + +DB_NAME=rss +DB_USER=rss +DB_PASS=S5xwsI9HKjdaBWLpCkqp0mA0DJYZerpD +DB_HOST=db +DB_PORT=5432 +DB_WRITE_HOST=db +DB_READ_HOST=db-replica + +# ================================================================================== +# REDIS CONFIGURATION - Con autenticación +# ================================================================================== +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=cVF79QuJulPO5auEhA2nNqv7mqAruzYh + +# ================================================================================== +# APPLICATION SECRETS +# ================================================================================== +SECRET_KEY=2eb7c221245bb8d896f83255188de614e59fae6b8dfbf16eee8a23c60dde4ffa + +# ================================================================================== +# MONITORING - Grafana +# ================================================================================== +GRAFANA_PASSWORD=R4J61V6OGLTCesrITwEvUPTm + +# ================================================================================== +# EXTERNAL SERVICES +# ================================================================================== +ALLTALK_URL=http://host.docker.internal:7851 + +# ================================================================================== +# AI MODELS & WORKERS +# ================================================================================== +RSS_MAX_WORKERS=3 +TARGET_LANGS=es +TRANSLATOR_BATCH=128 +ENQUEUE=300 + +# RSS Ingestor Configuration +RSS_POKE_INTERVAL_MIN=15 +RSS_MAX_FAILURES=10 +RSS_FEED_TIMEOUT=60 + +# URL Feed Discovery Worker +URL_DISCOVERY_INTERVAL_MIN=15 +URL_DISCOVERY_BATCH_SIZE=10 +MAX_FEEDS_PER_URL=5 + +# CTranslate2 / AI Model Paths +CT2_MODEL_PATH=/app/models/nllb-ct2 +CT2_DEVICE=cuda +CT2_COMPUTE_TYPE=int8_float16 +UNIVERSAL_MODEL=facebook/nllb-200-distilled-600M + +# Embeddings +EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 +EMB_BATCH=64 +EMB_DEVICE=cuda + +# NER +NER_LANG=es +NER_BATCH=64 + +# Flask / Gunicorn +GUNICORN_WORKERS=8 +FLASK_DEBUG=0 + +# Qdrant Configuration +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +QDRANT_COLLECTION_NAME=news_vectors +QDRANT_BATCH_SIZE=100 +QDRANT_SLEEP_IDLE=30 diff --git a/.gitignore b/.gitignore index 80d8412..028c9fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,80 +1,119 @@ +# ================================================================================== +# Python +# ================================================================================== __pycache__/ -*.pyc -*.pyo -*.pyd -.Python +*.py[cod] +*$py.class *.so -*.egg -*.egg-info/ -dist/ +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments venv/ -.env +env/ +ENV/ +.venv + +# ================================================================================== +# IDEs +# ================================================================================== .vscode/ .idea/ *.swp *.swo *~ .DS_Store -pgdata/ -pgdata-replica/ -pgdata.failed_restore/ -pgdata-replica.old.*/ -redis-data/ -hf_cache/ -models/nllb-ct2/ -qdrant_storage/ -*.log -*.db -*.sqlite -*.sqlite3 -data/ -*.mp4 -*.mp3 -*.wav -*.srt -*.tar.gz -*.zip -*.bak -*.old - +Thumbs.db # ================================================================================== -# SECURITY FILES - NEVER COMMIT THESE +# SECURITY - NEVER COMMIT # ================================================================================== -# Environment files with credentials .env -.env.backup* +.env.backup +.env.backup2 .env.generated .env.local .env.*.local -# Database backups +# Credentials +*.pem +*.key +*.crt +credentials.json +service-account.json + +# ================================================================================== +# RSS2 Data & Models (downloaded at runtime) +# ================================================================================== +# ML Models - downloaded/converted by workers +models/ +models/nllb-ct2/ +models/nllb-ct2-1.3b/ + +# Wikipedia images - downloaded by wiki_worker +data/wiki_images/ +data/backups/ + +# Database storage (PostgreSQL) +data/pgdata/ +pgdata/ +pgdata-replica/ +pgdata.failed_restore/ +pgdata-replica.old.*/ + +# Redis data +data/redis-data/ +redis-data/ + +# Qdrant vector storage +data/qdrant_storage/ +qdrant_storage/ + +# HuggingFace cache (downloaded models) +hf_cache/ + +# Monitoring data +monitoring/data/ + +# ================================================================================== +# Backup files +# ================================================================================== *.sql !init-db/*.sql !migrations/*.sql backup_*.sql - -# Redis backups *.rdb redis_backup_*.rdb - -# Qdrant backups qdrant_backup_*.tar.gz -# Docker compose with real credentials (if you create variations) +# ================================================================================== +# Temporary files +# ================================================================================== +tmp/ +temp/ +*.tmp +*.log + +# ================================================================================== +# Docker +# ================================================================================== docker-compose.override.yml -# Large Language Models -models/llm/ - -# User Uploads -static/uploads/ - -# Celery/Workers -celerybeat-schedule -celerybeat.pid - -# System/IDE -.DS_Store -Thumbs.db +# ================================================================================== +# Keep directory structure (optional - uncomment if needed) +# models/.gitkeep +# data/.gitkeep \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..63bafa1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,408 @@ +# AGENTS.md - RSS2 Development Guide + +## 🛠️ Build & Test Commands + +### Backend (Go) +```bash +cd backend && go mod tidy +cd backend && go build -o ../bin/server ./cmd/server +cd rss-ingestor-go && go build -o ../bin/rss-ingestor . + +# Single test +cd backend && go test ./internal/handlers -v -run TestLogin +cd backend && go test ./internal/auth -v -run TestGenerateToken +``` + +### Frontend +```bash +cd frontend && npm install +cd frontend && npm run dev # Development +cd frontend && npm run build # Production build +cd frontend && npm test # Run tests +cd frontend && npm run test:ui # UI mode +``` + +### Makefile +```bash +make build # Build all binaries +make clean # Remove binaries +make docker-build # Build Docker images +``` + +--- + +## 📁 Project Structure +``` +rss2/ +├── backend/ # Go API server + Workers +│ ├── cmd/ +│ │ ├── server/main.go # API REST principal (Gin) +│ │ ├── wiki_worker/main.go # Wikipedia integration +│ │ ├── qdrant/main.go # Vector indexing worker +│ │ ├── related/main.go # Related news worker +│ │ ├── topics/main.go # Country/topic matcher +│ │ ├── scraper/main.go # Deep scraping worker +│ │ ├── discovery/main.go # RSS feed discovery +│ │ └── topics/main.go # Topic matching +│ └── internal/ +│ ├── handlers/ # HTTP endpoints +│ ├── models/ # Data models +│ ├── auth/ # JWT authentication +│ ├── middleware/ # CORS, Auth middleware +│ ├── services/ # ML services (Translate, Embeddings, NER, Semantic Search) +│ ├── db/ # PostgreSQL connection +│ ├── cache/ # Redis connection +│ └── config/ # Configuration loading +├── frontend/ # React + TypeScript + Vite +│ └── src/ +│ ├── pages/ # Home, News, Search, Admin, Feeds, Stats +│ ├── components/ # Layout, UI components +│ └── services/ # API client +├── workers/ # Python workers +│ ├── ctranslator_worker.py # NLLB-200 translation (CTranslate2) +│ ├── ner_worker.py # Spacy NER + Topic extraction +│ ├── embeddings_worker.py # Sentence transformers embeddings +│ ├── cluster_worker.py # News clustering +│ ├── langdetect_worker.py # Language detection +│ ├── llm_categorizer_worker.py # Ollama LLM categorization +│ ├── simple_categorizer_worker.py +│ ├── simple_translator.py +│ ├── simple_translator_worker.py +│ ├── translation_worker.py +│ ├── translation_scheduler.py +│ └── remote_translator_worker.py # Remote GPU worker via WebSocket +├── data/ # PostgreSQL, Redis, Qdrant data +├── models/ # ML models (nllb-ct2) +├── hf_cache/ # HuggingFace cache +├── init-db/ # SQL migrations +├── monitoring/ # Prometheus + Grafana config +├── rss-ingestor-go/ # RSS crawler (Go) +└── docker-compose.yml # Full stack orchestration +``` + +--- + +## 🚀 Servicios del Sistema + +### Capa de Acceso y API (Puerto 8888) +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **nginx** | Nginx Alpine | Gateway y Proxy Inverso | +| **rss2_frontend** | React + Vite | Interfaz web responsiva | +| **backend-go** | Go + Gin | API REST principal | + +### Ingesta y Descubrimiento (Go) +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **rss-ingestor-go** | Go | Crawler RSS de alto rendimiento | +| **scraper** | Go | Scraper profundo con sanitización HTML | +| **discovery** | Go | Agente de descubrimiento de feeds RSS | + +### Procesamiento de Datos e IA +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **translator** | NLLB-200 (CPU) | Traducción neuronal CTranslate2 | +| **translator-gpu** | NLLB-200 (GPU) | Traducción acelerada CUDA | +| **remote-translator** | WebSocket | Worker GPU remoto | +| **embeddings** | S-Transformers | Generación de vectores semánticos | +| **ner** | Spacy + BERT | Reconocimiento de entidades (PER, ORG, LOC) | +| **llm-categorizer** | Ollama/Mistral | Clasificación con modelos de lenguaje | +| **wiki-worker** | Go | Integración Wikipedia + thumbnails | +| **topics** | Go | Matcher de países y temas | +| **related** | Go | Detección de noticias relacionadas | +| **qdrant-worker** | Go | Vectorización + búsqueda semántica | +| **cluster** | Python | Agrupación de noticias | +| **langdetect** | Python | Detección de idioma | +| **translation-scheduler** | Python | Creador de tareas de traducción | + +### Capa de Almacenamiento +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **db** | PostgreSQL 18 | Base de datos relacional | +| **qdrant** | Qdrant | Base de datos vectorial | +| **redis** | Redis 7 | Cache y colas de mensajes | + +### Monitoreo +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **prometheus** | Prometheus | Métricas del sistema | +| **grafana** | Grafana | Dashboard (puerto 3001) | +| **cadvisor** | cAdvisor | Monitoreo Docker | + +--- + +## 📝 Code Style Guidelines + +### Backend (Go) + +#### Imports +```go +package handlers + +import ( + "net/http" + "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/auth" + "github.com/rss2/backend/internal/models" +) +``` +**Order:** Standard → Third-party → Local packages +**Use:** `goimports` to auto-format + +#### Naming +- Packages: lowercase (`handlers`, `services`) +- Functions/Methods: camelCase (`GetNews`, `CreateUser`) +- Variables: camelCase (`userId`, `newsList`) +- Constants: UPPER_SNAKE_CASE (`MaxPageSize`) +- Types: PascalCase (`NewsResponse`, `User`) + +#### Error Handling +```go +func CreateResource(c *gin.Context) { + var req CreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse{ + Error: "Invalid request", + Message: err.Error(), + }) + return + } + // ... +} +``` +**Always return errors** | **Use `models.ErrorResponse`** + +#### Database +```go +err := db.GetPool().QueryRow(ctx, "SELECT * FROM users WHERE id = $1", id).Scan(&user) +if err != nil { + c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "User not found"}) + return +} +``` +**Use context** | **Named parameters ($1, $2)** + +#### Tests +```go +func TestLoginInvalidRequest(t *testing.T) { + router := gin.New() + router.POST("/auth/login", Login) + body := []byte(`{}`) + req, _ := http.NewRequest("POST", "/auth/login", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400, got %d", w.Code) + } +} +``` +**Use `gin.TestMode`** | **Test error paths** + +--- + +### Frontend (TypeScript + React) + +#### Imports +```tsx +import React, { useState, useEffect } from 'react' +import { Routes, Route } from 'react-router-dom' +import { Layout } from './components/layout/Layout' +import { api } from './services/api' +``` +**Order:** React → Router → Components → Services → Utils + +#### Components +```tsx +function NewsList() { + const [news, setNews] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { fetchNews(); }, []) + + const fetchNews = async () => { + try { + const res = await api.get('/news') + setNews(res.data) + } catch (err) { + setError(err.message) + } + } + + return ( +
    + {loading && } + {error && } +
    + ) +} +``` + +#### Types +```tsx +interface News { + id: number + title: string + summary: string + url: string + publishedAt: string +} +``` + +#### Styling +- Use Tailwind CSS utility classes +- Avoid inline styles +- Use `clsx` for conditional classes + +#### TypeScript Rules +- Use `strict: true` mode +- Avoid `any` - define interfaces +- Use type guards for narrowing +- Prefer optional chaining `?.` + +--- + +## 🔒 Security Guidelines + +1. **Never commit secrets** - Use `.env` in `.gitignore` +2. **Validate all inputs** - Use `go-playground/validator` +3. **Prepared statements** - Prevent SQL injection +4. **Rate limiting** - On sensitive endpoints +5. **HTTPS only** - Enforce in production + +### Variables de Entorno Críticas +```bash +POSTGRES_PASSWORD # Contraseña PostgreSQL +REDIS_PASSWORD # Contraseña Redis +DB_PASS # Contraseña para workers +SECRET_KEY # Key JWT +GRAFANA_PASSWORD # Dashboard password +``` + +--- + +## 🧪 Testing Best Practices + +### Backend +- Test error cases and edge cases +- Use `gin.TestMode` for HTTP tests + +### Frontend +- Test component rendering +- Test API integration +- Test error states +- Use vitest + +--- + +## 📦 Deployment + +```bash +# Generar credenciales seguras +./pre-deploy.sh --generate + +# Validar y desplegar +./pre-deploy.sh + +# O manualmente +docker compose up -d + +# Escalar workers de traducción +docker compose up -d --scale translator-gpu=4 +``` + +### Escalado de Workers GPU +```bash +# 1 worker GPU (8GB+ VRAM) +docker compose up -d --scale translator-gpu=1 + +# 2 workers GPU (16GB+ VRAM) +docker compose up -d --scale translator-gpu=2 + +# 4 workers GPU (32GB+ VRAM) +docker compose up -d --scale translator-gpu=4 +``` + +--- + +## 🔧 Configuración de Workers + +### Environment Variables Principales + +| Variable | Descripción | Default | +|----------|-------------|---------| +| `DB_HOST` | Host PostgreSQL | localhost | +| `DB_PORT` | Puerto PostgreSQL | 5432 | +| `DB_NAME` | Nombre base de datos | rss | +| `DB_USER` | Usuario PostgreSQL | rss | +| `DB_PASS` | Contraseña PostgreSQL | - | +| `TARGET_LANGS` | Idiomas destino | es | +| `TRANSLATOR_BATCH` | Tamaño de batch | 32 | +| `CT2_DEVICE` | Dispositivo (cpu/cuda) | cpu | +| `CT2_COMPUTE_TYPE` | Tipo (int8/float16) | int8 | +| `NER_BATCH` | Batch NER | 64 | +| `EMB_BATCH` | Batch embeddings | 64 | + +--- + +## 📊 Endpoints de API Principales + +### News +- `GET /api/news` - Listar noticias (paginado, filtros) +- `GET /api/news/:id` - Ver noticia con entidades +- `DELETE /api/news/:id` - Eliminar noticia (admin) + +### Feeds +- `GET /api/feeds` - Listar feeds +- `POST /api/feeds` - Crear feed (auth) +- `PUT /api/feeds/:id` - Actualizar feed (auth) +- `DELETE /api/feeds/:id` - Eliminar feed (auth) + +### Search +- `GET /api/search?q=...` - Búsqueda texto +- `GET /api/search?q=...&semantic=true` - Búsqueda semántica + +### Entities +- `GET /api/entities?tipo=persona` - Listar entidades (PER, ORG, LOC) + +### Admin +- `GET /api/admin/backup` - Backup SQL completo +- `GET /api/admin/backup/news` - Backup noticias (ZIP) +- `GET /api/admin/users` - Listar usuarios +- `POST /api/admin/workers/start` - Iniciar workers traducción +- `POST /api/admin/workers/stop` - Detener workers traducción +- `GET /api/admin/workers/status` - Estado de workers + +### Auth +- `POST /api/auth/login` - Iniciar sesión +- `POST /api/auth/register` - Registrarse +- `GET /api/auth/me` - Usuario actual (auth) + +### Stats +- `GET /api/stats` - Estadísticas globales + +--- + +## 🎯 Capabilities del Sistema + +1. **Enriquecimiento Wikipedia**: Detecta personas/orgs, descarga biografías e imágenes +2. **Categorización LLM**: Clasificación con Mistral-7B vía Ollama +3. **Búsqueda Semántica**: Qdrant vector search con mxbai-embed-large +4. **Traducción Neuronal**: NLLB-200 (600M-1.3B params) CPU/GPU +5. **NER**: Spacy es_core_news_lg para entidades nombradas +6. **Noticias Relacionadas**: Similitud coseno entre embeddings +7. **Detección de Idioma**: langdetect +8. **Clustering**: Agrupación automática de noticias +9. **WebSocket Workers**: Workers GPU remotos conectados por WS +10. **Backup Automático**: pg_dump con compresión ZIP + +--- + +## 📖 Documentación Adicional + +- [README.md](../README.md) - Guía de despliegue completo +- [DEPLOY.md](./DEPLOY.md) - Instrucciones de producción +- [SECURITY_GUIDE.md](./SECURITY_GUIDE.md) - Guía de seguridad +- [QUICKSTART_LLM.md](./QUICKSTART_LLM.md) - Configuración LLM +- [remote-worker.md](./remote-worker.md) - Workers remotos \ No newline at end of file diff --git a/DEPLOY.md b/DEPLOY.md index 9b1fa95..108c560 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,54 +1,353 @@ -# Deployment Guide +# 🚀 Guía de Despliegue RSS2 -This guide describes how to deploy the application to a new server. +## ⚡ Despliegue Rápido (5 minutos) -## Prerequisites +### Paso 1: Clonar el Proyecto -* **Linux Server** (Ubuntu 22.04+ recommended) -* **NVIDIA GPU**: Required for translation, embeddings, and NER services. -* **NVIDIA Container Toolkit**: Must be installed to allow Docker to access the GPU. -* **Docker** & **Docker Compose**: Latest versions. -* **Git**: To clone the repository. -* **External Service**: An instance of [AllTalk](https://github.com/erew123/alltalk_tts) running externally or on the host (port 7851 by default). +```bash +git clone https://github.com/tu-usuario/rss2.git +cd rss2 +``` -## Deployment Steps +### Paso 2: Generar Credenciales Seguras -1. **Clone the Repository** - ```bash - git clone - cd - ``` +```bash +./pre-deploy.sh --generate +``` -2. **Configure Environment Variables** - Copy the example configuration file: - ```bash - cp .env.example .env - ``` - Edit `.env` and set secure passwords and configuration: - ```bash - nano .env - ``` - * Change `POSTGRES_PASSWORD` and `DB_PASS` to a strong unique password. - * Change `SECRET_KEY` to a long random string. - * Verify `ALLTALK_URL` points to your AllTalk instance (default assumes host machine access). +**Salida esperada:** +``` +🔑 Generando credenciales seguras... -3. **Start the Services** - Run the following command to build and start the application: - ```bash - docker compose up -d --build - ``` +⚠️ IMPORTANTE: Guarda estas credenciales en un lugar seguro -4. **Database Initialization** - The database will automatically initialize on the first run using the scripts in `init-db/`. This may take a few minutes. Check logs with: - ```bash - docker compose logs -f db - ``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +POSTGRES_PASSWORD: S5xwsI9HKjdaBWLpCkqp0mA0DJYZerpD +REDIS_PASSWORD: cVF79QuJulPO5auEhA2nNqv7mqAruzYh +SECRET_KEY: 2eb7c221245bb8d896f83255188de614e59fae6b8dfbf16eee8a23c60dde4ffa +GRAFANA_PASSWORD: R4J61V6OGLTCesrITwEvUPTm +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` -5. **Verify Deployment** - Access the application at `http://:8001`. +**⚠️ IMPORTANTE:** Copia estas contraseñas y guárdalas en un gestor de contraseñas. ¡Nunca se volverán a mostrar! -## Important Notes +### Paso 3: Validar y Desplegar -* **Models**: The application mounts `./models` and `./hf_cache` to persist AI models. On the first run, it will attempt to download necessary models (NLLB, BERT, etc.), which requires significant bandwidth and time. -* **Data Persistence**: Database data is stored in `./pgdata` (mapped in docker-compose). Ensure this directory is backed up. -* **Security**: Ensure port 5432 (Postgres) and 6379 (Redis) are firewall-protected and not exposed to the public internet unless intended (Docker maps them to the host network). +```bash +./pre-deploy.sh +``` + +**El script te preguntará:** +``` +¿Deseas desplegar ahora? + 1) Desplegar automáticamente (recomendado) + 2) Solo validar y salir (modo manual) + Elige una opción: +``` + +**Opción A: Despliegue automático (Opción 1)** +- Elige `1` para que el script haga todo automáticamente +- Espera a que termine la construcción de las imágenes +- Espera a que inicien los servicios + +**Opción B: Despliegue manual (Opción 2)** +- Elige `2` para validar solo +- Luego ejecuta manualmente: + ```bash + docker compose up -d + ``` + +### Paso 4: Verificar Despliegue + +```bash +# Verificar estado de los servicios +docker compose ps + +# Deberías ver algo como: +# Name Status +# rss2_db Up (healthy) +# rss2_redis Up (healthy) +# rss2_backend_go Up (running) +# ... +``` + +### Paso 5: Acceder a la Aplicación + +```bash +# Aplicación web +http://localhost:8888 + +# API (prueba con curl) +curl http://localhost:8888/api/feeds +``` + +--- + +## 🔐 Seguridad + +### ¿Por qué es importante? + +**Sin seguridad (❌ MAL):** +```bash +docker compose up -d +WARN: The "DB_PASS" variable is not set. Defaulting to a blank string. +# Las contraseñas están vacías ¡PELIGRO! +``` + +**Con seguridad (✅ BIEN):** +```bash +./pre-deploy.sh --generate +./pre-deploy.sh +docker compose up -d +# ✅ Todo funciona correctamente +``` + +### Variables Críticas + +| Variable | Qué es | Por qué es importante | +|----------|--------|----------------------| +| `POSTGRES_PASSWORD` | Contraseña de PostgreSQL | Base de datos principal | +| `REDIS_PASSWORD` | Contraseña de Redis | Cache y colas | +| `DB_PASS` | Contraseña para workers | Conexiones de workers | +| `SECRET_KEY` | Key secreta de la app | JWT, encriptación | +| `GRAFANA_PASSWORD` | Contraseña de Grafana | Dashboard de monitoring | + +### Guardar tus Credenciales + +**Opción 1: Gestor de contraseñas (Recomendado)** +```bash +# Copia las credenciales y guárdalas en: +# - LastPass +# - 1Password +# - KeePass +# - Bitwarden +``` + +**Opción 2: Archivo seguro local** +```bash +# Crea un archivo seguro fuera del proyecto +echo "POSTGRES_PASSWORD=..." > ~/rss2-credentials.txt +echo "REDIS_PASSWORD=..." >> ~/rss2-credentials.txt +``` + +--- + +## 🛠️ Comandos Útiles + +### Verificar Credenciales +```bash +# Ver que las contraseñas están definidas +grep -E "^(POSTGRES_PASSWORD|REDIS_PASSWORD|DB_PASS)=" .env +``` + +### Reiniciar Servicios +```bash +# Reiniciar todos los servicios +docker compose restart + +# Reiniciar solo base de datos +docker compose restart db +``` + +### Escalar Workers +```bash +# Añadir más traductores (CPU) +docker compose up -d --scale translator=3 + +# Añadir más traductores (GPU) +docker compose up -d --scale translator-gpu=2 +``` + +### Ver Logs +```bash +# Ver logs en tiempo real +docker compose logs -f + +# Ver logs específicos +docker compose logs -f db +docker compose logs -f redis +docker compose logs -f backend-go +``` + +### Limpiar y Reiniciar +```bash +# Detener y eliminar todo (incluye datos) +docker compose down -v + +# Eliminar solo contenedores (mantiene datos) +docker compose down + +# Eliminar volumen específico +docker volume rm rss2_db +docker volume rm rss2_redis +``` + +--- + +## 🐛 Solución de Problemas + +### Problema: WARN sobre variables no definidas + +``` +WARN: The "DB_PASS" variable is not set. Defaulting to a blank string. +``` + +**Solución:** +```bash +# Ejecutar pre-deploy para generar credenciales +./pre-deploy.sh --generate +``` + +### Problema: Contraseña vacía en healthcheck + +``` +redis-cli: Authentication failed +``` + +**Solución:** +```bash +# Verificar REDIS_PASSWORD +grep REDIS_PASSWORD .env + +# Si está vacío, regenerar +./pre-deploy.sh --generate +``` + +### Problema: Port 8888 en uso + +``` +ERROR: failed to start service: port 8888 already in use +``` + +**Solución:** +```bash +# Cambiar puerto en docker-compose.yml +# O matar el proceso que usa el puerto +lsof -ti:8888 | xargs kill + +# O usar otro puerto +docker compose up -d --scale nginx=1 +``` + +### Problema: Base de datos no inicia + +``` +postgres: cannot connect to database +``` + +**Solución:** +```bash +# Ver logs de la base de datos +docker compose logs db + +# Reiniciar base de datos +docker compose restart db + +# Si persiste, eliminar y recrear (¡PERDERÁS DATOS!) +docker compose down -v +docker compose up -d db +``` + +--- + +## 📊 Estado de los Servicios + +### Servicios Críticos + +| Servicio | Puerto | Descripción | +|----------|--------|-------------| +| `nginx` | 8888 | Aplicación web | +| `backend-go` | 8080 | API interna | +| `db` | 5432 | PostgreSQL | +| `redis` | 6379 | Redis cache | +| `qdrant` | 6333 | Vector database | + +### Dashboard de Monitorización + +| Herramienta | Puerto | Acceso | +|-------------|--------|--------| +| Grafana | 3001 | http://127.0.0.1:3001 | +| Prometheus | 9090 | http://127.0.0.1:9090 | + +**Nota:** Accede a Grafana desde `127.0.0.1` (localhost) por seguridad. + +--- + +## 🔄 Actualización (Upgrade) + +### Actualizar a Nueva Versión + +```bash +# 1. Pull las nuevas imágenes +docker compose pull + +# 2. Backup de la base de datos +docker compose exec db pg_dump -U rss rss > backup.sql + +# 3. Parar servicios +docker compose down + +# 4. Actualizar código +git pull + +# 5. Reiniciar +docker compose up -d + +# 6. Verificar +curl http://localhost:8888/api/health +``` + +### Migrar Contraseñas + +Si tienes credenciales existentes y quieres mantenerlas: +```bash +# 1. Verificar .env existente +grep POSTGRES_PASSWORD .env + +# 2. Si quieres nuevas credenciales +./pre-deploy.sh --generate + +# 3. Si quieres mantener las existentes +# No hagas nada, las credenciales seguirán funcionando +``` + +--- + +## 📝 Archivos Importantes + +| Archivo | Propósito | +|---------|-----------| +| `.env` | Contraseñas y configuración (NO subir a Git) | +| `.env.example` | Plantilla (SÍ subir a Git) | +| `pre-deploy.sh` | Script de validación (SÍ subir a Git) | +| `generate_secure_credentials.sh` | Script de generación (SÍ subir a Git) | +| `docker-compose.yml` | Configuración de Docker (SÍ subir a Git) | + +--- + +## ✅ Checklist Final + +Antes de considerar tu despliegue como "listo", verifica: + +- [ ] `.env` tiene credenciales generadas (no por defecto) +- [ ] `.env` está en `.gitignore` +- [ ] Puedes acceder a http://localhost:8888 +- [ ] `docker compose ps` muestra todos los servicios "Up" +- [ ] La base de datos muestra "healthy" +- [ ] Los logs de la base de datos no tienen errores +- [ ] Redis responde a ping +- [ ] Qdrant está funcionando + +**¡Listo! Tu RSS2 está desplegado y funcionando.** 🎉 + +--- + +## 📚 Documentación Adicional + +- [README.md](./README.md) - Documentación general +- [SECURITY_GUIDE.md](./SECURITY_GUIDE.md) - Guía de seguridad +- [DOCKER.md](./DOCKER.md) - Configuración avanzada de Docker + +--- + +**RSS2** - *Transformando noticias en inteligencia con IA.* diff --git a/README.md b/README.md index 4019aaa..6758f51 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ RSS2 es una plataforma avanzada de agregación, traducción, análisis y vectori * **Traducción Neuronal de Alta Calidad**: Integración de NLLB-200 (vía CTranslate2) para traducir noticias de múltiples idiomas al español con precisión profesional. * **Inteligencia de Entidades (NER)**: Extracción y normalización automática de Personas, Organizaciones y Lugares para análisis de tendencias y mapeo de relaciones. * **Búsqueda de Noticias Relacionadas**: Algoritmos de similitud que agrupan noticias sobre el mismo tema automáticamente. +* **Detección de Idioma**: Identificación automática del idioma de origen para routing correcto a traducción. +* **Clustering de Noticias**: Agrupación automática de noticias relacionadas en eventos mediante embeddings. +* **Remote Workers GPU**: Workers de traducción GPUremotos conectados vía WebSocket para procesamiento distribuido. +* **Backup Automatizado**: Exportación completa de la base de datos en SQL o ZIP. --- @@ -22,14 +26,14 @@ El sistema se orquestra mediante Docker Compose y se divide en capas especializa ### Capa de Acceso y API | Servicio | Tecnología | Descripción | |---------|------------|-------------| -| **`nginx`** | Nginx Alpine | Gateway y Proxy Inverso (Puerto **8001**). | +| **`nginx`** | Nginx Alpine | Gateway y Proxy Inverso (Puerto **8888**). | | **`rss2_frontend`** | React + Vite | Interfaz web de usuario moderna y responsiva. | | **`backend-go`** | Go + Gin | API REST principal y gestión de lógica de negocio. | ### Ingesta y Descubrimiento (Go) | Servicio | Tecnología | Descripción | |---------|------------|-------------| -| **`rss-ingestor-go`** | Go | Crawler de alto rendimiento para feeds RSS. | +| **`rss-ingestor-go`** | Go | Crawler de alto rendimiento para feeds RSS (100 workers). | | **`scraper`** | Go | Scraper profundo con sanitización de HTML y extracción de texto. | | **`discovery`** | Go | Agente autónomo para descubrir nuevos feeds a partir de URLs. | @@ -37,13 +41,18 @@ El sistema se orquestra mediante Docker Compose y se divide en capas especializa | Servicio | Tecnología | Descripción | |---------|------------|-------------| | **`translator`** | NLLB-200 (CPU) | Traducción neuronal optimizada con CTranslate2. | -| **`translator-gpu`**| NLLB-200 (GPU) | Traducción acelerada por hardware (CUDA). | -| **`wiki-worker`** | Go | **[NUEVO]** Integración con Wikipedia y gestión de imágenes locales. | -| **`embeddings`** | S-Transformers | Generación de vectores para búsqueda semántica. | -| **`ner`** | Spacy / BERT | Reconocimiento de entidades nombradas (NER). | -| **`llm-categorizer`**| Ollama / Mistral | Clasificación avanzada mediante modelos de lenguaje. | +| **`translator-gpu`** | NLLB-200 (GPU) | Traducción acelerada por hardware (CUDA). | +| **`remote-translator`** | WebSocket | Worker GPU remoto (conexión vía ws://backend-go:8080/ws/worker). | +| **`wiki-worker`** | Go | Integración con Wikipedia y gestión de imágenes locales. | +| **`embeddings`** | S-Transformers | Generación de vectores para búsqueda semántica (paraphrase-multilingual-MiniLM-L12-v2). | +| **`ner`** | Spacy / es_core_news_lg | Reconocimiento de entidades nombradas (NER). | +| **`llm-categorizer`** | Ollama / Mistral | Clasificación avanzada mediante modelos de lenguaje. | | **`topics`** | Go | Matcher automático de países y temas predefinidos. | -| **`related`** | Go | Motor de detección de noticias relacionadas. | +| **`related`** | Go | Motor de detección de noticias relacionadas (similitud coseno). | +| **`qdrant-worker`** | Go | Vectorización y búsqueda semántica con Qdrant + Ollama. | +| **`cluster`** | Python | Agrupación de noticias por eventos. | +| **`langdetect`** | Python | Detección de idioma para routing de traducciones. | +| **`translation-scheduler`** | Python | Creador de tareas de traducción. | ### Capa de Almacenamiento | Servicio | Tecnología | Descripción | @@ -52,6 +61,90 @@ El sistema se orquestra mediante Docker Compose y se divide en capas especializa | **`qdrant`** | Qdrant | Base de datos vectorial para búsqueda por similitud. | | **`redis`** | Redis 7 | Colas de mensajes y caché de alto desempeño. | +### Monitoreo +| Servicio | Tecnología | Descripción | +|---------|------------|-------------| +| **`prometheus`** | Prometheus | Métricas del sistema. | +| **`grafana`** | Grafana | Dashboard (puerto **3001**). | +| **`cadvisor`** | cAdvisor | Monitoreo Docker. | + +--- + +## 📊 Endpoints de API + +### News +- `GET /api/news` - Listar noticias (paginado, filtros: q, category_id, country_id, translated_only) +- `GET /api/news/:id` - Ver noticia con entidades (incluye Wikipedia enrichment) +- `DELETE /api/news/:id` - Eliminar noticia (admin) + +### Feeds +- `GET /api/feeds` - Listar feeds +- `POST /api/feeds` - Crear feed (auth requerido) +- `PUT /api/feeds/:id` - Actualizar feed (auth requerido) +- `DELETE /api/feeds/:id` - Eliminar feed (auth requerido) +- `POST /api/feeds/:id/toggle` - Activar/desactivar feed +- `POST /api/feeds/:id/reactivate` - Reactivar feed + +### Search +- `GET /api/search?q=...` - Búsqueda texto (filtros: lang, categoria_id, pais_id) +- `GET /api/search?q=...&semantic=true` - Búsqueda semántica con Qdrant + +### Entities +- `GET /api/entities?tipo=persona|organizacion|lugar` - Listar entidades (con aliasing) + +### Admin +- `GET /api/admin/backup` - Backup SQL completo (descargable) +- `GET /api/admin/backup/news` - Backup noticias (ZIP) +- `GET /api/admin/users` - Listar usuarios +- `POST /api/admin/users/:id/promote` - Promover a admin +- `POST /api/admin/users/:id/demote` - Quitar admin +- `POST /api/admin/reset-db` - Resetear base de datos +- `POST /api/admin/aliases` - Crear alias de entidad +- `GET /api/admin/aliases/export` - Exportar aliases (CSV) +- `POST /api/admin/aliases/import` - Importar aliases (CSV) +- `POST /api/admin/entities/retype` - Cambiar tipo de entidad +- `POST /api/admin/workers/config` - Configurar workers (type, workers) +- `POST /api/admin/workers/start` - Iniciar workers traducción +- `POST /api/admin/workers/stop` - Detener workers traducción +- `GET /api/admin/workers/status` - Estado de workers +- `GET /api/admin/workers/remote` - Listar remote workers +- `POST /api/admin/workers/remote` - Crear remote worker +- `DELETE /api/admin/workers/remote/:id` - Eliminar remote worker + +### Auth +- `POST /api/auth/login` - Iniciar sesión +- `POST /api/auth/register` - Registrarse +- `GET /api/auth/me` - Usuario actual (auth requerido) +- `GET /api/auth/check-first-user` - Verificar si existe primer usuario + +### Stats +- `GET /api/stats` - Estadísticas globales (total news, feeds, users, hoy/semana/mes) +- `GET /api/categories` - Listar categorías +- `GET /api/countries` - Listar países con continentes + +### WebSocket +- `GET /ws/worker` - Conexión de remote workers (protocolo de autenticación con API key) + +--- + +## 🗄️ Esquema de Base de Datos (Tablas Principales) + +- **noticias**: Artículos RSS (titulo, resumen, url, feed_id, categoria_id, pais_id, lang) +- **feeds**: Fuentes RSS (nombre, url, activo, ultimo_fetch) +- **traducciones**: Traducciones (noticia_id, lang_from, lang_to, titulo_trad, resumen_trad, status, vectorized) +- **tags**: Entidades (valor, tipo: persona/organizacion/lugar/tema, wiki_summary, wiki_url, image_path) +- **tags_noticia**: Relación noticia-tag (traduccion_id, noticia_id, tag_id) +- **entity_aliases**: Alias de entidades (canonical_name, alias, tipo) +- **categorias**: Categorías de noticias +- **paises**: Países con continentes +- **related_noticias**: Noticias relacionadas (traduccion_id, related_traduccion_id, score) +- **traduccion_embeddings**: Embeddings en BD (traduccion_id, model, embedding) +- **users**: Usuarios (email, username, password_hash, is_admin) +- **config**: Configuración del sistema (translator_type, translator_workers, translator_status) +- **remote_workers**: Workers remotos (name, api_key, capabilities, status, last_seen) +- **favoritos**: Noticias favoritas de usuarios +- **search_history**: Historial de búsquedas + --- ## ⚙️ Guía de Configuración @@ -60,34 +153,228 @@ El sistema se orquestra mediante Docker Compose y se divide en capas especializa * **Modo Básico (CPU)**: 4+ Cores CPU, 8GB RAM. * **Modo Avanzado (IA)**: NVIDIA GPU con 8GB+ VRAM (mínimo recomendado para LLM y Traducción GPU). -### 2. Instalación Rápida +### 2. Instalación Rápida con Seguridad Automática + +#### Opción A: Despliegue con Validación Automática (RECOMENDADO) + ```bash +# Paso 1: Clonar el proyecto git clone cd rss2 -cp .env.example .env -# Edita .env con tus credenciales + +# Paso 2: Generar credenciales seguras automáticamente +./pre-deploy.sh --generate + +# Paso 3: Validar y desplegar +./pre-deploy.sh + +# Paso 4: Si eliges opción 2 en el prompt, despliega manualmente docker compose up -d ``` -### 3. Escalado de Workers (¡Importante!) -Para aumentar la velocidad de procesamiento (especialmente la traducción), puedes escalar los workers: +**Ventajas:** +- ✅ Genera credenciales seguras de 32 caracteres +- ✅ Valida que no haya contraseñas por defecto +- ✅ Muestra resumen de credenciales en consola +- ✅ Automático y seguro para producción + +#### Opción B: Despliegue Manual (Para desarrolladores) ```bash -# Ejecutar 4 traductores en paralelo -docker compose up -d --scale translator=4 +git clone +cd rss2 -# Si usas GPU y tienes capacidad -docker compose up -d --scale translator-gpu=2 +# Generar credenciales manualmente +./generate_secure_credentials.sh + +# Copiar credenciales a .env +cp .env.generated .env + +# Desplegar +docker compose up -d ``` --- +## 🔐 Despliegue con Seguridad Automática (RECOMENDADO) + +### 🛡️ Sistema de Validación Pre-Despliegue + +RSS2 incluye `pre-deploy.sh`, un script inteligente que se ejecuta **antes** de `docker compose` para: + +1. ✅ **Validar credenciales**: Asegura que `POSTGRES_PASSWORD`, `REDIS_PASSWORD`, `DB_PASS` estén definidas +2. ✅ **Generar automáticamente**: Crea credenciales seguras de 32 caracteres si faltan +3. ✅ **Prevenir errores**: Evita que los WARN de Docker por variables vacías +4. ✅ **Mostrar resumen**: Te muestra todas las credenciales en consola + +### 🚀 Despliegue Rápido y Seguro (3 Comandos) + +```bash +# 1. Generar credenciales seguras automáticamente +./pre-deploy.sh --generate + +# 2. Validar y desplegar (elige opción 2 para despliegue manual) +./pre-deploy.sh + +# 3. Alternativa: Despliegue manual después de validar +./pre-deploy.sh # ← Elige "2) Solo validar y salir" +docker compose up -d +``` + +**Flujo recomendado:** +```bash +./pre-deploy.sh --generate # Genera .env con credenciales seguras +./pre-deploy.sh # Valida y te pregunta si quieres desplegar +# Si eliges "2): Solo validar", luego: +docker compose up -d # Despliega manualmente +``` + +### 📋 Opciones del Script `pre-deploy.sh` + +```bash +# Validar credenciales existentes +./pre-deploy.sh + +# Generar credenciales seguras automáticamente +./pre-deploy.sh --generate + +# Saltar validación (NO RECOMENDADO para producción) +./pre-deploy.sh --skip + +# Ver ayuda +./pre-deploy.sh --help +``` + +**Salida típica del script:** +``` +╔═══════════════════════════════════════════════════════════╗ +║ 🔐 Pre-Deploy Security Check - RSS2 Platform ║ +╚═══════════════════════════════════════════════════════════╝ + +✅ Archivo .env encontrado +✅ DEFINIDO: POSTGRES_PASSWORD=8x7f2k9m4p1q3w5e +✅ DEFINIDO: REDIS_PASSWORD=a9b8c7d6e5f4g3h2 +✅ DEFINIDO: DB_PASS=8x7f2k9m4p1q3w5e + +📋 Resumen de Credenciales Activas + POSTGRES_PASSWORD: ✓ + REDIS_PASSWORD: ✓ + DB_PASS: ✓ + +✅ VALIDACIÓN COMPLETADA - LISTO PARA DESPLEGAR +``` + +### 🔒 Variables Críticas que Requieren Contraseñas + +| Variable | Descripción | Uso | +|----------|-------------|-----| +| `POSTGRES_PASSWORD` | Contraseña de PostgreSQL | Base de datos | +| `REDIS_PASSWORD` | Contraseña de Redis | Cache y colas | +| `DB_PASS` | Contraseña para workers | Conexiones de workers | +| `SECRET_KEY` | Key secreta de la aplicación | JWT, encriptación | +| `GRAFANA_PASSWORD` | Contraseña de Grafana | Dashboard de monitoring | + +--- + +### 3. Escalado de Workers GPU (¡Importante!) + +#### 🚀 Workers de Traducción GPU Multi-Instancia + +RSS2 soporta múltiples workers de traducción ejecutándose en paralelo utilizando GPUs, lo que acelera significativamente el proceso de traducción masiva. + +**Arquitectura Multi-Worker:** +- Cada worker es una instancia independiente del servicio `ctranslator_worker` +- Los workers compiten automáticamente por las traducciones pendientes usando `SELECT ... FOR UPDATE SKIP LOCKED` +- Cada worker procesa su propio lote (batch) de traducciones sin bloquearse mutuamente +- Soporta hasta 8 workers simultáneos (configurable vía panel admin) + +**Requisitos GPU:** +```bash +# Verificar GPU disponibles +nvidia-smi + +# Instalar drivers NVIDIA (si no los tienes) +# Ubuntu/Debian: +sudo apt-get update +sudo apt-get install -y nvidia-driver-535 +``` + +**Despliegue con Múltiples Workers GPU:** + +```bash +# Opción 1: Desplegar 1 worker GPU (mínimo recomendado) +docker compose up -d --scale translator-gpu=1 + +# Opción 2: Desplegar 2 workers GPU (recomendado: 16GB+ VRAM total) +docker compose up -d --scale translator-gpu=2 + +# Opción 3: Desplegar 4 workers GPU (requiere: 32GB+ VRAM total) +docker compose up -d --scale translator-gpu=4 + +# Opción 4: Combinar CPU y GPU +# 2 workers CPU + 2 workers GPU +docker compose up -d --scale translator=2 --scale translator-gpu=2 +``` + +**Panel de Administración:** +Desde `/admin/settings`, puedes: +- Activar modo GPU (CUDA) o CPU +- Configurar número de workers (1-8) +- Iniciar/detener workers automáticamente + +**Configuración de GPU por Worker:** +Cada worker GPU está configurado con: +- `CT2_DEVICE=cuda` - Usa dispositivos CUDA +- `CT2_COMPUTE_TYPE=float16` - Precisión mixta para velocidad +- `TRANSLATOR_BATCH=128` - Tamaño de lote optimizado +- `PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512` - Gestión de memoria PyTorch +- `NCCL_DEBUG=INFO` - Debug de comunicación multi-GPU + +**Monitorización:** +```bash +# Ver estado de workers +docker compose --profile gpu ps translator-gpu + +# Ver uso de GPU +watch -n 1 nvidia-smi + +# Ver logs en tiempo real +docker compose logs -f translator-gpu + +# Métricas en Grafana +# Dashboard: Translation Workers +# Métricas: translator_*_transitions_per_minute +``` + +**Performance Esperada:** +| Workers GPU | VRAM Total | Tasa de Traducción | +|-------------|------------|-------------------| +| 1 worker | 8GB+ | ~500 traducciones/min | +| 2 workers | 16GB+ | ~1000 traducciones/min | +| 4 workers | 32GB+ | ~2000 traducciones/min | +| 8 workers | 64GB+ | ~4000 traducciones/min | + +--- + ## 🛡️ Administración y Mantenimiento ### Copias de Seguridad (Backups) + Desde el panel de Administración (`/admin/settings`), puedes realizar: -* **Backup Completo**: Volcado SQL de toda la base de datos. -* **Backup de Noticias (ZIP)**: **[NUEVO]** Genera un archivo comprimido que incluye las tablas de noticias, traducciones y todas sus etiquetas. Ideal para migraciones de contenido. + +**Backup Completo (SQL):** +- Descarga un archivo `.sql` con todo el contenido de la base de datos +- Incluye: noticias, feeds, traducciones, usuarios, etiquetas, favoritos, videos, eventos, historial de búsqueda +- Formato: PostgreSQL custom format con datos y estructura +- Tamaño típico: 10-100MB dependiendo del volumen de datos + +**Backup de Noticias (ZIP):** +- Genera un archivo comprimido `.zip` con: + - Tabla `noticias` (artículos completos) + - Tabla `traducciones` (todas las traducciones) + - Tablas `tags` y `tags_noticia` (metadatos y relaciones) +- Ideal para: Migraciones de contenido, exportación de datos, backup ligero +- Tamaño típico: 5-50MB (solo contenido de noticias) ### Variables de Entorno Clave (`.env`) | Variable | Descripción | @@ -95,7 +382,108 @@ Desde el panel de Administración (`/admin/settings`), puedes realizar: | `WIKI_SLEEP` | Tiempo de espera entre peticiones a Wikipedia (evita bloqueos). | | `SCHEDULER_BATCH`| Cantidad de noticias a enviar a traducir por ciclo. | | `TARGET_LANGS` | Idiomas destino (ej: `es`). | -| `OLLAMA_HOST` | Dirección del servidor Ollama para categorización. | +| `OLLAMA_URL` | URL del servidor Ollama para categorización. | + +--- + +## 🎯 Descripción de Workers + +### rss-ingestor-go +Crawler RSS de alto rendimiento escrito en Go. Gestiona hasta 100 workers paralelos para procesar múltiples feeds simultáneamente. Detecta nuevos artículos, extrae metadatos básicos y los inserta en la base de datos. + +### scraper (Go) +Scraper profundo que: +- Descarga el contenido completo de URLs de artículos +- Sanitiza HTML removiendo scripts y estilos +- Extrae texto limpio del body +- Limita el contenido a 20 noticias por ciclo para evitar sobrecarga + +### discovery (Go) +Agente de descubrimiento de feeds que: +- Analiza URLs proporcionadas para detectar feeds RSS/Atom +- Soporta hasta 5 feeds por URL +- Ejecuta cada 900 segundos (15 minutos) + +### translator (Python/CTranslate2) +Worker de traducción neuronal: +- Modelo: facebook/nllb-200-distilled-600M convertido a CTranslate2 +- Dispositivo: CPU (int8) +- Batch: 32 traducciones por ciclo +- Locking: `FOR UPDATE SKIP LOCKED` para evitar procesamiento duplicado +- Soporta múltiples idiomas fuente + +### translator-gpu (Python/CTranslate2) +Versión acelerada por GPU del translator: +- Dispositivo: CUDA +- Compute type: float16 +- Batch: 128 traducciones por ciclo +- Ideal para procesamiento de alto volumen + +### remote-translator (Python) +Worker remoto conectado vía WebSocket: +- Autenticación con API key +- Conexión a `ws://backend-go:8080/ws/worker` +- Modelo: facebook/nllb-200-1.3B (más grande) +- GPU requerida + +### langdetect (Python) +Detector de idioma: +- Usa biblioteca `langdetect` +- Proceso: 1000 noticias por ciclo +- Establece lang_from para correcto routing a traducción + +### ner (Python/spaCy) +Extracción de entidades nombradas: +- Modelo: es_core_news_lg +- Tipos: PERSON (persona), ORG (organizacion), LOC/GPE (lugar) +- También extrae topics (noun-chunks) +- Configurable via entity_config.json (blacklist, synonyms) + +### embeddings (Python) +Generador de embeddings para búsqueda semántica: +- Modelo: paraphrase-multilingual-MiniLM-L12-v2 +- Dispositivo: GPU (cuda) +- Genera vectores de 384 dimensiones +- Alimenta Qdrant + +### qdrant-worker (Go) +Worker de vectorización Qdrant: +- Conecta a Qdrant (puerto 6333) +- Genera embeddings via Ollama (mxbai-embed-large) +- Sube puntos a collection "news_vectors" +- Actualiza estado en BD (vectorized=true) + +### related (Go) +Detector de noticias relacionadas: +- Usa similitud coseno entre embeddings +- Almacena top-k relaciones en tabla related_noticias +- Soporta configuración de threshold mínimo + +### wiki-worker (Go) +Enriquecedor de Wikipedia: +- Descarga summaries de Wikipedia API +- Descarga thumbnails de imágenes +- Almacena en `./data/wiki_images` +- Evita rate limiting con WIKI_SLEEP + +### llm-categorizer (Python) +Categorizador con LLM: +- Conexión a Ollama +- Usa modelo Mistral para clasificación +- Procesa 10 noticias por batch + +### cluster (Python) +Agrupador de noticias por eventos: +- Usa embeddings paraphrase-multilingual-MiniLM-L12-v2 +- Threshold: 0.35 de distancia +- Agrupa noticias similares + +### translation-scheduler (Python) +Creador de tareas de traducción: +- Query: noticias sin traducción al español +- Crea entradas en tabla traducciones +- Batch: 1000 por ciclo +- Ciclo: 30 segundos --- @@ -121,4 +509,41 @@ Las respuestas de noticias ahora incluyen el objeto `entities` enriquecido: --- -**RSS2** - *Transformando noticias en inteligencia con IA localizada.* +## 🧪 Testing + +### Backend (Go) +```bash +cd backend && go test ./internal/handlers -v -run TestLogin +cd backend && go test ./internal/auth -v -run TestGenerateToken +``` + +### Frontend +```bash +cd frontend && npm run test:ui +``` + +--- + +## 📁 Estructura de Archivos del Proyecto + +``` +rss2/ +├── backend/ # API REST Go (Gin) +├── frontend/ # Interfaz React + TypeScript +├── workers/ # Workers Python +├── rss-ingestor-go/ # Crawler RSS Go +├── docker-compose.yml # Orquestación +├── nginx.conf # Config Nginx +├── monitoring/ # Prometheus + Grafana +├── init-db/ # Migraciones SQL +├── data/ # Datos persistentes +├── models/ # Modelos ML +├── hf_cache/ # Cache HuggingFace +├── pre-deploy.sh # Script seguridad +├── generate_secure_credentials.sh +└── AGENTS.md # Guía desarrollo +``` + +--- + +**RSS2** - *Transformando noticias en inteligencia con IA localizada.* \ No newline at end of file diff --git a/README_DEPLOY.md b/README_DEPLOY.md new file mode 100644 index 0000000..50b0d69 --- /dev/null +++ b/README_DEPLOY.md @@ -0,0 +1,220 @@ +# 🚀 Despliegue RSS2 - 3 Comandos Simples + +## ⚡ Flujo Rápido (3 pasos) + +```bash +# 1. Generar credenciales seguras +./generate_secure_credentials.sh --force + +# 2. Desplegar +docker compose up -d + +# 3. Verificar +docker compose ps +``` + +**¡Listo!** La aplicación estará disponible en `http://localhost:8888` + +--- + +## 🔐 ¿Por qué 3 pasos? + +**Problema original:** +```bash +docker compose up -d +WARN: DB_PASS no definida → Contraseña vacía → ¡Inseguro! +``` + +**Solución:** +1. `./generate_secure_credentials.sh --force` → Crea `.env` con contraseñas de 32 caracteres +2. `docker compose up -d` → Docker carga `.env` automáticamente +3. `docker compose ps` → Verifica que todo esté bien + +--- + +## 📋 Qué hace cada comando + +### 1. `./generate_secure_credentials.sh --force` + +- Crea contraseñas seguras de 32 caracteres aleatorios +- Genera archivo `.env` con todas las variables necesarias +- Guarda backup del `.env` anterior +- **Tiempo: 5 segundos** + +**Salida:** +``` +POSTGRES_PASSWORD: S5xwsI9HKjdaBWLpCkqp0mA0DJYZerpD +REDIS_PASSWORD: cVF79QuJulPO5auEhA2nNqv7mqAruzYh +SECRET_KEY: 2eb7c221245bb8d896f83255188de614e59fae6b8dfbf16eee8a23c60dde4ffa +GRAFANA_PASSWORD: R4J61V6OGLTCesrITwEvUPTm +``` + +### 2. `docker compose up -d` + +- Lee `.env` automáticamente (está configurado en `docker-compose.yml`) +- Inicia todos los servicios en modo detached (background) +- Espera a que estén listos +- **Tiempo: 2-5 minutos** (primera vez que descarga imágenes) + +### 3. `docker compose ps` + +- Muestra estado de todos los servicios +- Deberías ver `Up` para todos +- **Tiempo: 1 segundo** + +**Salida esperada:** +``` +Name Status +rss2_db Up (healthy) +rss2_redis Up (healthy) +rss2_backend_go Up (running) +rss2_nginx Up (running) +... +``` + +--- + +## 🎯 Comandos Útiles + +### Verificar credenciales +```bash +grep -E "^(POSTGRES_PASSWORD|REDIS_PASSWORD|DB_PASS)=" .env +``` + +### Ver logs en tiempo real +```bash +docker compose logs -f +``` + +### Reiniciar servicios +```bash +# Todos +docker compose restart + +# Solo base de datos +docker compose restart db +``` + +### Verificar estado +```bash +docker compose ps +``` + +### Limpiar todo (PERDERÁS DATOS) +```bash +docker compose down -v +``` + +### Escalar workers +```bash +# Más traductores CPU +docker compose up -d --scale translator=3 + +# Más traductores GPU +docker compose up -d --scale translator-gpu=2 +``` + +--- + +## 🔒 Seguridad + +### Contraseñas por defecto = PELIGRO ❌ + +```bash +# MAL - Contraseñas vacías +docker compose up -d +WARN: DB_PASS no set +# Las contraseñas están vacías +``` + +### Contraseñas generadas = SEGURO ✅ + +```bash +# BIEN +./generate_secure_credentials.sh --force +docker compose up -d +# Contraseñas de 32 caracteres aleatorios +``` + +### Guardar tus credenciales + +**Opción 1: Gestor de contraseñas (Recomendado)** +```bash +# Copia las contraseñas que muestra el script +# Guárdalas en: LastPass, 1Password, KeePass, Bitwarden +``` + +**Opción 2: Archivo seguro local** +```bash +# Crea un archivo fuera del proyecto +echo "POSTGRES_PASSWORD=..." > ~/rss2-credentials.txt +``` + +--- + +## 📊 Estado de los Servicios + +| Servicio | Puerto | Descripción | +|----------|--------|-------------| +| `nginx` | 8888 | Web app | +| `backend-go` | 8080 | API | +| `db` | 5432 | PostgreSQL | +| `redis` | 6379 | Cache | +| `qdrant` | 6333 | Vector DB | + +**DASHBOARDS:** +- Grafana: http://127.0.0.1:3001 +- Prometheus: http://127.0.0.1:9090 + +--- + +## 🐛 Solución de Problemas + +### WARN sobre contraseñas vacías +```bash +# Solución: Generar credenciales +./generate_secure_credentials.sh --force +``` + +### Port 8888 en uso +```bash +# Cambiar puerto en nginx +``` + +### Base de datos no inicia +```bash +# Ver logs +docker compose logs db + +# Reiniciar +docker compose restart db +``` + +### Redis autenticación fallida +```bash +# Ver REDIS_PASSWORD +grep REDIS_PASSWORD .env +``` + +--- + +## ✅ Checklist Final + +Antes de considerar tu despliegue listo: + +- [ ] `.env` tiene contraseñas generadas (no por defecto) +- [ ] `docker compose ps` muestra todos "Up" +- [ ] `http://localhost:8888` abre la app +- [ ] `curl http://localhost:8888/api/health` responde + +--- + +## 📚 Documentación + +- [README.md](./README.md) - Documentación completa +- [DEPLOY.md](./DEPLOY.md) - Guía detallada +- [SECURITY_GUIDE.md](./SECURITY_GUIDE.md) - Seguridad + +--- + +**RSS2** - *Transformando noticias en inteligencia con IA* 🚀 diff --git a/alias.sh b/alias.sh new file mode 100755 index 0000000..6054fe6 --- /dev/null +++ b/alias.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# Aliases y funciones para despliegue automático de RSS2 +# Copia este archivo y ejecútalo: source alias.sh + +# ======================================== +# FUNCIONES DE VALIDACIÓN AUTOMÁTICA +# ======================================== + +# Validar credenciales críticas +validate_creds() { + # Verificar si .env existe + if [ ! -f .env ]; then + echo "🔑 Generando credenciales seguras..." + ./generate_secure_credentials.sh --force + exit $? + fi + + # Verificar contraseñas vacías + for var in POSTGRES_PASSWORD REDIS_PASSWORD DB_PASS; do + val=$(grep "^${var}=" .env 2>/dev/null | cut -d'=' -f2 | tr -d ' ') + if [ -z "$val" ] || [ "$val" = "change_*" ] || [ "$val" = "" ]; then + echo "🔑 $var está vacía, generando..." + ./generate_secure_credentials.sh --force + exit $? + fi + done + + echo "✅ Credenciales OK" +} + +# ======================================== +# ALIASES PRINCIPALES +# ======================================== + +# Alias principal: docker compose con validación automática +alias dc="docker compose" +alias dcu="docker_compose_up" # docker compose up +alias dcd="docker_compose_down" # docker compose down + +# Alias con validación automática +docker_compose_up() { + echo "🔐 Validando credenciales..." + validate_creds + + echo "🚀 docker compose up -d" + dc up -d +} + +docker_compose_down() { + echo "📦 docker compose down -v" + dc down -v +} + +# Alias para despliegue completo con validación +deploy() { + echo "================================" + echo "🚀 DESPLIEGUE AUTOMÁTICO RSS2" + echo "================================" + echo "" + + echo "1. 🔐 Validando credenciales..." + validate_creds + + echo "2. 📦 Deteniendo y eliminando..." + dc down -v 2>/dev/null || true + + echo "3. 🚀 Iniciando servicios..." + dc up -d --build + + echo "4. ✅ Verificando estado..." + dc ps + + echo "" + echo "================================" + echo "✅ ¡Despliegue completado!" + echo "🌐 App: http://localhost:8888" + echo "================================" +} + +# Alias para despliegue rápido sin rebuild +deploy-quick() { + echo "🚀 Despliegue rápido (sin rebuild)..." + dc up -d + + echo "📊 Estado:" + dc ps +} + +# Alias para ver logs +tail-logs() { + echo "📊 Logs en tiempo real..." + dc logs -f +} + +# ======================================== +# FUNCIONES DE UTILIDAD +# ======================================== + +# Generar credenciales +generate-secrets() { + echo "🔑 Generando credenciales seguras..." + ./generate_secure_credentials.sh --force +} + +# Backup +backup() { + echo "📦 Creando backup..." + dc exec db pg_dump -U rss rss > backup_$(date +%Y%m%d).sql + echo "✅ Backup guardado en ./backup_$(date +%Y%m%d).sql" +} + +# ======================================== +# MENSAJE DE BIENVENIDA +# ======================================== +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + echo "🎉 RSS2 Deployment Aliases cargadas" + echo "" + echo "Comandos disponibles:" + echo " dc docker compose" + echo " dcu docker compose up (con validación)" + echo " dcd docker compose down" + echo " deploy Despliegue completo con validación" + echo " deploy-quick Despliegue rápido" + echo " tail-logs Ver logs en tiempo real" + echo " generate-secrets Generar credenciales" + echo " backup Crear backup" + echo "" +fi diff --git a/backend/internal/handlers/admin.go b/backend/internal/handlers/admin.go index 8b4e784..0e40f80 100644 --- a/backend/internal/handlers/admin.go +++ b/backend/internal/handlers/admin.go @@ -20,8 +20,6 @@ import ( "github.com/rss2/backend/internal/models" ) - - func CreateAlias(c *gin.Context) { var req models.EntityAliasRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -82,7 +80,7 @@ func CreateAlias(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reassign news mentions safely", "message": err.Error()}) return } - + // Delete any remaining orphaned mentions of the alias that couldn't be merged (duplicates) _, err = tx.Exec(ctx, "DELETE FROM tags_noticia WHERE tag_id = $1", aliasTagId) if err != nil { @@ -112,8 +110,6 @@ func CreateAlias(c *gin.Context) { }) } - - func ExportAliases(c *gin.Context) { rows, err := db.GetPool().Query(c.Request.Context(), "SELECT alias, canonical_name, tipo FROM entity_aliases ORDER BY tipo, canonical_name") @@ -533,8 +529,8 @@ func StopWorkers(c *gin.Context) { // PatchEntityTipo changes the tipo of all tags matching a given valor func PatchEntityTipo(c *gin.Context) { var req struct { - Valor string `json:"valor" binding:"required"` - NewTipo string `json:"new_tipo" binding:"required"` + Valor string `json:"valor" binding:"required"` + NewTipo string `json:"new_tipo" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request", "message": err.Error()}) @@ -561,7 +557,7 @@ func PatchEntityTipo(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch existing tags", "message": err.Error()}) return } - + type OldTag struct { ID int Tipo string @@ -643,32 +639,15 @@ func PatchEntityTipo(c *gin.Context) { // BackupDatabase runs pg_dump and returns the SQL as a downloadable file func BackupDatabase(c *gin.Context) { - dbHost := os.Getenv("DB_HOST") - if dbHost == "" { - dbHost = "db" - } - dbPort := os.Getenv("DB_PORT") - if dbPort == "" { - dbPort = "5432" - } - dbName := os.Getenv("DB_NAME") - if dbName == "" { - dbName = "rss" - } - dbUser := os.Getenv("DB_USER") - if dbUser == "" { - dbUser = "rss" - } - dbPass := os.Getenv("DB_PASS") - - cmd := exec.Command("pg_dump", - "-h", dbHost, - "-p", dbPort, - "-U", dbUser, - "-d", dbName, + // Ejecutar pg_dump desde dentro del contenedor db para evitar problemas de red + // Usamos docker exec para ejecutar el comando dentro del servicio db + cmd := exec.Command("docker", "exec", "rss2_db", "pg_dump", + "-U", os.Getenv("POSTGRES_USER"), + "-d", os.Getenv("POSTGRES_DB"), "--no-password", + "--format=plain", + "--pghost=5432", ) - cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", dbPass)) var out bytes.Buffer var stderr bytes.Buffer @@ -769,4 +748,3 @@ func BackupNewsZipped(c *gin.Context) { c.Header("Cache-Control", "no-cache") c.Data(http.StatusOK, "application/zip", buf.Bytes()) } - diff --git a/docker-compose.yml b/docker-compose.yml index a5c51e5..d4fc742 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,16 @@ +# ================================================================================== +# CONFIGURACIÓN DE SECRETOS - Se cargan desde .env +# ================================================================================== +# Las variables POSTGRES_PASSWORD, REDIS_PASSWORD, DB_PASS, SECRET_KEY, GRAFANA_PASSWORD +# deben estar definidas en un archivo .env antes de ejecutar docker compose up +# +# Para generar credenciales seguras automáticamente: +# ./generate_secure_credentials.sh --force +# +# Despliegue simple: +# docker compose up -d +# ================================================================================== + services: db: image: postgres:18-alpine @@ -25,6 +38,7 @@ services: restart: unless-stopped healthcheck: test: [ "CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $$POSTGRES_USER -d $$POSTGRES_DB || exit 1" ] + # NOTA: Si falla, verifica que POSTGRES_PASSWORD esté definido en .env interval: 5s timeout: 5s retries: 30 @@ -43,7 +57,7 @@ services: TZ: Europe/Madrid # SEGURIDAD: Redis con autenticación command: > - redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD} + redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD:-!ERROR!REDIS_PASSWORD_NEEDED!} volumes: - ./data/redis-data:/data - /etc/timezone:/etc/timezone:ro @@ -55,6 +69,7 @@ services: - rss2_redis restart: unless-stopped healthcheck: + # VALIDACIÓN: Si REDIS_PASSWORD está vacío, esto fallará y detendrá el despliegue test: [ "CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping" ] interval: 5s timeout: 3s @@ -282,8 +297,6 @@ services: dockerfile: Dockerfile.translator image: rss2-translator:latest command: bash -lc "python -m workers.ctranslator_worker" - security_opt: - - seccomp=unconfined environment: DB_HOST: db DB_PORT: 5432 @@ -299,14 +312,60 @@ services: HF_HOME: /app/hf_cache TZ: Europe/Madrid TRANSLATOR_ID: ${TRANSLATOR_ID:-} + PYTORCH_ENABLE_MPS_FALLBACK: 1 volumes: - ./workers:/app/workers - ./hf_cache:/app/hf_cache - ./models:/app/models networks: - backend - profiles: - - cpu-only + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + # ================================================================================== + # TRANSLATOR GPU - Multiple instances with --scale for parallel GPU processing + # Configure multiple GPU workers: docker compose up -d --scale translator-gpu=2 + # ================================================================================== + translator-gpu: + build: + context: . + dockerfile: Dockerfile.translator-gpu + image: rss2-translator-gpu:latest + command: bash -lc "python -m workers.ctranslator_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + TARGET_LANGS: es + TRANSLATOR_BATCH: 256 + CT2_MODEL_PATH: /app/models/nllb-ct2-1.3b + CT2_DEVICE: cuda + CT2_COMPUTE_TYPE: float16 + UNIVERSAL_MODEL: facebook/nllb-200-1.3B + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + TRANSLATOR_ID: ${TRANSLATOR_ID:-} + PYTORCH_CUDA_ALLOC_CONF: max_split_size_mb:512 + NCCL_DEBUG: INFO + volumes: + - ./workers:/app/workers + - ./hf_cache:/app/hf_cache + - ./models:/app/models + - /dev/nvidia:/dev/nvidia + - /usr/local/nvidia:/usr/local/nvidia:ro + networks: + - backend + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] depends_on: db: condition: service_healthy @@ -335,8 +394,6 @@ services: - ./models:/app/models networks: - backend - profiles: - - remote deploy: resources: limits: @@ -381,52 +438,6 @@ services: condition: service_healthy restart: unless-stopped - # ================================================================================== - # TRANSLATOR GPU (CTranslate2 with CUDA) - # ================================================================================== - translator-gpu: - build: - context: . - dockerfile: Dockerfile.translator-gpu - image: rss2-translator-gpu:latest - container_name: rss2_translator_gpu - command: bash -lc "python -m workers.ctranslator_worker" - security_opt: - - seccomp=unconfined - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - TARGET_LANGS: es - TRANSLATOR_BATCH: 64 - CT2_MODEL_PATH: /app/models/nllb-ct2 - CT2_DEVICE: cuda - CT2_COMPUTE_TYPE: float16 - UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - - ./hf_cache:/app/hf_cache - - ./models:/app/models - networks: - - backend - deploy: - resources: - limits: - memory: 4G - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [ gpu ] - depends_on: - db: - condition: service_healthy - restart: unless-stopped - embeddings: build: context: . diff --git a/docker-compose.yml.backup b/docker-compose.yml.backup new file mode 100644 index 0000000..7dfb3d6 --- /dev/null +++ b/docker-compose.yml.backup @@ -0,0 +1,802 @@ +# ================================================================================== +# CONFIGURACIÓN DE SECRETOS - Se cargan desde .env +# ================================================================================== +# Las variables POSTGRES_PASSWORD, REDIS_PASSWORD, DB_PASS, SECRET_KEY, GRAFANA_PASSWORD +# deben estar definidas en un archivo .env antes de ejecutar docker compose up +# +# Para generar credenciales seguras automáticamente: +# ./generate_secure_credentials.sh --force +# +# Despliegue simple: +# docker compose up -d +# ================================================================================== + +services: + db: + image: postgres:18-alpine + container_name: rss2_db + shm_size: 4gb + environment: + POSTGRES_DB: ${POSTGRES_DB:-rss} + POSTGRES_USER: ${POSTGRES_USER:-rss} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C.UTF-8" + LANG: C.UTF-8 + LC_ALL: C.UTF-8 + TZ: Europe/Madrid + PGDATA: /var/lib/postgresql/data/18/main + volumes: + - ./data/pgdata:/var/lib/postgresql/data + - ./init-db:/docker-entrypoint-initdb.d:rw + - ./docker-entrypoint-db.sh:/docker-entrypoint-db.sh:ro + entrypoint: ["bash", "/docker-entrypoint-db.sh"] + networks: + backend: + aliases: + - db + - rss2_db + restart: unless-stopped + healthcheck: + test: [ "CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $$POSTGRES_USER -d $$POSTGRES_DB || exit 1" ] + # NOTA: Si falla, verifica que POSTGRES_PASSWORD esté definido en .env + interval: 5s + timeout: 5s + retries: 30 + start_period: 20s + deploy: + resources: + limits: + memory: 8G + reservations: + memory: 4G + + redis: + image: redis:7-alpine + container_name: rss2_redis + environment: + TZ: Europe/Madrid + # SEGURIDAD: Redis con autenticación + command: > + redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD:-!ERROR!REDIS_PASSWORD_NEEDED!} + volumes: + - ./data/redis-data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + networks: + backend: + aliases: + - redis + - rss2_redis + restart: unless-stopped + healthcheck: + # VALIDACIÓN: Si REDIS_PASSWORD está vacío, esto fallará y detendrá el despliegue + test: [ "CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping" ] + interval: 5s + timeout: 3s + retries: 5 + deploy: + resources: + limits: + memory: 768M + reservations: + memory: 512M + + rss-ingestor-go: + build: + context: ./rss-ingestor-go + dockerfile: Dockerfile + container_name: rss2_ingestor_go + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + RSS_MAX_WORKERS: 100 + RSS_POKE_INTERVAL_MIN: 60 + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + memory: 512M + + langdetect: + build: + context: . + dockerfile: Dockerfile + container_name: rss2_langdetect_py + command: bash -lc "python -m workers.langdetect_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + LANG_DETECT_SLEEP: 60 + LANG_DETECT_BATCH: 1000 + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + + # ================================================================================== + # SCRAPER WORKER (Go) - Extrae artículos de URLs + # ================================================================================== + scraper: + build: + context: . + dockerfile: Dockerfile.scraper + container_name: rss2_scraper + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + SCRAPER_SLEEP: 60 + SCRAPER_BATCH: 10 + SCRAPER_ENRICH_LIMIT: 20 + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 512M + + # ================================================================================== + # DISCOVERY WORKER (Go) - Descubre RSS feeds + # ================================================================================== + discovery: + build: + context: . + dockerfile: Dockerfile.discovery + container_name: rss2_discovery + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + DISCOVERY_INTERVAL: 900 + DISCOVERY_BATCH: 10 + MAX_FEEDS_PER_URL: 5 + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 512M + + # ================================================================================== + # WIKI WORKER (Go) - Wikipedia info and thumbnails + # ================================================================================== + wiki-worker: + build: + context: . + dockerfile: Dockerfile.wiki + container_name: rss2_wiki_worker + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + WIKI_SLEEP: 10 + TZ: Europe/Madrid + volumes: + - ./data/wiki_images:/app/data/wiki_images + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.5' + memory: 256M + + # ================================================================================== + # BACKEND GO (API REST) + # ================================================================================== + backend-go: + build: + context: ./backend + dockerfile: Dockerfile + container_name: rss2_backend_go + environment: + TZ: Europe/Madrid + DATABASE_URL: postgres://${POSTGRES_USER:-rss}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-rss}?sslmode=disable + REDIS_URL: redis://:${REDIS_PASSWORD:-rss_redis_pass_2024}@redis:6379 + SECRET_KEY: ${SECRET_KEY:-change_this_to_a_long_random_string} + SERVER_PORT: "8080" + volumes: + - ./data/wiki_images:/app/data/wiki_images + networks: + - backend + - frontend + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + restart: unless-stopped + + # ================================================================================== + # FRONTEND REACT + # ================================================================================== + rss2_frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: rss2_frontend + environment: + TZ: Europe/Madrid + VITE_API_URL: /api + networks: + - frontend + depends_on: + - backend-go + restart: unless-stopped + + # ================================================================================== + # NGINX (Puerto 8001 - sirve React + proxy API) + # ================================================================================== + nginx: + image: nginx:alpine + container_name: rss2_nginx + ports: + - "8888:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + networks: + - frontend + depends_on: + - rss2_frontend + - backend-go + restart: unless-stopped + + # ================================================================================== + # TRANSLATOR CPU (CTranslate2) - Scale with: docker compose up -d --scale translator=3 + # ================================================================================== + translator: + build: + context: . + dockerfile: Dockerfile.translator + image: rss2-translator:latest + command: bash -lc "python -m workers.ctranslator_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + TARGET_LANGS: es + TRANSLATOR_BATCH: 32 + CT2_MODEL_PATH: /app/models/nllb-ct2 + CT2_DEVICE: cpu + CT2_COMPUTE_TYPE: int8 + UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + TRANSLATOR_ID: ${TRANSLATOR_ID:-} + PYTORCH_ENABLE_MPS_FALLBACK: 1 + volumes: + - ./workers:/app/workers + - ./hf_cache:/app/hf_cache + - ./models:/app/models + networks: + - backend + profiles: + - cpu-only + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + # ================================================================================== + # TRANSLATOR GPU - Multiple instances with --scale for parallel GPU processing + # Configure multiple GPU workers: docker compose up -d --scale translator-gpu=2 + # ================================================================================== + translator-gpu: + build: + context: . + dockerfile: Dockerfile.translator-gpu + image: rss2-translator-gpu:latest + command: bash -lc "python -m workers.ctranslator_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + TARGET_LANGS: es + TRANSLATOR_BATCH: 128 + CT2_MODEL_PATH: /app/models/nllb-ct2 + CT2_DEVICE: cuda + CT2_COMPUTE_TYPE: float16 + UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + TRANSLATOR_ID: ${TRANSLATOR_ID:-} + PYTORCH_CUDA_ALLOC_CONF: max_split_size_mb:512 + NCCL_DEBUG: INFO + volumes: + - ./workers:/app/workers + - ./hf_cache:/app/hf_cache + - ./models:/app/models + - /dev/nvidia:/dev/nvidia + - /usr/local/nvidia:/usr/local/nvidia:ro + networks: + - backend + profiles: + - gpu + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + # ================================================================================== + # REMOTE TRANSLATOR WORKER - Worker remoto que se conecta por WebSocket + # ================================================================================== + remote-translator: + build: + context: . + dockerfile: Dockerfile.remote-worker + container_name: rss2_remote_translator + environment: + WORKER_NAME: ${WORKER_NAME:-remote-worker-1} + WORKER_API_KEY: ${WORKER_API_KEY:-} + WORKER_SERVER: ${WORKER_SERVER:-ws://backend-go:8080/ws/worker} + CT2_DEVICE: ${CT2_DEVICE:-cuda} + CT2_MODEL_PATH: /app/models/nllb-ct2 + CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-float16} + UNIVERSAL_MODEL: facebook/nllb-200-1.3B + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + volumes: + - ./hf_cache:/app/hf_cache + - ./models:/app/models + networks: + - backend + profiles: + - remote + deploy: + resources: + limits: + memory: 6G + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [ gpu ] + restart: unless-stopped + + # ================================================================================== + # TRANSLATION SCHEDULER - Creates translation jobs + # ================================================================================== + translation-scheduler: + build: + context: . + dockerfile: Dockerfile.scheduler + image: rss2-scheduler:latest + container_name: rss2_translation_scheduler + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + TARGET_LANGS: es + SCHEDULER_BATCH: 1000 + SCHEDULER_SLEEP: 30 + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + networks: + - backend + deploy: + resources: + limits: + cpus: '0.5' + memory: 256M + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + embeddings: + build: + context: . + dockerfile: Dockerfile + container_name: rss2_embeddings_py + command: bash -lc "python -m workers.embeddings_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 + EMB_BATCH: 64 + EMB_SLEEP_IDLE: 5 + EMB_LANGS: es + EMB_LIMIT: 1000 + DEVICE: cuda + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + - ./hf_cache:/app/hf_cache + networks: + - backend + deploy: + resources: + limits: + memory: 6G + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [ gpu ] + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + # ================================================================================== + # TOPICS WORKER (Go) - Matching temas y países + # ================================================================================== + topics: + build: + context: . + dockerfile: Dockerfile.topics + container_name: rss2_topics + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + TOPICS_SLEEP: 10 + TOPICS_BATCH: 500 + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 512M + + # ================================================================================== + # RELATED WORKER (Go) - Noticias relacionadas + # ================================================================================== + related: + build: + context: . + dockerfile: Dockerfile.related + container_name: rss2_related + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + RELATED_SLEEP: 10 + RELATED_BATCH: 200 + RELATED_TOPK: 10 + EMB_MODEL: mxbai-embed-large + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 1G + + qdrant: + image: qdrant/qdrant:latest + container_name: rss2_qdrant + environment: + TZ: Europe/Madrid + QDRANT__SERVICE__GRPC_PORT: 6334 + volumes: + - ./data/qdrant_storage:/qdrant/storage + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + networks: + - backend + restart: unless-stopped + deploy: + resources: + limits: + cpus: '4' + memory: 4G + reservations: + memory: 2G + + # ================================================================================== + # QDRANT WORKER (Go) - Vectorización y búsqueda semántica + # ================================================================================== + qdrant-worker: + build: + context: . + dockerfile: Dockerfile.qdrant + container_name: rss2_qdrant_worker + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + QDRANT_HOST: qdrant + QDRANT_PORT: 6333 + QDRANT_COLLECTION: news_vectors + OLLAMA_URL: http://ollama:11434 + QDRANT_SLEEP: 30 + QDRANT_BATCH: 100 + TZ: Europe/Madrid + networks: + - backend + depends_on: + db: + condition: service_healthy + qdrant: + condition: service_started + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 1G + + # ================================================================================== + # NER WORKER (Python) - Extracción de entidades + # ================================================================================== + ner: + build: + context: . + dockerfile: Dockerfile + container_name: rss2_ner + command: bash -lc "python -m workers.ner_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + NER_LANG: es + NER_BATCH: 64 + HF_HOME: /app/hf_cache + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + - ./hf_cache:/app/hf_cache + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 2G + + # ================================================================================== + # CLUSTER WORKER (Python) - Agrupación de noticias + # ================================================================================== + cluster: + build: + context: . + dockerfile: Dockerfile + container_name: rss2_cluster_py + command: bash -lc "python -m workers.cluster_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + EVENT_DIST_THRESHOLD: 0.35 + EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 2G + + # ================================================================================== + # LLM CATEGORIZER (Python) - Categorización con Ollama + # ================================================================================== + llm-categorizer: + build: + context: . + dockerfile: Dockerfile + container_name: rss2_llm_categorizer + command: bash -lc "python -m workers.simple_categorizer_worker" + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${DB_NAME:-rss} + DB_USER: ${DB_USER:-rss} + DB_PASS: ${DB_PASS} + CATEGORIZER_BATCH_SIZE: 10 + CATEGORIZER_SLEEP_IDLE: 5 + TZ: Europe/Madrid + volumes: + - ./workers:/app/workers + networks: + - backend + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 1G + + # ================================================================================== + # MONITORING STACK - SECURED + # ================================================================================== + + prometheus: + image: prom/prometheus:latest + container_name: rss2_prometheus + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + # SEGURIDAD: Sin exposición de puertos - acceso solo vía Grafana o túnel SSH + # ports: + # - "9090:9090" + networks: + - monitoring + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 2G + + grafana: + image: grafana/grafana:latest + container_name: rss2_grafana + # SEGURIDAD: Acceso solo en localhost o vía túnel SSH + # Para acceso remoto, usar túnel SSH: ssh -L 3001:localhost:3001 user@server + ports: + - "127.0.0.1:3001:3000" + environment: + # SEGURIDAD: Cambiar este password en producción + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-change_this_password} + - GF_USERS_ALLOW_SIGN_UP=false + - GF_SERVER_ROOT_URL=http://localhost:3001 + - GF_SECURITY_COOKIE_SECURE=false + - GF_SECURITY_COOKIE_SAMESITE=lax + volumes: + - grafana_data:/var/lib/grafana + networks: + - monitoring + depends_on: + - prometheus + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 1G + + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: rss2_cadvisor + # SEGURIDAD: Sin exposición de puertos - solo acceso interno + # ports: + # - "8081:8080" + volumes: + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + devices: + - /dev/kmsg + networks: + - monitoring + restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + +# ================================================================================== +# REDES SEGMENTADAS +# ================================================================================== +networks: + # Red frontal - Solo nginx y web app + frontend: + name: rss2_frontend + driver: bridge + internal: false + + # Red backend - Base de datos, workers, redis, qdrant + backend: + name: rss2_backend + driver: bridge + internal: false # Acceso externo permitido (necesario para ingestor) + + # Red de monitoreo - Prometheus, Grafana, cAdvisor + monitoring: + name: rss2_monitoring + driver: bridge + internal: true + +volumes: + prometheus_data: + grafana_data: + torch_extensions: diff --git a/generate_secure_credentials.sh b/generate_secure_credentials.sh index d345db7..5bb7b2b 100755 --- a/generate_secure_credentials.sh +++ b/generate_secure_credentials.sh @@ -18,6 +18,33 @@ set -e # Exit on error +# Argumentos para modo automático +FORCE="false" + +# Verificar argumentos +while [[ $# -gt 0 ]]; do + case $1 in + --force) + FORCE="true" + shift + ;; + -h|--help) + echo "Generador de Credenciales Seguras" + echo "" + echo "Uso: ./generate_secure_credentials.sh [--force]" + echo "" + echo "Opciones:" + echo " --force Forzar reemplazo de .env sin preguntar" + echo " --help Mostrar esta ayuda" + exit 0 + ;; + *) + echo "Opción desconocida: $1" + exit 1 + ;; + esac +done + # Colores para output RED='\033[0;31m' GREEN='\033[0;32m' @@ -157,17 +184,22 @@ EOF echo -e "${GREEN}✅ Archivo generado: $ENV_FILE${NC}\n" -# Preguntar si quiere reemplazar .env -echo -e "${YELLOW}¿Deseas reemplazar el archivo .env actual con el generado?${NC}" -echo -e "${YELLOW}(Recomendado: revisa $ENV_FILE primero)${NC}" -read -p "¿Continuar? (s/N): " -n 1 -r -echo -if [[ $REPLY =~ ^[SsYy]$ ]]; then +# Preguntar si quiere reemplazar .env (si no se usa --force) +if [ "$FORCE" = "true" ]; then mv "$ENV_FILE" .env - echo -e "${GREEN}✅ Archivo .env actualizado${NC}" + echo -e "${GREEN}✅ Archivo .env actualizado automáticamente${NC}" else - echo -e "${YELLOW}⚠️ Archivo guardado como: $ENV_FILE${NC}" - echo -e "${YELLOW} Para usarlo: mv $ENV_FILE .env${NC}" + echo -e "${YELLOW}¿Deseas reemplazar el archivo .env actual con el generado?${NC}" + echo -e "${YELLOW}(Recomendado: revisa $ENV_FILE primero)${NC}" + read -p "¿Continuar? (s/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[SsYy]$ ]]; then + mv "$ENV_FILE" .env + echo -e "${GREEN}✅ Archivo .env actualizado${NC}" + else + echo -e "${YELLOW}⚠️ Archivo guardado como: $ENV_FILE${NC}" + echo -e "${YELLOW} Para usarlo: mv $ENV_FILE .env${NC}" + fi fi echo "" diff --git a/pre-deploy.sh b/pre-deploy.sh new file mode 100755 index 0000000..683b0a6 --- /dev/null +++ b/pre-deploy.sh @@ -0,0 +1,411 @@ +#!/bin/bash + +# ================================================================================== +# Pre-Deploy Script - Validación de Credenciales Seguras +# ================================================================================== +# +# Este script se ejecuta antes de docker compose up para: +# 1. Verificar que existen credenciales en .env +# 2. Generar credenciales seguras si faltan +# 3. Validar que las variables críticas estén definidas +# 4. Mostrar resumen de credenciales en consola +# +# Uso: +# ./pre-deploy.sh +# +# Salida: +# - Éxito: Muestra resumen de credenciales y permite continuar +# - Error: Sale con código 1 si faltan credenciales críticas +# +# ================================================================================== + +set -e # Exit on error + +# Colores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}╔═══════════════════════════════════════════════════════════╗" +echo -e "║ 🔐 Pre-Deploy Security Check - RSS2 Platform ║" +echo -e "╚═══════════════════════════════════════════════════════════╝${NC}\n" + +# Función para mostrar ayuda +show_help() { + cat << EOF +${BLUE}Uso:${NC} + ./pre-deploy.sh [--generate|--skip] + +${BLUE}Opciones:${NC} + --generate Generar credenciales seguras automáticamente + --skip Saltar validación y continuar (NO RECOMENDADO) + +${BLUE}Ejemplos:${NC} + ./pre-deploy.sh # Validar credenciales existentes + ./pre-deploy.sh --generate # Generar nuevas credenciales + ./pre-deploy.sh --skip # Saltar validación + +${YELLOW}Nota:${NC} Las credenciales se guardan en .env para docker compose +EOF +} + +# Verificar argumentos +if [ $# -gt 0 ]; then + case "$1" in + --generate) + echo -e "${GREEN}🔄 Generando credenciales seguras...${NC}\n" + bash ./generate_secure_credentials.sh --force + exit $? + ;; + --skip) + echo -e "${YELLOW}⚠️ Saltando validación de credenciales${NC}" + echo -e "${YELLOW}⚠️ Esto puede causar advertencias de WARN durante el despliegue${NC}" + echo "" + # Leer credenciales de ejemplo si no existen + if [ ! -f .env ]; then + if [ -f .env.example ]; then + cp .env.example .env + echo -e "${GREEN}✅ .env creado desde .env.example${NC}" + elif [ -f .env.secure.example ]; then + cp .env.secure.example .env + echo -e "${GREEN}✅ .env creado desde .env.secure.example${NC}" + else + echo -e "${RED}❌ Error: No hay .env.example para copiar${NC}" + exit 1 + fi + fi + ;; + *) + show_help + exit 1 + ;; + esac +fi + +# Función para generar credenciales seguras +generate_credentials() { + if [ -f ./generate_secure_credentials.sh ]; then + echo -e "${GREEN}🔄 Generando credenciales seguras...${NC}\n" + bash ./generate_secure_credentials.sh --force + return $? + else + echo -e "${RED}❌ Error: generate_secure_credentials.sh no encontrado${NC}" + return 1 + fi +} + +# Verificar si .env existe y tiene contenido +check_env_file() { + if [ ! -f .env ]; then + echo -e "${RED}❌ Error: Archivo .env no encontrado${NC}" + echo -e "${YELLOW}💡 Generando credenciales seguras automáticamente...${NC}" + generate_credentials + return $? + fi + + if [ ! -s .env ]; then + echo -e "${RED}❌ Error: Archivo .env está vacío${NC}" + echo -e "${YELLOW}💡 Generando credenciales seguras automáticamente...${NC}" + generate_credentials + return $? + fi + + echo -e "${GREEN}✅ Archivo .env encontrado${NC}" + return 0 +} + +# Función para leer variables del archivo .env +get_env_var() { + local var_name="$1" + grep "^${var_name}=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '[:space:]' +} + +# Función para validar variables críticas +validate_credentials() { + local missing=0 + local critical_missing=0 + local critical_vars=(POSTGRES_PASSWORD REDIS_PASSWORD DB_PASS) + + echo -e "\n${BLUE}🔍 Validando credenciales críticas...${NC}\n" + + for var in "${critical_vars[@]}"; do + local value + value=$(get_env_var "$var") + + if [ -z "$value" ]; then + echo -e "${RED}❌ FALTA: $var está vacía${NC}" + ((missing++)) + ((critical_missing++)) + else + echo -e "${GREEN}✅ DEFINIDO: $var=${value:0:16}${NC}" + fi + done + + if [ $critical_missing -gt 0 ]; then + return 1 + fi + + return 0 +} + +# Función para mostrar resumen de credenciales +show_credentials_summary() { + echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE}📋 Resumen de Credenciales Activas${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" + + if [ -f .env ]; then + echo "${BLUE}📁 Archivo .env:${NC}" + echo -e " ${GREEN}📍 Ruta:${NC} $(pwd)/.env" + echo -e " ${GREEN}📏 Tamaño:${NC} $(wc -c < .env) bytes" + echo -e " ${GREEN}📊 Líneas:${NC} $(wc -l < .env)" + echo "" + + echo "${BLUE}🔑 Variables Críticas:${NC}" + echo -e " ${GREEN}✅ POSTGRES_PASSWORD:${NC} ✓" + echo -e " ${GREEN}✅ REDIS_PASSWORD:${NC} ✓" + echo -e " ${GREEN}✅ DB_PASS:${NC} ✓" + echo "" + + echo "${BLUE}📝 Otras Variables Importantes:${NC}" + for var in POSTGRES_USER DB_NAME SECRET_KEY GRAFANA_PASSWORD; do + local value + value=$(get_env_var "$var") + if [ -n "$value" ]; then + echo -e " ${GREEN}✅ $var:${NC} ${value:0:12}..." + else + echo -e " ${YELLOW}⚠️ $var:${NC} No definido" + fi + done + else + echo -e "${RED}❌ No se pudo cargar .env${NC}" + return 1 + fi + + echo "" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${GREEN}✅ Validación completada - Listo para desplegar${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" + + # Preguntar si quiere copiar en clipboard (macOS) + if command -v pbcopy >/dev/null 2>&1; then + echo -e "${YELLOW}💡 Para copiar credenciales, usa: pbcopy < .env${NC}" + fi + + return 0 +} + +# Función para mostrar advertencias de seguridad +show_security_warnings() { + echo -e "\n${YELLOW}⚠️ RECOMENDACIONES DE SEGURIDAD:${NC}\n" + + if [ -f .env ]; then + if [ -f .gitignore ]; then + if grep -q "^.env" .gitignore; then + echo -e " ✅ .env está en .gitignore" + else + echo -e " ${RED}❌ .env NO está en .gitignore${NC}" + echo -e " ${YELLOW} → Añade .env al .gitignore${NC}" + fi + else + echo -e " ${YELLOW}⚠️ No hay .gitignore, crea uno para proteger .env${NC}" + fi + fi + + echo "" + echo -e " ✅ Credenciales no están en Dockerfile (se inyectan en runtime)" + echo -e " ✅ Credenciales no están en docker-compose.yml (se cargan desde .env)" + echo -e " ✅ Variables sensibles usan ${NC}${GREEN}recomendados${NC}${BLUE} (no hardcoded)" + echo "" +} + +# Función para desplegar con docker compose +deploy_with_compose() { + echo -e "\n${GREEN}🚀 DESPLIEGUE AUTOMÁTICO${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + + # Detener y limpiar contenedores + echo -e "${YELLOW}📦 Deteniendo contenedores...${NC}" + docker compose down -v 2>/dev/null || true + + # Iniciar servicios + echo -e "${GREEN}📦 Construyendo e iniciando servicios...${NC}" + docker compose up -d --build + + # Esperar a que los servicios estén saludables + echo -e "${GREEN}✅ Esperando a que los servicios estén listos...${NC}" + sleep 5 + + # Verificar estado + echo -e "${BLUE}📊 Estado de los servicios:${NC}" + docker compose ps + + # Verificar base de datos + echo -e "${BLUE}🔍 Verificando base de datos...${NC}" + if docker compose ps db | grep -q "healthy\|Up"; then + echo -e "${GREEN}✅ Base de datos: Iniciada${NC}" + + # Esperar a que el schema se inicialice + echo -e "${GREEN}✅ Esperando inicialización del schema...${NC}" + sleep 10 + + # Ejecutar scripts SQL si existen + if [ -d init-db ] && [ "$(ls -A init-db 2>/dev/null)" ]; then + echo -e "${YELLOW}📄 Ejecutando scripts de inicialización...${NC}" + docker compose exec -T db psql -U rss -d rss -f /docker-entrypoint-initdb.d/*.sql 2>&1 | head -20 || true + fi + + # Verificar Redis + echo -e "${BLUE}🔍 Verificando Redis...${NC}" + sleep 5 + if docker compose ps redis | grep -q "Up"; then + echo -e "${GREEN}✅ Redis: Iniciado${NC}" + fi + else + echo -e "${RED}❌ Error: Base de datos no se inició correctamente${NC}" + docker compose logs db + return 1 + fi + + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${GREEN}✅ DESPLIEGUE COMPLETADO${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + echo -e "${BLUE}🌐 Endpoints disponibles:${NC}" + echo -e " ${GREEN}App:${NC} http://localhost:8888" + echo -e " ${GREEN}Backend API:${NC} http://localhost:8888/api" + echo -e " ${GREEN}Grafana:${NC} http://127.0.0.1:3001" + echo -e " ${GREEN}Prometheus:${NC} http://127.0.0.1:9090" + echo "" + + # Verificar si existe .env.backup y restaurarlo + if [ -f .env.backup ]; then + echo -e "${YELLOW}⚠️ Nota: Se encontró .env.backup, puedes restaurarlo si fue necesario${NC}" + fi + + return 0 +} + +# Función principal +main() { + # Verificar que .env exista y tenga contenido + if ! check_env_file; then + return 1 + fi + + # Validar credenciales críticas + if ! validate_credentials; then + echo -e "\n${RED}❌ Error: Faltan credenciales críticas${NC}" + echo -e "${YELLOW}💡 Ejecuta: ./pre-deploy.sh --generate${NC}" + return 1 + fi + + # Mostrar resumen de credenciales + if ! show_credentials_summary; then + return 1 + fi + + # Mostrar advertencias de seguridad + show_security_warnings + + echo -e "\n${GREEN}╔═══════════════════════════════════════════════════════════╗" + echo -e "║ ✅ PRE-DEPLOY VALIDACIÓN EXITOSA - LISTO PARA INICIAR ║" + echo -e "╚═══════════════════════════════════════════════════════════╝${NC}\n" + + # Preguntar si quiere desplegar automáticamente + echo -e "${BLUE}¿Deseas desplegar ahora?${NC}" + echo -e " 1) Desplegar automáticamente (recomendado)" + echo -e " 2) Solo validar y salir (modo manual)" + echo -e " " + read -p " Elige una opción: " choice + + case $choice in + 1) + if deploy_with_compose; then + return 0 + else + echo -e "\n${RED}❌ Error en el despliegue${NC}" + return 1 + fi + ;; + 2) + echo -e "\n${YELLOW}✅ Validación completada - Salir${NC}" + echo -e "${BLUE}▶️ Para desplegar manualmente, ejecuta:${NC}" + echo -e " ${GREEN}docker compose up -d${NC}" + echo "" + return 0 + ;; + *) + echo -e "${YELLOW}⚠️ Opción no válida, usando opción 2${NC}" + return 0 + ;; + esac +} + +# Ejecutar +main + +# ================================================================================== +# Verificación de modelo GPU (después del despliegue) +# ================================================================================== +check_gpu_model() { + MODEL_DIR="./models/nllb-ct2-1.3b" + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 🤖 Verificando modelo de traducción GPU" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + if [ ! -d "$MODEL_DIR" ]; then + echo " ⚠️ Directorio de modelo no existe" + return 0 + fi + + model_ok=true + + # Verificar model.bin + if [ -f "$MODEL_DIR/model.bin" ]; then + size=$(stat -c%s "$MODEL_DIR/model.bin" 2>/dev/null || echo 0) + if [ "$size" -gt 1000000 ]; then + echo " ✓ model.bin ($(numfmt --to=iec-i --suffix=B $size 2>/dev/null || echo "${size}B"))" + else + echo " ✗ model.bin demasiado pequeño" + model_ok=false + fi + else + echo " ✗ model.bin no encontrado" + model_ok=false + fi + + # Verificar config.json + if [ -f "$MODEL_DIR/config.json" ]; then + echo " ✓ config.json" + else + echo " ✗ config.json no encontrado" + model_ok=false + fi + + # Verificar shared_vocabulary.json + if [ -f "$MODEL_DIR/shared_vocabulary.json" ]; then + echo " ✓ shared_vocabulary.json" + else + echo " ✗ shared_vocabulary.json no encontrado" + model_ok=false + fi + + echo "" + if [ "$model_ok" = true ]; then + echo " ✓ Modelo verificado - workers GPU iniciarán rápidamente" + else + echo " ⚠️ Modelo incompleto - primer worker lo convertirá (5-10 min)" + echo "" + echo " Pre-convertir manualmente:" + echo " docker run --rm -v \$(pwd)/models:/app/models rss2-translator-gpu:latest \\" + echo " ct2-transformers-converter --model facebook/nllb-200-1.3B \\" + echo " --output_dir /app/models/nllb-ct2-1.3b --quantization float16 --force" + fi +} + +check_gpu_model diff --git a/prepare_model.sh b/prepare_model.sh new file mode 100644 index 0000000..5d9ca26 --- /dev/null +++ b/prepare_model.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# ============================================================================== +# Pre-deployment model preparation for translator-gpu +# Ensures the NLLB-1.3B model is properly converted before starting workers +# ============================================================================== + +set -e + +MODEL_DIR="./models/nllb-ct2-1.3b" +REQUIRED_FILES=("model.bin" "config.json" "shared_vocabulary.json") +MIN_SIZES=(1000000 100 1000000) # Minimum sizes in bytes + +echo "========================================" +echo " Pre-Deploy Model Preparation" +echo "========================================" + +# Check if model directory exists +if [ ! -d "$MODEL_DIR" ]; then + echo "Creating model directory..." + mkdir -p "$MODEL_DIR" +fi + +# Check if all required files exist and have minimum size +all_present=true +for i in "${!REQUIRED_FILES[@]}"; do + file="${REQUIRED_FILES[$i]}" + min_size="${MIN_SIZES[$i]}" + + if [ -f "$MODEL_DIR/$file" ]; then + size=$(stat -c%s "$MODEL_DIR/$file" 2>/dev/null || stat -f%z "$MODEL_DIR/$file" 2>/dev/null || echo 0) + if [ "$size" -lt "$min_size" ]; then + echo " ✗ $file is too small ($size bytes), needs re-conversion" + all_present=false + break + else + echo " ✓ $file OK ($(numfmt --to=iec-i --suffix=B $size))" + fi + else + echo " ✗ $file missing" + all_present=false + fi +done + +if [ "$all_present" = true ]; then + echo "" + echo "✓ Model files verified - workers can start immediately" + exit 0 +fi + +echo "" +echo "⚠ Model incomplete or missing - will be converted by first worker" +echo " This may take 5-10 minutes on first deployment" +echo "" +echo "To pre-convert the model manually (optional):" +echo " docker run --rm -v \$(pwd)/models:/app/models rss2-translator-gpu:latest \\" +echo " ct2-transformers-converter --model facebook/nllb-200-1.3B \\" +echo " --output_dir /app/models/nllb-ct2-1.3b --quantization float16 --force" +echo "" + +exit 0 \ No newline at end of file diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 0dc1126..1000186 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -2,6 +2,7 @@ import os import time import logging import re +import fcntl from typing import List, Optional import psycopg2 @@ -19,19 +20,21 @@ LOG = logging.getLogger("translator_ct2") TRANSLATOR_ID = os.environ.get("TRANSLATOR_ID", "") TRANSLATOR_TOTAL = int(os.environ.get("TRANSLATOR_TOTAL", "1")) + def clean_text(text: str) -> str: if not text: return "" - text = re.sub(r'<[^>]+>', '', text) - text = text.replace('', '') - text = text.replace(' ', ' ') - text = text.replace('&', '&') - text = text.replace('<', '<') - text = text.replace('>', '>') - text = text.replace('"', '"') - text = re.sub(r'\s+', ' ', text) + text = re.sub(r"<[^>]+>", "", text) + text = text.replace("", "") + text = text.replace(" ", " ") + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + text = text.replace(""", '"') + text = re.sub(r"\s+", " ", text) return text.strip() + DB_CONFIG = { "host": os.environ.get("DB_HOST", "localhost"), "port": int(os.environ.get("DB_PORT", 5432)), @@ -40,12 +43,14 @@ DB_CONFIG = { "password": os.environ.get("DB_PASS", "x"), } + def _env_list(name: str, default="es"): raw = os.environ.get(name) if raw: return [s.strip() for s in raw.split(",") if s.strip()] return [default] + def _env_int(name: str, default: int = 8): v = os.environ.get(name) try: @@ -53,14 +58,16 @@ def _env_int(name: str, default: int = 8): except Exception: return default + def _env_str(name: str, default=None): v = os.environ.get(name) return v if v else default + TARGET_LANGS = _env_list("TARGET_LANGS") -BATCH_SIZE = _env_int("TRANSLATOR_BATCH", 8) -MAX_SRC_TOKENS = _env_int("MAX_SRC_TOKENS", 512) -MAX_NEW_TOKENS = _env_int("MAX_NEW_TOKENS", 512) +BATCH_SIZE = _env_int("TRANSLATOR_BATCH", 128) +MAX_SRC_TOKENS = _env_int("MAX_SRC_TOKENS", 256) +MAX_NEW_TOKENS = _env_int("MAX_NEW_TOKENS", 256) CT2_MODEL_PATH = _env_str("CT2_MODEL_PATH", "/app/models/nllb-ct2") CT2_DEVICE = _env_str("CT2_DEVICE", "cpu") @@ -69,87 +76,160 @@ UNIVERSAL_MODEL = _env_str("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M" BODY_CHARS_CHUNK = _env_int("BODY_CHARS_CHUNK", 900) LANG_CODE_MAP = { - "en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn", - "it": "ita_Latn", "pt": "por_Latn", "nl": "nld_Latn", "sv": "swe_Latn", - "da": "dan_Latn", "fi": "fin_Latn", "no": "nob_Latn", - "pl": "pol_Latn", "cs": "ces_Latn", "sk": "slk_Latn", - "sl": "slv_Latn", "hu": "hun_Latn", "ro": "ron_Latn", - "el": "ell_Grek", "ru": "rus_Cyrl", "uk": "ukr_Cyrl", - "tr": "tur_Latn", "ar": "arb_Arab", "fa": "pes_Arab", - "he": "heb_Hebr", "zh": "zho_Hans", "ja": "jpn_Jpan", - "ko": "kor_Hang", "vi": "vie_Latn", + "en": "eng_Latn", + "es": "spa_Latn", + "fr": "fra_Latn", + "de": "deu_Latn", + "it": "ita_Latn", + "pt": "por_Latn", + "nl": "nld_Latn", + "sv": "swe_Latn", + "da": "dan_Latn", + "fi": "fin_Latn", + "no": "nob_Latn", + "pl": "pol_Latn", + "cs": "ces_Latn", + "sk": "slk_Latn", + "sl": "slv_Latn", + "hu": "hun_Latn", + "ro": "ron_Latn", + "el": "ell_Grek", + "ru": "rus_Cyrl", + "uk": "ukr_Cyrl", + "tr": "tur_Latn", + "ar": "arb_Arab", + "fa": "pes_Arab", + "he": "heb_Hebr", + "zh": "zho_Hans", + "ja": "jpn_Jpan", + "ko": "kor_Hang", + "vi": "vie_Latn", } _tokenizer = None _translator = None + def ensure_model(): global _tokenizer, _translator - + if _translator: return - + model_path = CT2_MODEL_PATH model_bin = os.path.join(model_path, "model.bin") - - if not os.path.exists(model_bin): - LOG.info(f"CTranslate2 model not found at {model_path}, converting from {UNIVERSAL_MODEL}...") + + # Check if model exists AND is complete (all required files present and non-empty) + required_files = ["model.bin", "config.json", "shared_vocabulary.json"] + model_exists = os.path.exists(model_bin) + + if model_exists: + # Verify all files exist and have reasonable size + all_files_ok = True + for f in required_files: + fpath = os.path.join(model_path, f) + if not os.path.exists(fpath) or os.path.getsize(fpath) < 1000: + all_files_ok = False + break + + if not all_files_ok: + LOG.info(f"Model files incomplete or corrupted, re-converting...") + # Clean up corrupted files + for f in required_files: + try: + fpath = os.path.join(model_path, f) + if os.path.exists(fpath): + os.remove(fpath) + except: + pass + model_exists = False + + if not model_exists: + LOG.info( + f"CTranslate2 model not found at {model_path}, converting from {UNIVERSAL_MODEL}..." + ) convert_model() - - LOG.info(f"Loading CTranslate2 model from {model_path} on {CT2_DEVICE}") - + + device = os.environ.get("CT2_DEVICE", "cpu") + LOG.info(f"Loading CTranslate2 model from {model_path} on {device}") + _translator = ctranslate2.Translator( model_path, - device=CT2_DEVICE, + device=device, compute_type=CT2_COMPUTE_TYPE, ) - + _tokenizer = AutoTokenizer.from_pretrained(UNIVERSAL_MODEL) LOG.info("CTranslate2 model loaded successfully") + def convert_model(): import subprocess - + model_path = CT2_MODEL_PATH + lock_file = os.path.join(model_path, ".converting.lock") + + # Clean up any corrupted files from previous failed conversions + if os.path.exists(model_path): + for f in os.listdir(model_path): + if f.endswith(".lock") or f.startswith("."): + try: + os.remove(os.path.join(model_path, f)) + except: + pass + os.makedirs(model_path, exist_ok=True) - - quantization = CT2_COMPUTE_TYPE if CT2_COMPUTE_TYPE != "auto" else "int8" - - cmd = [ - "ct2-transformers-converter", - "--model", UNIVERSAL_MODEL, - "--output_dir", model_path, - "--quantization", quantization, - "--force" - ] - - LOG.info(f"Running: {' '.join(cmd)}") - result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800) - - if result.returncode != 0: - LOG.error(f"Model conversion failed: {result.stderr}") - raise RuntimeError("Failed to convert model") - - LOG.info("Model conversion completed") + + # Use lock file to prevent multiple workers from converting simultaneously + lock_fd = os.open(lock_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + try: + quantization = CT2_COMPUTE_TYPE if CT2_COMPUTE_TYPE != "auto" else "float16" + + cmd = [ + "ct2-transformers-converter", + "--model", + UNIVERSAL_MODEL, + "--output_dir", + model_path, + "--quantization", + quantization, + "--force", + ] + + LOG.info(f"Running: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600) + + if result.returncode != 0: + LOG.error(f"Model conversion failed: {result.stderr}") + raise RuntimeError("Failed to convert model") + + LOG.info("Model conversion completed") + finally: + os.close(lock_fd) + try: + os.remove(lock_file) + except: + pass + def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: if not texts: return [] - + ensure_model() - + clean = [(t or "").strip() for t in texts] if all(not t for t in clean): return ["" for _ in clean] - + src_code = LANG_CODE_MAP.get(src, f"{src}_Latn") tgt_code = LANG_CODE_MAP.get(tgt, "spa_Latn") - + try: _tokenizer.src_lang = src_code except Exception: pass - + sources = [] for t in clean: if t: @@ -158,18 +238,18 @@ def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: sources.append(tokens) else: sources.append([]) - + target_prefix = [[tgt_code]] * len(sources) - + results = _translator.translate_batch( sources, target_prefix=target_prefix, - beam_size=2, + beam_size=1, max_decoding_length=MAX_NEW_TOKENS, - repetition_penalty=2.0, - no_repeat_ngram_size=3, + repetition_penalty=1.2, + no_repeat_ngram_size=2, ) - + translated = [] for result in results: try: @@ -197,18 +277,19 @@ def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: except Exception as e: LOG.error(f"Error processing result: {e}") translated.append("") - + return translated + def split_body_into_chunks(text: str) -> List[str]: text = (text or "").strip() if len(text) <= BODY_CHARS_CHUNK: return [text] if text else [] - - parts = re.split(r'(\n\n+|(?<=[\.\!\?؛؟。])\s+)', text) + + parts = re.split(r"(\n\n+|(?<=[\.\!\?؛؟。])\s+)", text) chunks = [] current = "" - + for part in parts: if not part: continue @@ -220,31 +301,34 @@ def split_body_into_chunks(text: str) -> List[str]: current = part if current.strip(): chunks.append(current.strip()) - + return chunks if chunks else [text] + def translate_body_long(src: str, tgt: str, body: str) -> str: body = (body or "").strip() if not body: return "" - + chunks = split_body_into_chunks(body) if len(chunks) == 1: return translate_texts(src, tgt, [body])[0] - + translated_chunks = [] for ch in chunks: tr = translate_texts(src, tgt, [ch])[0] translated_chunks.append(tr) - + return " ".join(translated_chunks) + def normalize_lang(lang: Optional[str], default: str = "es") -> Optional[str]: if not lang: return default lang = lang.strip().lower()[:2] return lang if lang else default + def detect_lang(text: str) -> str: if not text or len(text) < 10: return "en" @@ -253,63 +337,75 @@ def detect_lang(text: str) -> str: except Exception: return "en" + def process_batch(conn, rows): todo = [] - + for r in rows: lang_to = normalize_lang(r.get("lang_to"), "es") or "es" - lang_from = normalize_lang(r.get("lang_from")) or detect_lang(r.get("titulo") or "") - + lang_from = normalize_lang(r.get("lang_from")) or detect_lang( + r.get("titulo") or "" + ) + titulo = (r.get("titulo") or "").strip() resumen = (r.get("resumen") or "").strip() - + if lang_from == lang_to: # Mark as done and copy original text if languages match cursor = conn.cursor() - cursor.execute(""" + cursor.execute( + """ UPDATE traducciones SET titulo_trad = %s, resumen_trad = %s, status = 'done' WHERE id = %s - """, (titulo, resumen, r.get("tr_id"))) + """, + (titulo, resumen, r.get("tr_id")), + ) conn.commit() cursor.close() continue - - todo.append({ - "tr_id": r.get("tr_id"), - "lang_from": lang_from, - "lang_to": lang_to, - "titulo": titulo, - "resumen": resumen, - }) - + + todo.append( + { + "tr_id": r.get("tr_id"), + "lang_from": lang_from, + "lang_to": lang_to, + "titulo": titulo, + "resumen": resumen, + } + ) + if not todo: return - + # 1. FAST LOCKING: Commit locked_at immediately to inform other workers cursor = conn.cursor() tr_ids = [item["tr_id"] for item in todo] - cursor.execute(f""" + cursor.execute( + f""" UPDATE traducciones SET locked_at = NOW() - WHERE id = ANY(ARRAY[{','.join(['%s'] * len(tr_ids))}]) - """, tr_ids) + WHERE id = ANY(ARRAY[{",".join(["%s"] * len(tr_ids))}]) + """, + tr_ids, + ) conn.commit() cursor.close() - + from collections import defaultdict + groups = defaultdict(list) for item in todo: key = (item["lang_from"], item["lang_to"]) groups[key].append(item) - + for (lang_from, lang_to), items in groups.items(): LOG.info(f"Translating {lang_from} -> {lang_to} ({len(items)} items)") - + try: titles = [i["titulo"] for i in items] translated_titles = translate_texts(lang_from, lang_to, titles) - + for item, tt in zip(items, translated_titles): body = (item["resumen"] or "").strip() tb = "" @@ -319,52 +415,61 @@ def process_batch(conn, rows): except Exception as e: LOG.error(f"Body translation error for ID {item['tr_id']}: {e}") tb = item["resumen"] - + tt = clean_text((tt or "").strip()) tb = clean_text((tb or "").strip()) - + if not tt: tt = item["titulo"] if not tb: tb = item["resumen"] - + # 2. INDIVIDUAL COMMIT: Save each item as it's done try: cursor = conn.cursor() - cursor.execute(""" + cursor.execute( + """ UPDATE traducciones SET titulo_trad = %s, resumen_trad = %s, status = 'done', locked_at = NULL WHERE id = %s - """, (tt, tb, item["tr_id"])) + """, + (tt, tb, item["tr_id"]), + ) conn.commit() cursor.close() except Exception as e: LOG.error(f"Update error for ID {item['tr_id']}: {e}") conn.rollback() - + LOG.info(f"Finished group {lang_from} -> {lang_to}") - + except Exception as e: LOG.error(f"Batch group error {lang_from} -> {lang_to}: {e}") # Mark these as error to avoid infinite loop if it's a model crash try: cursor = conn.cursor() - cursor.execute(""" + cursor.execute( + """ UPDATE traducciones SET status = 'error', locked_at = NULL WHERE id = ANY(ARRAY[{','.join(['%s'] * len(items))}]) - """, [i["tr_id"] for i in items]) + """, + [i["tr_id"] for i in items], + ) conn.commit() cursor.close() except: conn.rollback() + def fetch_pending_translations(conn): cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - + worker_id = os.environ.get("HOSTNAME", f"worker-{os.getpid()}") - + total_found = 0 + for lang in TARGET_LANGS: - cursor.execute(""" + cursor.execute( + """ SELECT t.id as tr_id, t.lang_from, t.lang_to, n.titulo, n.resumen, n.id as noticia_id FROM traducciones t @@ -375,31 +480,45 @@ def fetch_pending_translations(conn): ORDER BY n.fecha DESC LIMIT %s FOR UPDATE SKIP LOCKED - """, (lang, BATCH_SIZE)) - + """, + (lang, BATCH_SIZE), + ) + rows = cursor.fetchall() if rows: LOG.info(f"Found {len(rows)} pending translations for {lang}") process_batch(conn, rows) - + total_found += len(rows) + cursor.close() + return total_found + def connect_db(): return psycopg2.connect(**DB_CONFIG) + def main(): - LOG.info(f"CTranslate2 translator worker started (device={CT2_DEVICE}, instances={TRANSLATOR_TOTAL})") + LOG.info( + f"CTranslate2 translator worker started (device={CT2_DEVICE}, instances={TRANSLATOR_TOTAL})" + ) ensure_model() - + while True: try: conn = connect_db() - fetch_pending_translations(conn) + total = fetch_pending_translations(conn) conn.close() + + if total == 0: + LOG.info("No pending translations, sleeping...") + else: + LOG.info(f"Processed {total} translations, sleeping...") except Exception as e: LOG.error(f"Error: {e}") - + time.sleep(30) + if __name__ == "__main__": main() From a285021e6c48e81586bcf4af09811ecd2fbacfe6 Mon Sep 17 00:00:00 2001 From: jlimolina Date: Mon, 10 Aug 2026 15:25:41 +0200 Subject: [PATCH 16/21] =?UTF-8?q?fix(backup):=20corregir=20descarga=20y=20?= =?UTF-8?q?a=C3=B1adir=20restauraci=C3=B3n=20de=20BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backend usa DATABASE_URL (resolvePgConnInfo) en vez de DB_* que no existían en el contenedor API, arreglando el fallo de pg_dump - Dockerfile backend: alpine 3.23 para pg_dump 18.4 (compatible con PostgreSQL 18 del servidor) - BackupDatabase ya no usa docker exec (no disponible en la imagen) - Nuevo endpoint POST /api/admin/restore para restaurar .sql o .zip vía psql - UI de Configuración: sección Restaurar Base de Datos - Regenerado go.sum para el build --- backend/Dockerfile | 2 +- backend/cmd/qdrant/main.go | 139 +- backend/cmd/server/main.go | 1 + backend/go.mod | 4 +- backend/go.sum | 158 + backend/internal/handlers/admin.go | 203 +- backend/internal/handlers/feed.go | 215 +- backend/internal/handlers/search.go | 83 +- backend/internal/handlers/worker_ws.go | 7 +- backup.sh | 16 + docker-compose.local.yml | 10 + docker-compose.yml | 193 +- docker-entrypoint-db.sh | 26 +- feeds.csv | 6822 +++++++++++++++++++- frontend/src/pages/AdminSettings.tsx | 90 +- frontend/src/services/api.ts | 11 +- init-db/36-add_eventos_cluster_columns.sql | 11 + init-db/37-topics-seed.sql | 18 + init-db/38-add_news_topics_score.sql | 7 + init-db/39-add_related_traduccion_id.sql | 10 + init-db/40-add_vectorization_columns.sql | 2 + workers/ctranslator_worker.py | 46 +- 22 files changed, 7624 insertions(+), 450 deletions(-) create mode 100644 backend/go.sum create mode 100755 backup.sh create mode 100644 docker-compose.local.yml create mode 100644 init-db/36-add_eventos_cluster_columns.sql create mode 100644 init-db/37-topics-seed.sql create mode 100644 init-db/38-add_news_topics_score.sql create mode 100644 init-db/39-add_related_traduccion_id.sql create mode 100644 init-db/40-add_vectorization_columns.sql diff --git a/backend/Dockerfile b/backend/Dockerfile index 5f04721..0723c1a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -12,7 +12,7 @@ RUN go mod tidy RUN CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o /server ./cmd/server -FROM alpine:3.19 +FROM alpine:3.23 RUN apk add --no-cache ca-certificates tzdata postgresql-client diff --git a/backend/cmd/qdrant/main.go b/backend/cmd/qdrant/main.go index 73701d7..bf9f054 100644 --- a/backend/cmd/qdrant/main.go +++ b/backend/cmd/qdrant/main.go @@ -11,6 +11,7 @@ import ( "os" "os/signal" "strconv" + "strings" "syscall" "time" @@ -23,7 +24,6 @@ var ( logger *log.Logger dbPool *pgxpool.Pool qdrantURL string - ollamaURL string collection = "news_vectors" sleepSec = 30 batchSize = 100 @@ -39,7 +39,6 @@ func loadConfig() { qdrantHost := getEnv("QDRANT_HOST", "localhost") qdrantPort := getEnvInt("QDRANT_PORT", 6333) qdrantURL = fmt.Sprintf("http://%s:%d", qdrantHost, qdrantPort) - ollamaURL = getEnv("OLLAMA_URL", "http://ollama:11434") collection = getEnv("QDRANT_COLLECTION", "news_vectors") } @@ -61,7 +60,7 @@ func getEnvInt(key string, defaultValue int) int { type Translation struct { ID int64 - NoticiaID int64 + NoticiaID string Lang string Titulo string Resumen string @@ -70,6 +69,7 @@ type Translation struct { FuenteNombre string CategoriaID *int64 PaisID *int64 + Embedding []float64 } func getPendingTranslations(ctx context.Context) ([]Translation, error) { @@ -84,9 +84,11 @@ func getPendingTranslations(ctx context.Context) ([]Translation, error) { n.fecha, n.fuente_nombre, n.categoria_id, - n.pais_id + n.pais_id, + te.embedding::text FROM traducciones t INNER JOIN noticias n ON t.noticia_id = n.id + INNER JOIN traduccion_embeddings te ON te.traduccion_id = t.id WHERE t.vectorized = FALSE AND t.status = 'done' ORDER BY t.created_at ASC @@ -100,54 +102,41 @@ func getPendingTranslations(ctx context.Context) ([]Translation, error) { var translations []Translation for rows.Next() { var t Translation + var embText string if err := rows.Scan( &t.ID, &t.NoticiaID, &t.Lang, &t.Titulo, &t.Resumen, &t.URL, &t.Fecha, &t.FuenteNombre, &t.CategoriaID, &t.PaisID, + &embText, ); err != nil { + logger.Printf("Error scanning translation row: %v", err) continue } + emb, err := parseEmbedding(embText) + if err != nil { + logger.Printf("Error parsing embedding for translation %d: %v", t.ID, err) + continue + } + t.Embedding = emb translations = append(translations, t) } return translations, nil } -type EmbeddingRequest struct { - Model string `json:"model"` - Input string `json:"input"` -} - -type EmbeddingResponse struct { - Embedding []float64 `json:"embedding"` -} - -func generateEmbedding(text string) ([]float64, error) { - reqBody := EmbeddingRequest{ - Model: "mxbai-embed-large", - Input: text, +func parseEmbedding(s string) ([]float64, error) { + s = strings.Trim(strings.TrimSpace(s), "{}") + if s == "" { + return nil, fmt.Errorf("empty embedding") } - - body, err := json.Marshal(reqBody) - if err != nil { - return nil, err + parts := strings.Split(s, ",") + emb := make([]float64, len(parts)) + for i, p := range parts { + v, err := strconv.ParseFloat(strings.TrimSpace(p), 64) + if err != nil { + return nil, err + } + emb[i] = v } - - client := &http.Client{Timeout: 60 * time.Second} - resp, err := client.Post(ollamaURL+"/api/embeddings", "application/json", bytes.NewReader(body)) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Ollama returned status %d", resp.StatusCode) - } - - var result EmbeddingResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, err - } - - return result.Embedding, nil + return emb, nil } type QdrantPoint struct { @@ -160,7 +149,7 @@ type QdrantUpsertRequest struct { Points []QdrantPoint `json:"points"` } -func ensureCollection() error { +func ensureCollection(ctx context.Context) error { req, err := http.NewRequest("GET", qdrantURL+"/collections/"+collection, nil) if err != nil { return err @@ -177,16 +166,15 @@ func ensureCollection() error { return nil } - // Get embedding dimension - emb, err := generateEmbedding("test") + // Get embedding dimension from stored embeddings + var dimension int + err = dbPool.QueryRow(ctx, `SELECT dim FROM traduccion_embeddings LIMIT 1`).Scan(&dimension) if err != nil { return fmt.Errorf("failed to get embedding dimension: %w", err) } - dimension := len(emb) // Create collection createReq := map[string]interface{}{ - "name": collection, "vectors": map[string]interface{}{ "size": dimension, "distance": "Cosine", @@ -194,26 +182,35 @@ func ensureCollection() error { } body, _ := json.Marshal(createReq) - resp2, err := http.Post(qdrantURL+"/collections", "application/json", bytes.NewReader(body)) + createURL := qdrantURL + "/collections/" + collection + req2, err := http.NewRequest(http.MethodPut, createURL, bytes.NewReader(body)) + if err != nil { + return err + } + req2.Header.Set("Content-Type", "application/json") + resp2, err := http.DefaultClient.Do(req2) if err != nil { return err } defer resp2.Body.Close() + if resp2.StatusCode != http.StatusOK { + respBody, _ := io.ReadAll(resp2.Body) + return fmt.Errorf("Qdrant create collection returned %d: %s", resp2.StatusCode, string(respBody)) + } + logger.Printf("Created collection %s with dimension %d", collection, dimension) return nil } -func uploadToQdrant(translations []Translation, embeddings [][]float64) error { +func uploadToQdrant(translations []Translation, pointIDs []string) error { points := make([]QdrantPoint, 0, len(translations)) for i, t := range translations { - if embeddings[i] == nil { + if t.Embedding == nil || len(t.Embedding) == 0 { continue } - pointID := uuid.New().String() - payload := map[string]interface{}{ "news_id": t.NoticiaID, "traduccion_id": t.ID, @@ -235,8 +232,8 @@ func uploadToQdrant(translations []Translation, embeddings [][]float64) error { } points = append(points, QdrantPoint{ - ID: pointID, - Vector: embeddings[i], + ID: pointIDs[i], + Vector: t.Embedding, Payload: payload, }) } @@ -252,7 +249,12 @@ func uploadToQdrant(translations []Translation, embeddings [][]float64) error { } url := fmt.Sprintf("%s/collections/%s/points", qdrantURL, collection) - resp, err := http.Post(url, "application/json", bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(body)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) if err != nil { return err } @@ -317,7 +319,7 @@ func main() { ctx := context.Background() - if err := ensureCollection(); err != nil { + if err := ensureCollection(ctx); err != nil { logger.Printf("Warning: Could not ensure collection: %v", err) } @@ -330,8 +332,8 @@ func main() { os.Exit(0) }() - logger.Printf("Config: qdrant=%s, ollama=%s, collection=%s, sleep=%ds, batch=%d", - qdrantURL, ollamaURL, collection, sleepSec, batchSize) + logger.Printf("Config: qdrant=%s, collection=%s, sleep=%ds, batch=%d", + qdrantURL, collection, sleepSec, batchSize) totalProcessed := 0 @@ -351,30 +353,19 @@ func main() { logger.Printf("Processing %d translations...", len(translations)) - // Generate embeddings - embeddings := make([][]float64, len(translations)) - for i, t := range translations { - text := fmt.Sprintf("%s %s", t.Titulo, t.Resumen) - emb, err := generateEmbedding(text) - if err != nil { - logger.Printf("Error generating embedding for %d: %v", t.ID, err) - continue - } - embeddings[i] = emb - } - - // Upload to Qdrant - if err := uploadToQdrant(translations, embeddings); err != nil { - logger.Printf("Error uploading to Qdrant: %v", err) - continue - } - - // Update DB status + // Generate point IDs once (used both in Qdrant and DB) pointIDs := make([]string, len(translations)) for i := range translations { pointIDs[i] = uuid.New().String() } + // Upload to Qdrant + if err := uploadToQdrant(translations, pointIDs); err != nil { + logger.Printf("Error uploading to Qdrant: %v", err) + continue + } + + // Update DB status if err := updateTranslationStatus(ctx, translations, pointIDs); err != nil { logger.Printf("Error updating status: %v", err) } diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 2f3abf7..bfa59bd 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -187,6 +187,7 @@ func main() { admin.POST("/entities/retype", handlers.PatchEntityTipo) admin.GET("/backup", handlers.BackupDatabase) admin.GET("/backup/news", handlers.BackupNewsZipped) + admin.POST("/restore", handlers.RestoreDatabase) admin.GET("/users", handlers.GetUsers) admin.POST("/users/:id/promote", handlers.PromoteUser) admin.POST("/users/:id/demote", handlers.DemoteUser) diff --git a/backend/go.mod b/backend/go.mod index 54449fd..bd2af34 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,6 +1,8 @@ module github.com/rss2/backend -go 1.22 +go 1.23 + +toolchain go1.23.12 require ( github.com/PuerkitoBio/goquery v1.9.2 diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..bfa3dc0 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,158 @@ +github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= +github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= +github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s= +github.com/mmcdole/gofeed v1.2.1/go.mod h1:2wVInNpgmC85q16QTTuwbuKxtKkHLCDDtf0dCmnrNr4= +github.com/mmcdole/goxpp v1.1.0 h1:WwslZNF7KNAXTFuzRtn/OKZxFLJAAyOA9w82mDz2ZGI= +github.com/mmcdole/goxpp v1.1.0/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/backend/internal/handlers/admin.go b/backend/internal/handlers/admin.go index 0e40f80..48c9e9f 100644 --- a/backend/internal/handlers/admin.go +++ b/backend/internal/handlers/admin.go @@ -6,14 +6,15 @@ import ( "context" "encoding/csv" "fmt" + "io" "log" "net/http" + "net/url" "os" "os/exec" "strconv" "strings" "time" - "github.com/gin-gonic/gin" "github.com/rss2/backend/internal/config" "github.com/rss2/backend/internal/db" @@ -637,17 +638,73 @@ func PatchEntityTipo(c *gin.Context) { }) } +// pgConnInfo holds PostgreSQL connection parameters for pg_dump +type pgConnInfo struct { + host, port, name, user, pass string +} + +// resolvePgConnInfo prefers DATABASE_URL (the variable the API container gets) +// and falls back to the individual DB_* variables used by the workers. +func resolvePgConnInfo() pgConnInfo { + if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" { + if u, err := url.Parse(dbURL); err == nil && u.Hostname() != "" { + info := pgConnInfo{ + host: u.Hostname(), + user: u.User.Username(), + name: strings.TrimPrefix(u.Path, "/"), + port: u.Port(), + } + if p, ok := u.User.Password(); ok { + info.pass = p + } + if info.port == "" { + info.port = "5432" + } + if info.user == "" { + info.user = "postgres" + } + if info.name == "" { + info.name = "postgres" + } + return info + } + } + + info := pgConnInfo{ + host: os.Getenv("DB_HOST"), + port: os.Getenv("DB_PORT"), + name: os.Getenv("DB_NAME"), + user: os.Getenv("DB_USER"), + pass: os.Getenv("DB_PASS"), + } + if info.host == "" { + info.host = "db" + } + if info.port == "" { + info.port = "5432" + } + if info.name == "" { + info.name = "rss" + } + if info.user == "" { + info.user = "rss" + } + return info +} + // BackupDatabase runs pg_dump and returns the SQL as a downloadable file func BackupDatabase(c *gin.Context) { - // Ejecutar pg_dump desde dentro del contenedor db para evitar problemas de red - // Usamos docker exec para ejecutar el comando dentro del servicio db - cmd := exec.Command("docker", "exec", "rss2_db", "pg_dump", - "-U", os.Getenv("POSTGRES_USER"), - "-d", os.Getenv("POSTGRES_DB"), + info := resolvePgConnInfo() + + cmd := exec.Command("pg_dump", + "-h", info.host, + "-p", info.port, + "-U", info.user, + "-d", info.name, "--no-password", "--format=plain", - "--pghost=5432", ) + cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", info.pass)) var out bytes.Buffer var stderr bytes.Buffer @@ -671,32 +728,16 @@ func BackupDatabase(c *gin.Context) { // BackupNewsZipped performs a pg_dump of news tables and returns a ZIP file func BackupNewsZipped(c *gin.Context) { - dbHost := os.Getenv("DB_HOST") - if dbHost == "" { - dbHost = "db" - } - dbPort := os.Getenv("DB_PORT") - if dbPort == "" { - dbPort = "5432" - } - dbName := os.Getenv("DB_NAME") - if dbName == "" { - dbName = "rss" - } - dbUser := os.Getenv("DB_USER") - if dbUser == "" { - dbUser = "rss" - } - dbPass := os.Getenv("DB_PASS") + info := resolvePgConnInfo() // Tables to backup tables := []string{"noticias", "traducciones", "tags", "tags_noticia"} args := []string{ - "-h", dbHost, - "-p", dbPort, - "-U", dbUser, - "-d", dbName, + "-h", info.host, + "-p", info.port, + "-U", info.user, + "-d", info.name, "--no-password", } @@ -705,7 +746,7 @@ func BackupNewsZipped(c *gin.Context) { } cmd := exec.Command("pg_dump", args...) - cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", dbPass)) + cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", info.pass)) var sqlOut bytes.Buffer var stderr bytes.Buffer @@ -748,3 +789,107 @@ func BackupNewsZipped(c *gin.Context) { c.Header("Cache-Control", "no-cache") c.Data(http.StatusOK, "application/zip", buf.Bytes()) } + +// RestoreDatabase accepts an uploaded .sql or .zip backup and restores it via psql +func RestoreDatabase(c *gin.Context) { + file, err := c.FormFile("file") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "No file uploaded", + "message": "Se requiere un archivo .sql o .zip", + }) + return + } + + uploaded, err := file.Open() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to open uploaded file"}) + return + } + defer uploaded.Close() + + data, err := io.ReadAll(uploaded) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read uploaded file"}) + return + } + + var sqlData []byte + lowerName := strings.ToLower(file.Filename) + switch { + case strings.HasSuffix(lowerName, ".sql"): + sqlData = data + case strings.HasSuffix(lowerName, ".zip"): + zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ZIP file", "message": err.Error()}) + return + } + found := false + for _, zf := range zr.File { + if zf.FileInfo().IsDir() || !strings.HasSuffix(strings.ToLower(zf.Name), ".sql") { + continue + } + rc, err := zf.Open() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to open SQL inside ZIP", "message": err.Error()}) + return + } + sqlData, err = io.ReadAll(rc) + rc.Close() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read SQL inside ZIP", "message": err.Error()}) + return + } + found = true + break + } + if !found { + c.JSON(http.StatusBadRequest, gin.H{"error": "No SQL file found inside ZIP"}) + return + } + default: + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Unsupported file type", + "message": "Solo se permiten archivos .sql o .zip", + }) + return + } + + if len(sqlData) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Empty backup file"}) + return + } + + info := resolvePgConnInfo() + + cmd := exec.Command("psql", + "-h", info.host, + "-p", info.port, + "-U", info.user, + "-d", info.name, + "--no-password", + "-v", "ON_ERROR_STOP=1", + ) + cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", info.pass)) + cmd.Stdin = bytes.NewReader(sqlData) + + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "Restore failed", + "details": stderr.String(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "Base de datos restaurada correctamente", + "filename": file.Filename, + "output": out.String(), + }) +} diff --git a/backend/internal/handlers/feed.go b/backend/internal/handlers/feed.go index 884115a..ad7afc3 100644 --- a/backend/internal/handlers/feed.go +++ b/backend/internal/handlers/feed.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "net/url" "strconv" "strings" @@ -397,11 +398,46 @@ func ImportFeeds(c *gin.Context) { } reader := csv.NewReader(strings.NewReader(string(content))) - _, err = reader.Read() + records, err := reader.ReadAll() if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "Invalid CSV format"}) return } + if len(records) == 0 { + c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "Empty CSV file"}) + return + } + + colIndex := func(header []string, names ...string) int { + for i, h := range header { + h = strings.ToLower(strings.TrimSpace(h)) + for _, n := range names { + if h == n { + return i + } + } + } + return -1 + } + + header := records[0] + isHeader := colIndex(header, "url", "link", "feed", "nombre", "name", "titulo") != -1 + + var dataRows [][]string + iNombre, iDesc, iURL, iCat, iPais, iLang, iActivo, iFallos := -1, -1, -1, -1, -1, -1, -1, -1 + if isHeader { + iNombre = colIndex(header, "nombre", "name", "titulo", "title") + iDesc = colIndex(header, "descripcion", "description") + iURL = colIndex(header, "url", "link", "feed") + iCat = colIndex(header, "categoria_id", "category_id", "categoria", "category") + iPais = colIndex(header, "pais_id", "country_id", "pais", "country") + iLang = colIndex(header, "idioma", "language", "lang") + iActivo = colIndex(header, "activo", "active", "enabled") + iFallos = colIndex(header, "fallos", "failures", "fails") + dataRows = records[1:] + } else { + dataRows = records + } imported := 0 skipped := 0 @@ -415,71 +451,115 @@ func ImportFeeds(c *gin.Context) { } defer tx.Rollback(context.Background()) - for { - record, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - failed++ - continue + field := func(record []string, idx int) string { + if idx < 0 || idx >= len(record) { + return "" } + return strings.TrimSpace(record[idx]) + } - if len(record) < 4 { + for _, record := range dataRows { + if len(record) == 0 { skipped++ continue } - nombre := strings.TrimSpace(record[1]) - url := strings.TrimSpace(record[3]) + var nombre, url string + if isHeader { + nombre = field(record, iNombre) + url = field(record, iURL) + } else if len(record) == 1 { + url = field(record, 0) + } else if len(record) == 2 { + a := field(record, 0) + b := field(record, 1) + if looksLikeURL(a) { + url, nombre = a, b + } else { + nombre, url = a, b + } + } else { + nombre = field(record, 1) + url = field(record, 3) + } - if nombre == "" || url == "" { + if url == "" { skipped++ continue } + if nombre == "" { + nombre = nombreFromURL(url) + } var descripcion *string - if len(record) > 2 && strings.TrimSpace(record[2]) != "" { - descripcionStr := strings.TrimSpace(record[2]) - descripcion = &descripcionStr - } - var categoriaID *int64 - if len(record) > 4 && strings.TrimSpace(record[4]) != "" { - catID, err := strconv.ParseInt(strings.TrimSpace(record[4]), 10, 64) - if err == nil { - categoriaID = &catID - } - } - var paisID *int64 - if len(record) > 6 && strings.TrimSpace(record[6]) != "" { - pID, err := strconv.ParseInt(strings.TrimSpace(record[6]), 10, 64) - if err == nil { - paisID = &pID - } - } - var idioma *string - if len(record) > 8 && strings.TrimSpace(record[8]) != "" { - lang := strings.TrimSpace(record[8]) - if len(lang) > 2 { - lang = lang[:2] - } - idioma = &lang - } - activo := true - if len(record) > 9 && strings.TrimSpace(record[9]) != "" { - activo = strings.ToLower(strings.TrimSpace(record[9])) == "true" + var fallos int64 + + if isHeader { + if d := field(record, iDesc); d != "" { + descripcion = &d + } + if v := field(record, iCat); v != "" { + if id, err := strconv.ParseInt(v, 10, 64); err == nil { + categoriaID = &id + } + } + if v := field(record, iPais); v != "" { + if id, err := strconv.ParseInt(v, 10, 64); err == nil { + paisID = &id + } + } + if l := field(record, iLang); l != "" { + if len(l) > 2 { + l = l[:2] + } + idioma = &l + } + if a := field(record, iActivo); a != "" { + activo = strings.ToLower(a) == "true" + } + if f := field(record, iFallos); f != "" { + if v, err := strconv.ParseInt(f, 10, 64); err == nil { + fallos = v + } + } + } else { + if d := field(record, 2); d != "" { + descripcion = &d + } + if v := field(record, 4); v != "" { + if id, err := strconv.ParseInt(v, 10, 64); err == nil { + categoriaID = &id + } + } + if v := field(record, 6); v != "" { + if id, err := strconv.ParseInt(v, 10, 64); err == nil { + paisID = &id + } + } + if l := field(record, 8); l != "" { + if len(l) > 2 { + l = l[:2] + } + idioma = &l + } + if a := field(record, 9); a != "" { + activo = strings.ToLower(a) == "true" + } + if f := field(record, 10); f != "" { + if v, err := strconv.ParseInt(f, 10, 64); err == nil { + fallos = v + } + } } - var fallos int64 - if len(record) > 10 && strings.TrimSpace(record[10]) != "" { - f, err := strconv.ParseInt(strings.TrimSpace(record[10]), 10, 64) - if err == nil { - fallos = f - } + if _, err := tx.Exec(context.Background(), "SAVEPOINT sp"); err != nil { + failed++ + errors = append(errors, fmt.Sprintf("Error for %s: %v", url, err)) + continue } var existingID int64 @@ -490,22 +570,25 @@ func ImportFeeds(c *gin.Context) { WHERE id=$8`, nombre, descripcion, categoriaID, paisID, idioma, activo, fallos, existingID, ) - if err != nil { - failed++ - errors = append(errors, fmt.Sprintf("Error updating %s: %v", url, err)) - continue - } } else { _, err = tx.Exec(context.Background(), ` INSERT INTO feeds (nombre, descripcion, url, categoria_id, pais_id, idioma, activo, fallos) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, nombre, descripcion, url, categoriaID, paisID, idioma, activo, fallos, ) - if err != nil { - failed++ - errors = append(errors, fmt.Sprintf("Error inserting %s: %v", url, err)) - continue - } + } + + if err != nil { + tx.Exec(context.Background(), "ROLLBACK TO SAVEPOINT sp") + failed++ + errors = append(errors, fmt.Sprintf("Error upserting %s: %v", url, err)) + continue + } + + if _, err := tx.Exec(context.Background(), "RELEASE SAVEPOINT sp"); err != nil { + failed++ + errors = append(errors, fmt.Sprintf("Error for %s: %v", url, err)) + continue } imported++ @@ -525,6 +608,22 @@ func ImportFeeds(c *gin.Context) { }) } +func looksLikeURL(s string) bool { + return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") || strings.HasPrefix(s, "http://www.") || strings.HasPrefix(s, "https://www.") || strings.HasPrefix(s, "www.") || strings.HasPrefix(s, "feed://") +} + +func nombreFromURL(u string) string { + u = strings.TrimSpace(u) + if !strings.HasPrefix(u, "http://") && !strings.HasPrefix(u, "https://") { + u = "https://" + u + } + parsed, err := url.Parse(u) + if err != nil || parsed.Host == "" { + return u + } + return strings.TrimPrefix(parsed.Host, "www.") +} + func stringOrEmpty(s *string) string { if s == nil { return "" diff --git a/backend/internal/handlers/search.go b/backend/internal/handlers/search.go index 3803510..23b2999 100644 --- a/backend/internal/handlers/search.go +++ b/backend/internal/handlers/search.go @@ -51,55 +51,45 @@ func SearchNews(c *gin.Context) { offset := (page - 1) * perPage // Build dynamic query - args := []interface{}{} - argNum := 1 + args := []interface{}{lang} + argNum := 2 whereClause := "WHERE 1=1" if query != "" { - whereClause += " AND (n.titulo ILIKE $" + strconv.Itoa(argNum) + " OR n.resumen ILIKE $" + strconv.Itoa(argNum) + " OR n.contenido ILIKE $" + strconv.Itoa(argNum) + ")" + whereClause += " AND (n.titulo ILIKE $" + strconv.Itoa(argNum) + " OR n.resumen ILIKE $" + strconv.Itoa(argNum) + ")" args = append(args, "%"+query+"%") argNum++ } - if lang != "" { - whereClause += " AND t.lang_to = $" + strconv.Itoa(argNum) - args = append(args, lang) - argNum++ - } - if categoriaID != "" { - whereClause += " AND n.categoria_id = $" + strconv.Itoa(argNum) - catID, err := strconv.ParseInt(categoriaID, 10, 64) - if err == nil { + if catID, err := strconv.ParseInt(categoriaID, 10, 64); err == nil { + whereClause += " AND n.categoria_id = $" + strconv.Itoa(argNum) args = append(args, catID) argNum++ } } if paisID != "" { - whereClause += " AND n.pais_id = $" + strconv.Itoa(argNum) - pID, err := strconv.ParseInt(paisID, 10, 64) - if err == nil { + if pID, err := strconv.ParseInt(paisID, 10, 64); err == nil { + whereClause += " AND n.pais_id = $" + strconv.Itoa(argNum) args = append(args, pID) argNum++ } } - args = append(args, perPage, offset) - sqlQuery := ` - SELECT n.id, n.titulo, n.resumen, n.contenido, n.url, n.imagen, - n.feed_id, n.lang, n.categoria_id, n.pais_id, n.created_at, n.updated_at, - COALESCE(t.titulo_trad, '') as titulo_trad, - COALESCE(t.resumen_trad, '') as resumen_trad, - t.lang_to as lang_trad, - f.nombre as fuente_nombre + SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, + n.categoria_id, n.pais_id, n.fuente_nombre, + t.titulo_trad, + t.resumen_trad, + t.lang_to as lang_trad FROM noticias n - LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $` + strconv.Itoa(argNum) + ` - LEFT JOIN feeds f ON f.id = n.feed_id + LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $1 ` + whereClause + ` - ORDER BY n.created_at DESC - LIMIT $` + strconv.Itoa(argNum+1) + ` OFFSET $` + strconv.Itoa(argNum+2) + ORDER BY n.fecha DESC + LIMIT $` + strconv.Itoa(argNum) + ` OFFSET $` + strconv.Itoa(argNum+1) + + args = append(args, perPage, offset) rows, err := db.GetPool().Query(ctx, sqlQuery, args...) if err != nil { @@ -108,33 +98,38 @@ func SearchNews(c *gin.Context) { } defer rows.Close() - var newsList []models.NewsWithTranslations + var newsList []NewsResponse for rows.Next() { - var n models.NewsWithTranslations - var imagen *string + var n NewsResponse + var imagenURL, fuenteNombre *string + var categoriaIDp, paisIDp *int64 err := rows.Scan( - &n.ID, &n.Title, &n.Summary, &n.Content, &n.URL, &imagen, - &n.FeedID, &n.Lang, &n.CategoryID, &n.CountryID, &n.CreatedAt, &n.UpdatedAt, + &n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL, + &categoriaIDp, &paisIDp, &fuenteNombre, &n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated, ) if err != nil { continue } - if imagen != nil { - n.ImageURL = imagen + if imagenURL != nil { + n.ImagenURL = imagenURL } + if fuenteNombre != nil { + n.FuenteNombre = *fuenteNombre + } + n.CategoriaID = categoriaIDp + n.PaisID = paisIDp newsList = append(newsList, n) } // Get total count countArgs := args[:len(args)-2] - // Remove LIMIT/OFFSET from args for count var total int err = db.GetPool().QueryRow(ctx, ` SELECT COUNT(*) FROM noticias n - LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $`+strconv.Itoa(argNum)+` + LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $1 `+whereClause, countArgs...).Scan(&total) if err != nil { total = len(newsList) @@ -142,15 +137,13 @@ func SearchNews(c *gin.Context) { totalPages := (total + perPage - 1) / perPage - response := models.NewsListResponse{ - News: newsList, - Total: total, - Page: page, - PerPage: perPage, - TotalPages: totalPages, - } - - c.JSON(http.StatusOK, response) + c.JSON(http.StatusOK, gin.H{ + "news": newsList, + "total": total, + "page": page, + "per_page": perPage, + "total_pages": totalPages, + }) } func GetStats(c *gin.Context) { diff --git a/backend/internal/handlers/worker_ws.go b/backend/internal/handlers/worker_ws.go index a9d58cd..6798ceb 100644 --- a/backend/internal/handlers/worker_ws.go +++ b/backend/internal/handlers/worker_ws.go @@ -132,7 +132,10 @@ func writeLoop(wsWorker *WSWorker) { for { select { case <-ticker.C: - if err := wsWorker.conn.WriteMessage(websocket.PingMessage, nil); err != nil { + wsWorker.mu.Lock() + err := wsWorker.conn.WriteMessage(websocket.PingMessage, nil) + wsWorker.mu.Unlock() + if err != nil { return } } @@ -163,7 +166,9 @@ func heartbeatLoop(wsWorker *WSWorker) { func sendWS(wsWorker *WSWorker, msg models.WSServerMessage) { data, _ := json.Marshal(msg) + wsWorker.mu.Lock() wsWorker.conn.WriteMessage(websocket.TextMessage, data) + wsWorker.mu.Unlock() } func cleanupWorker(wsWorker *WSWorker, apiKey string) { diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..abcb203 --- /dev/null +++ b/backup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Backup diario de PostgreSQL rss2 +set -e + +BACKUP_DIR="/home/x/rss2/backups" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +OUT="$BACKUP_DIR/rss-${TIMESTAMP}.sql.gz" +LOG="$BACKUP_DIR/backup.log" + +mkdir -p "$BACKUP_DIR" +docker exec rss2_db pg_dump -U rss -d rss --no-owner --no-privileges | gzip > "$OUT" + +# Retener solo los ultimos 30 dias (borrado seguro de ficheros concretos, nunca rm -rf) +find "$BACKUP_DIR" -maxdepth 1 -name 'rss-*.sql.gz' -mtime +30 -delete + +echo "$(date '+%F %T') Backup OK: $OUT ($(du -h "$OUT" | cut -f1))" >> "$LOG" diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 0000000..c4051c3 --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,10 @@ +# Override local: permite acceder a Grafana en http://localhost:3001 +# La red monitoring es internal:true y compose descarta los puertos de servicios +# que solo están en redes internas. Añadir la red frontend (no internal) permite +# publicar el puerto 127.0.0.1:3001. +# Uso: docker compose -f docker-compose.yml -f docker-compose.local.yml up -d +services: + grafana: + networks: + - monitoring + - frontend diff --git a/docker-compose.yml b/docker-compose.yml index d4fc742..a6f2eb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -308,6 +308,7 @@ services: CT2_MODEL_PATH: /app/models/nllb-ct2 CT2_DEVICE: cpu CT2_COMPUTE_TYPE: int8 + CT2_INTRA_THREADS: 4 UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M HF_HOME: /app/hf_cache TZ: Europe/Madrid @@ -325,14 +326,13 @@ services: restart: unless-stopped # ================================================================================== - # TRANSLATOR GPU - Multiple instances with --scale for parallel GPU processing - # Configure multiple GPU workers: docker compose up -d --scale translator-gpu=2 + # TRANSLATOR CPU Nº2 - Segundo worker CPU (paralelo al translator) # ================================================================================== translator-gpu: build: context: . - dockerfile: Dockerfile.translator-gpu - image: rss2-translator-gpu:latest + dockerfile: Dockerfile.translator + image: rss2-translator:latest command: bash -lc "python -m workers.ctranslator_worker" environment: DB_HOST: db @@ -341,70 +341,27 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} TARGET_LANGS: es - TRANSLATOR_BATCH: 256 - CT2_MODEL_PATH: /app/models/nllb-ct2-1.3b - CT2_DEVICE: cuda - CT2_COMPUTE_TYPE: float16 - UNIVERSAL_MODEL: facebook/nllb-200-1.3B + TRANSLATOR_BATCH: 32 + CT2_MODEL_PATH: /app/models/nllb-ct2 + CT2_DEVICE: cpu + CT2_COMPUTE_TYPE: int8 + CT2_INTRA_THREADS: 4 + UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M HF_HOME: /app/hf_cache TZ: Europe/Madrid TRANSLATOR_ID: ${TRANSLATOR_ID:-} - PYTORCH_CUDA_ALLOC_CONF: max_split_size_mb:512 - NCCL_DEBUG: INFO + PYTORCH_ENABLE_MPS_FALLBACK: 1 volumes: - ./workers:/app/workers - ./hf_cache:/app/hf_cache - ./models:/app/models - - /dev/nvidia:/dev/nvidia - - /usr/local/nvidia:/usr/local/nvidia:ro networks: - backend - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] depends_on: db: condition: service_healthy restart: unless-stopped - # ================================================================================== - # REMOTE TRANSLATOR WORKER - Worker remoto que se conecta por WebSocket - # ================================================================================== - remote-translator: - build: - context: . - dockerfile: Dockerfile.remote-worker - container_name: rss2_remote_translator - environment: - WORKER_NAME: ${WORKER_NAME:-remote-worker-1} - WORKER_API_KEY: ${WORKER_API_KEY:-} - WORKER_SERVER: ${WORKER_SERVER:-ws://backend-go:8080/ws/worker} - CT2_DEVICE: ${CT2_DEVICE:-cuda} - CT2_MODEL_PATH: /app/models/nllb-ct2 - CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-float16} - UNIVERSAL_MODEL: facebook/nllb-200-1.3B - HF_HOME: /app/hf_cache - TZ: Europe/Madrid - volumes: - - ./hf_cache:/app/hf_cache - - ./models:/app/models - networks: - - backend - deploy: - resources: - limits: - memory: 6G - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [ gpu ] - restart: unless-stopped - # ================================================================================== # TRANSLATION SCHEDULER - Creates translation jobs # ================================================================================== @@ -455,7 +412,7 @@ services: EMB_SLEEP_IDLE: 5 EMB_LANGS: es EMB_LIMIT: 1000 - DEVICE: cuda + DEVICE: cpu HF_HOME: /app/hf_cache TZ: Europe/Madrid volumes: @@ -467,11 +424,6 @@ services: resources: limits: memory: 6G - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [ gpu ] depends_on: db: condition: service_healthy @@ -523,7 +475,7 @@ services: RELATED_SLEEP: 10 RELATED_BATCH: 200 RELATED_TOPK: 10 - EMB_MODEL: mxbai-embed-large + EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 TZ: Europe/Madrid networks: - backend @@ -660,117 +612,8 @@ services: memory: 2G # ================================================================================== - # LLM CATEGORIZER (Python) - Categorización con Ollama + # REDES SEGMENTADAS # ================================================================================== - llm-categorizer: - build: - context: . - dockerfile: Dockerfile - container_name: rss2_llm_categorizer - command: bash -lc "python -m workers.simple_categorizer_worker" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${DB_NAME:-rss} - DB_USER: ${DB_USER:-rss} - DB_PASS: ${DB_PASS} - CATEGORIZER_BATCH_SIZE: 10 - CATEGORIZER_SLEEP_IDLE: 5 - TZ: Europe/Madrid - volumes: - - ./workers:/app/workers - networks: - - backend - depends_on: - db: - condition: service_healthy - restart: unless-stopped - deploy: - resources: - limits: - cpus: '2' - memory: 1G - - # ================================================================================== - # MONITORING STACK - SECURED - # ================================================================================== - - prometheus: - image: prom/prometheus:latest - container_name: rss2_prometheus - volumes: - - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--web.console.libraries=/usr/share/prometheus/console_libraries' - - '--web.console.templates=/usr/share/prometheus/consoles' - # SEGURIDAD: Sin exposición de puertos - acceso solo vía Grafana o túnel SSH - # ports: - # - "9090:9090" - networks: - - monitoring - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 2G - - grafana: - image: grafana/grafana:latest - container_name: rss2_grafana - # SEGURIDAD: Acceso solo en localhost o vía túnel SSH - # Para acceso remoto, usar túnel SSH: ssh -L 3001:localhost:3001 user@server - ports: - - "127.0.0.1:3001:3000" - environment: - # SEGURIDAD: Cambiar este password en producción - - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-change_this_password} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_SERVER_ROOT_URL=http://localhost:3001 - - GF_SECURITY_COOKIE_SECURE=false - - GF_SECURITY_COOKIE_SAMESITE=lax - volumes: - - grafana_data:/var/lib/grafana - networks: - - monitoring - depends_on: - - prometheus - restart: unless-stopped - deploy: - resources: - limits: - cpus: '1' - memory: 1G - - cadvisor: - image: gcr.io/cadvisor/cadvisor:latest - container_name: rss2_cadvisor - # SEGURIDAD: Sin exposición de puertos - solo acceso interno - # ports: - # - "8081:8080" - volumes: - - /:/rootfs:ro - - /var/run:/var/run:ro - - /sys:/sys:ro - - /var/lib/docker/:/var/lib/docker:ro - - /dev/disk/:/dev/disk:ro - devices: - - /dev/kmsg - networks: - - monitoring - restart: unless-stopped - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - -# ================================================================================== -# REDES SEGMENTADAS -# ================================================================================== networks: # Red frontal - Solo nginx y web app frontend: @@ -784,13 +627,5 @@ networks: driver: bridge internal: false # Acceso externo permitido (necesario para ingestor) - # Red de monitoreo - Prometheus, Grafana, cAdvisor - monitoring: - name: rss2_monitoring - driver: bridge - internal: true - volumes: - prometheus_data: - grafana_data: torch_extensions: diff --git a/docker-entrypoint-db.sh b/docker-entrypoint-db.sh index 1eb2722..3c66863 100755 --- a/docker-entrypoint-db.sh +++ b/docker-entrypoint-db.sh @@ -1,27 +1,23 @@ #!/bin/bash set -e -# Detectar si la base de datos necesita reinicialización +# Directorio de datos de PostgreSQL PGDATA_DIR="/var/lib/postgresql/data/18/main" -echo "RSS2: Checking database integrity..." +echo "RSS2: Checking database presence..." -# Si no existe el archivo de versión, es una base de datos nueva -if [ ! -f "$PGDATA_DIR/PG_VERSION" ]; then - echo "RSS2: New database - will be initialized by docker-entrypoint" +# REGLA: nunca se borra ni se modifica el directorio de datos automáticamente. +# Solo se informa del estado y se deja que el entrypoint oficial de PostgreSQL +# inicialice (si no existe) o arranque con los datos existentes. +if [ -f "$PGDATA_DIR/PG_VERSION" ]; then + echo "RSS2: Existing database found at $PGDATA_DIR - starting normally" else - # Verificar si la base de datos es funcional - if ! pg_isready -h localhost -p 5432 -U "${POSTGRES_USER:-rss}" 2>/dev/null; then - echo "RSS2: Database appears corrupted - removing old data files for fresh initialization..." - # Eliminar solo los archivos de datos, no todo el directorio - rm -rf "$PGDATA_DIR"/* - echo "RSS2: Data files removed - docker-entrypoint will initialize fresh database" - else - echo "RSS2: Database is healthy" - fi + echo "RSS2: No database found at $PGDATA_DIR - docker-entrypoint will initialize it" fi -# Ejecutar el entrypoint original con los parámetros de PostgreSQL +# Ejecutar el entrypoint original con los parámetros de PostgreSQL. +# Si la base de datos estuviera corrupta, PostgreSQL fallará al arrancar y +# registrará el error en los logs; NO se elimina ningún dato de forma automática. exec docker-entrypoint.sh \ postgres \ -c max_connections=200 \ diff --git a/feeds.csv b/feeds.csv index b7ce60f..8adc0f3 100644 --- a/feeds.csv +++ b/feeds.csv @@ -1,18 +1,6804 @@ -id,nombre,descripcion,url,categoria_id,categoria,pais_id,pais,idioma,activo,fallos -19,8am Daily – Dari,روزنامه ۸صبح افغانستان به زبان دری.,https://8am.af/feed,7,Internacional,1,Afganistán,fa,False,30 -20,8am Daily – Pashto,د ۸صبح ورځپاڼې پښتو خپرونه.,https://8am.af/ps/feed,7,Internacional,1,Afganistán,ps,False,30 -1,Afghanistan News.Net – Noticias,Cobertura continua de noticias generales sobre Afganistán y su entorno regional.,https://feeds.afghanistannews.net/rss/6e1d5c8e1f98f17c,7,Internacional,1,Afganistán,en,True,0 -36,Arezo TV – Dari,آرزو تلویزیون – خبر و گزارش به دری.,https://arezo.tv/fa/feed,7,Internacional,1,Afganistán,fa,False,30 -37,Arezo TV – Pashto,د آرزو تلویزیون پښتو خبرونه.,https://arezo.tv/ps/feed,7,Internacional,1,Afganistán,ps,False,30 -4,Ariana News – Dari,خبرها و تحلیل‌ها از افغانستان به زبان دری.,https://ariananews.af/feed,7,Internacional,1,Afganistán,fa,True,0 -28,Avapress – Dari,خبرگزاری صدای افغان (آوا) به زبان دری.,https://avapress.com/fa/rss,7,Internacional,1,Afganistán,fa,False,30 -29,Avapress – Pashto,د افغان غږ خبري آژانس په پښتو ژبه.,https://avapress.com/ps/rss,7,Internacional,1,Afganistán,ps,False,30 -23,Bakhtar News Agency – Dari,آژانس خبری باختر به زبان دری.,https://bakhtarnews.af/fa/feed,7,Internacional,1,Afganistán,fa,False,5 -24,Bakhtar News Agency – Pashto,د باختر خبري اژانس پښتو پاڼه.,https://bakhtarnews.af/ps/feed,7,Internacional,1,Afganistán,ps,False,5 -38,Barya News – Dari,باریانیوز – رسانه خبری افغانستان.,https://barya.news/feed,7,Internacional,1,Afganistán,fa,False,30 -39,Barya News – Pashto,باریانیوز پښتو خپرونې.,https://barya.news/ps/feed,7,Internacional,1,Afganistán,ps,False,30 -47,Chaprast News – Dari,چپرست نیوز – خبرهای افغانستان.,https://chaprast.com/feed,7,Internacional,1,Afganistán,fa,False,30 -30,Ensaf News – Dari,رسانه تحلیلی به زبان دری.,https://www.ensafnews.com/fa/feed,7,Internacional,1,Afganistán,fa,False,30 -27,Hamshahri Afghanistan – Dari,نسخه افغانستان همشهری به زبان دری.,https://hamshahri.af/feed,7,Internacional,1,Afganistán,fa,False,30 -44,Jomhor News – Dari,جمهور نیوز – خبرگزاری مستقل دری.,https://jomhornews.com/fa/rss,7,Internacional,1,Afganistán,fa,False,30 -45,Jomhor News – Pashto,جمهور نیوز پښتو.,https://jomhornews.com/ps/rss,7,Internacional,1,Afganistán,ps,False,30 +id,nombre,descripcion,url,categoria_id,categoria,pais_id,pais,idioma,activo,fallos +19,8am Daily – Dari,روزنامه ۸صبح افغانستان به زبان دری.,https://8am.af/feed,7,Internacional,1,Afganistán,fa,False,30 +20,8am Daily – Pashto,د ۸صبح ورځپاڼې پښتو خپرونه.,https://8am.af/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +1,Afghanistan News.Net – Noticias,Cobertura continua de noticias generales sobre Afganistán y su entorno regional.,https://feeds.afghanistannews.net/rss/6e1d5c8e1f98f17c,7,Internacional,1,Afganistán,en,True,0 +36,Arezo TV – Dari,آرزو تلویزیون – خبر و گزارش به دری.,https://arezo.tv/fa/feed,7,Internacional,1,Afganistán,fa,False,30 +37,Arezo TV – Pashto,د آرزو تلویزیون پښتو خبرونه.,https://arezo.tv/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +4,Ariana News – Dari,خبرها و تحلیل‌ها از افغانستان به زبان دری.,https://ariananews.af/feed,7,Internacional,1,Afganistán,fa,True,0 +28,Avapress – Dari,خبرگزاری صدای افغان (آوا) به زبان دری.,https://avapress.com/fa/rss,7,Internacional,1,Afganistán,fa,False,30 +29,Avapress – Pashto,د افغان غږ خبري آژانس په پښتو ژبه.,https://avapress.com/ps/rss,7,Internacional,1,Afganistán,ps,False,30 +23,Bakhtar News Agency – Dari,آژانس خبری باختر به زبان دری.,https://bakhtarnews.af/fa/feed,7,Internacional,1,Afganistán,fa,False,5 +24,Bakhtar News Agency – Pashto,د باختر خبري اژانس پښتو پاڼه.,https://bakhtarnews.af/ps/feed,7,Internacional,1,Afganistán,ps,False,5 +38,Barya News – Dari,باریانیوز – رسانه خبری افغانستان.,https://barya.news/feed,7,Internacional,1,Afganistán,fa,False,30 +39,Barya News – Pashto,باریانیوز پښتو خپرونې.,https://barya.news/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +47,Chaprast News – Dari,چپرست نیوز – خبرهای افغانستان.,https://chaprast.com/feed,7,Internacional,1,Afganistán,fa,False,30 +30,Ensaf News – Dari,رسانه تحلیلی به زبان دری.,https://www.ensafnews.com/fa/feed,7,Internacional,1,Afganistán,fa,False,30 +27,Hamshahri Afghanistan – Dari,نسخه افغانستان همشهری به زبان دری.,https://hamshahri.af/feed,7,Internacional,1,Afganistán,fa,False,30 +44,Jomhor News – Dari,جمهور نیوز – خبرگزاری مستقل دری.,https://jomhornews.com/fa/rss,7,Internacional,1,Afganistán,fa,False,30 +45,Jomhor News – Pashto,جمهور نیوز پښتو.,https://jomhornews.com/ps/rss,7,Internacional,1,Afganistán,ps,False,30 +16,Kabul Now – Pashto,خبرونه او راپورونه د افغانستان په اړه.,https://kabulnow.af/feed,7,Internacional,1,Afganistán,ps,False,30 +3,Khaama Press – Afghan News,Medio independiente con cobertura 24/7 de Afganistán y noticias internacionales relacionadas.,https://www.khaama.com/feed,7,Internacional,1,Afganistán,en,True,0 +34,Lemar TV – Pashto,لـمر تلویزیون د نړۍ او افغانستان پښتو خبرونه.,https://lemar.tv/feed,7,Internacional,1,Afganistán,ps,False,30 +40,MOBY Group – Dari,گروه رسانه‌ای MOBY – آخرین اخبار افغانستان.,https://mobygroup.com/fa/feed,7,Internacional,1,Afganistán,fa,False,30 +41,MOBY Group – Pashto,موبی ګروپ پښتو خپرونې.,https://mobygroup.com/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +35,Noor TV – Dari,تلویزیون نور با خبرها و تحلیل‌ها به زبان دری.,https://noortv.af/feed,7,Internacional,1,Afganistán,fa,False,30 +2,Pajhwok – Dari,خبرگزاری پژواک به زبان دری.,https://pajhwok.com/feed,7,Internacional,1,Afganistán,fa,False,30 +12,Radio Azadi – Dari,خبرها به زبان دری از افغانستان و منطقه.,https://da.azadiradio.com/rss,7,Internacional,1,Afganistán,fa,False,30 +11,Radio Azadi – Pashto,خبرونه په پښتو ژبه د افغانستان او سیمې څخه.,https://pa.azadiradio.com/rss,7,Internacional,1,Afganistán,ps,False,30 +42,Sabah News – Dari,صبح‌نیوز – خبرگزاری افغانستان.,https://sabahnews.af/feed,7,Internacional,1,Afganistán,fa,False,30 +43,Sabah News – Pashto,صبح‌نیوز پښتو پاڼه.,https://sabahnews.af/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +25,Salam Times – Dari,رسانه منطقه‌ای به زبان دری.,https://dari.salaamtimes.com/feed,7,Internacional,1,Afganistán,fa,False,30 +26,Salam Times – Pashto,سلا‌م ټایمز په پښتو ژبه.,https://pashto.salaamtimes.com/feed,7,Internacional,1,Afganistán,ps,False,30 +48,Seda-e-Azadi – Dari,صدای آزادی – اخبار افغانستان.,https://sedayeazadi.net/feed,7,Internacional,1,Afganistán,fa,False,30 +33,Shamshad TV – Pashto,شمشاد تلویزیون د افغانستان په پښتو ژبه خپرونې.,https://shamshadtv.com/feed,7,Internacional,1,Afganistán,ps,False,30 +46,Subhe Kabul – Dari,صبح کابل – روزنامه و پایگاه خبری افغانستان.,https://subhekabul.com/feed,7,Internacional,1,Afganistán,fa,True,0 +21,The Killid Group – Dari,رادیو و وبسایت کلید به زبان دری.,https://tkg.af/fa/feed,7,Internacional,1,Afganistán,fa,False,30 +22,The Killid Group – Pashto,د کلید راډیو او وېبپاڼه په پښتو ژبه.,https://tkg.af/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +18,Tolo News – Dari,خبرها، گزارش‌ها و تحلیل‌ها درباره افغانستان.,https://tolonews.com/fa/rss,7,Internacional,1,Afganistán,fa,False,30 +49,Watan News – Dari,وطن نیوز – خبرگزاری دری افغانستان.,https://watannews.af/feed,7,Internacional,1,Afganistán,fa,False,30 +50,Watan News – Pashto,وطن نیوز پښتو پاڼه.,https://watannews.af/ps/feed,7,Internacional,1,Afganistán,ps,False,30 +32,Zhwandoon News – Dari,تلویزیون ژوندون به زبان دری.,https://zhwandoon.tv/fa/rss,7,Internacional,1,Afganistán,fa,False,30 +31,Zhwandoon News – Pashto,د ژوندون ټلویزیون او رېډیو پښتو خپرونې.,https://zhwandoon.tv/ps/rss,7,Internacional,1,Afganistán,ps,False,30 +70,Shqipëria.com – Cultura,"Portal sobre cultura, identidad y patrimonio albanés.",https://shqiperia.com/feed,2,Cultura,2,Albania,sq,False,30 +54,bota.al – Cultura y sociedad,"Portal que ofrece noticias sobre cultura, historia y eventos en Albania.",https://bota.al/feed,2,Cultura,2,Albania,sq,True,0 +62,RTSH Sport – Deportes,"Sección deportiva de RTSH: fútbol, ligas y competiciones nacionales.",https://rtsh.al/sport/feed,3,Deportes,2,Albania,sq,False,30 +57,Shekulli – Deportes Albania,Portal deportivo albanés con cobertura de fútbol y otros deportes.,https://www.shekulli.com.al/rss,3,Deportes,2,Albania,sq,False,5 +56,Revista Monitor – Economía & Opinión,Revista semanal albanesa de economía y opinión.,https://monitor.al/feed,4,Economía,2,Albania,sq,False,30 +55,Scan TV – Economía Albania,Noticias y análisis económicos de Albania.,https://scan-tv.com/feed,4,Economía,2,Albania,sq,True,0 +51,Abcnews.al – Albania,Noticias generales de Albania en albanés.,https://abcnews.al/feed,7,Internacional,2,Albania,sq,True,0 +65,KosovaPress Albania Edition – Internacional,Agencia informativa para la región Albania–Kosova.,https://kosovapress.com/feed,7,Internacional,2,Albania,sq,False,30 +52,News24.al – Albania,Portal de noticias 24 horas de Albania.,https://news24.al/feed,7,Internacional,2,Albania,sq,True,0 +61,RTSH Albania – Noticias,Radio Televizioni Shqiptar: noticias nacionales en albanés.,https://rtsh.al/feed,7,Internacional,2,Albania,sq,True,0 +63,RTV21 Albania – Noticias,Canal informativo con actualidad política y social.,https://rtv21.tv/feed,7,Internacional,2,Albania,sq,True,0 +64,Shqip.com – Noticias,"Uno de los principales portales del país, con actualidad general.",https://shqip.com/feed,7,Internacional,2,Albania,sq,True,0 +53,Tirana Times – English Edition,"Periódico en inglés sobre Albania: política, economía, sociedad.",https://www.tiranatimes.com/feed,7,Internacional,2,Albania,en,True,0 +58,Exit.al – Opinión Albania,Medio de noticias y opinión independiente en Albania.,https://exit.al/feed,10,Opinión,2,Albania,sq,False,30 +68,Periskopi Albania Edition – Opinión,Artículos de análisis y opinión sobre Albania y los Balcanes.,https://periskopi.com/feed,10,Opinión,2,Albania,sq,False,30 +66,Albanian Post – English,Periódico en inglés con análisis político y económico.,https://albanianpost.com/en/feed,11,Política,2,Albania,en,False,30 +69,Lapsi.al – Política,Medio digital fuerte en noticias políticas y actualidad.,https://lapsi.al/feed,11,Política,2,Albania,sq,True,0 +67,Publiku – Sociedad,Noticias locales y temas sociales de Albania.,https://publiku.com/feed,13,Sociedad,2,Albania,sq,False,30 +59,Teknologjia.al – Tecnología Albania,Portal albanés dedicado a tecnología e innovación.,https://teknologjia.al/feed,14,Tecnología,2,Albania,sq,False,30 +60,Travel Albania – Viajes en Albania,Blog/portal de viajes centrado en destinos albaneses.,https://travelalbania.net/feed,15,Viajes,2,Albania,en,True,0 +131,Astronomie.de – Ciencia,"Astronomía, cosmos y astrofísica.",https://www.astronomie.de/rss,1,Ciencia,3,Alemania,de,False,30 +73,Der Spiegel – Wissenschaft,"Ciencia, tecnología y descubrimientos.",https://www.spiegel.de/wissenschaft/index.rss,1,Ciencia,3,Alemania,de,True,0 +76,Die Zeit – Wissen,"Ciencia, salud e investigación.",https://www.zeit.de/wissen/index.rss,1,Ciencia,3,Alemania,de,False,30 +128,ElektronikPraxis – Electrónica,"Ingeniería, circuitos y diseño electrónico.",https://www.elektronikpraxis.vogel.de/rss/feed.xml,1,Ciencia,3,Alemania,de,False,30 +134,Elektroniknet – Ciencia/Tecnología,Noticias técnicas sobre electrónica y semiconductores.,https://www.elektroniknet.de/rss,1,Ciencia,3,Alemania,de,True,0 +105,FAZ – Wissenschaft,Ciencia y descubrimientos.,https://www.faz.net/rss/aktuell/wissen,1,Ciencia,3,Alemania,de,True,0 +152,HumanTech – Biotecnología,"Biotech, genética, bioingeniería y medicina.",https://humantech.de/feed,1,Ciencia,3,Alemania,de,False,30 +129,Ingenieur.de – Ingeniería,Noticias científicas e ingeniería alemana.,https://www.ingenieur.de/feed,1,Ciencia,3,Alemania,de,True,0 +150,Laborpraxis – Laboratorio y ciencia,"Biotecnología, química, análisis y laboratorio.",https://www.laborpraxis.vogel.de/rss/feed.xml,1,Ciencia,3,Alemania,de,False,30 +151,Photonik – Ciencia óptica,"Láser, fotónica, óptica y investigación avanzada.",https://www.photonik.de/rss,1,Ciencia,3,Alemania,de,False,30 +154,Plastverarbeiter – Ingeniería de materiales,"Materiales, polímeros, procesos industriales.",https://www.plastverarbeiter.de/rss,1,Ciencia,3,Alemania,de,False,30 +149,Process – Ingeniería química,"Ingeniería, industria, procesos químicos.",https://www.process.vogel.de/rss/feed.xml,1,Ciencia,3,Alemania,de,False,30 +130,Spektrum.de – Ciencia,"Divulgación científica, física, biología, astronomía.",https://www.spektrum.de/alias/rss/spektrum-rss/1242668,1,Ciencia,3,Alemania,de,False,30 +114,Welt – Wissenschaft,"Ciencia, tecnología y descubrimientos.",https://www.welt.de/wissenschaft/?rss=1,1,Ciencia,3,Alemania,de,False,30 +74,Der Spiegel – Kultur,"Arte, cine, literatura y cultura.",https://www.spiegel.de/kultur/index.rss,2,Cultura,3,Alemania,de,True,0 +104,FAZ – Feuilleton,Cultura y sociedad.,https://www.faz.net/rss/aktuell/feuilleton,2,Cultura,3,Alemania,de,True,0 +109,Süddeutsche Zeitung – Kultur,"Cultura, cine y arte.",https://rss.sueddeutsche.de/app/service/rss/kultur/index.rss,2,Cultura,3,Alemania,de,False,30 +83,Kicker.de – Deportes,"Noticias deportivas, Bundesliga.",https://www.kicker.de/rss/news,3,Deportes,3,Alemania,de,False,30 +72,Der Spiegel – Wirtschaft,Economía y finanzas en Alemania y Europa.,https://www.spiegel.de/wirtschaft/index.rss,4,Economía,3,Alemania,de,True,0 +77,Die Zeit – Wirtschaft,"Economía, negocios y mercados.",https://www.zeit.de/wirtschaft/index.rss,4,Economía,3,Alemania,de,False,30 +103,FAZ – Economía,Economía y negocios en FAZ.,https://www.faz.net/rss/aktuell/wirtschaft,4,Economía,3,Alemania,de,True,0 +80,Handelsblatt – Economía,El mayor diario económico de Alemania.,https://www.handelsblatt.com/contentexport/feed/schlagzeilen,4,Economía,3,Alemania,de,True,0 +108,Süddeutsche Zeitung – Wirtschaft,Economía alemana y global.,https://rss.sueddeutsche.de/app/service/rss/wirtschaft/index.rss,4,Economía,3,Alemania,de,False,30 +113,Welt – Economía,"Mercados, empresas, inflación, energía.",https://www.welt.de/wirtschaft/?rss=1,4,Economía,3,Alemania,de,False,30 +101,Bild – Noticias,Actualidad alemana al instante.,https://www.bild.de/rssfeeds/vw-home/vw-home-16725562,7,Internacional,3,Alemania,de,True,0 +84,Deutsche Welle – Internacional (EN),Noticias globales en inglés desde Alemania.,https://rss.dw.com/rdf/rss-en-top,7,Internacional,3,Alemania,en,True,0 +106,Süddeutsche Zeitung – Top News,Uno de los periódicos más grandes de Alemania.,https://rss.sueddeutsche.de/app/service/rss/alles/index.rss,7,Internacional,3,Alemania,de,False,30 +78,Tagesschau – Top News,Noticias principales de la televisión pública alemana.,https://www.tagesschau.de/xml/rss2,7,Internacional,3,Alemania,de,True,0 +115,ARD – Politik,Noticias políticas de la cadena pública ARD.,https://www.ard.de/?rss=politics,11,Política,3,Alemania,de,False,30 +71,Der Spiegel – Politik,Noticias políticas de uno de los principales medios alemanes.,https://www.spiegel.de/politik/index.rss,11,Política,3,Alemania,de,True,0 +75,Die Zeit – Politik,Noticias políticas alemanas y europeas.,https://www.zeit.de/index.rss,11,Política,3,Alemania,de,False,30 +102,FAZ – Política,Frankfurter Allgemeine Zeitung: política alemana y europea.,https://www.faz.net/rss/aktuell/politik,11,Política,3,Alemania,de,True,0 +107,Süddeutsche Zeitung – Politik,Política alemana.,https://rss.sueddeutsche.de/app/service/rss/politik/index.rss,11,Política,3,Alemania,de,False,30 +112,Welt – Política,Noticias políticas alemanas y globales.,https://www.welt.de/feeds/latest.rss,11,Política,3,Alemania,de,True,0 +85,Focus Online – Panorama,"Actualidad general, sociedad y sucesos.",https://www.focus.de/atom.xml,13,Sociedad,3,Alemania,de,False,30 +79,Tagesspiegel – Berlin,"Actualidad, sociedad y política en Berlín.",https://www.tagesspiegel.de/contentexport/feed/home,13,Sociedad,3,Alemania,de,True,0 +120,All About Samsung – Tecnología,Noticias sobre Samsung y dispositivos móviles.,https://allaboutsamsung.de/feed,14,Tecnología,3,Alemania,de,False,30 +119,Android-Hilfe.de – Android,Noticias y actualizaciones del ecosistema Android.,https://www.android-hilfe.de/forums/-/index.rss,14,Tecnología,3,Alemania,de,True,0 +122,Apfeltalk – Apple,"Comunidad Apple alemana, tecnología y actualidad.",https://www.apfeltalk.de/magazin/feed,14,Tecnología,3,Alemania,de,True,0 +110,Chip.de – Tecnología,"Tecnología, informática, móviles y análisis.",https://www.chip.de/rss/rss_topnews.xml,14,Tecnología,3,Alemania,de,False,30 +125,CloudComputing-Insider – Tecnología,"Cloud, virtualización, DevOps y sistemas distribuidos.",https://www.cloudcomputing-insider.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +136,Computer Weekly DE – IT,"Tecnología empresarial, cloud, IA, ciberseguridad.",https://www.computerweekly.com/rss,14,Tecnología,3,Alemania,de,False,30 +117,ComputerBase – Hardware/Software,"Hardware, análisis y novedades tecnológicas.",https://www.computerbase.de/rss/news.xml,14,Tecnología,3,Alemania,de,False,30 +138,DerStandard Tech – Tecnología,"Tecnología, redes, IA y digitalización.",https://www.derstandard.de/rss/Tech,14,Tecnología,3,Alemania,de,False,30 +144,Digital Fernsehen – Tecnología TV,"Televisión digital, satélite, dispositivos, IPTV.",https://www.digitalfernsehen.de/feed,14,Tecnología,3,Alemania,de,True,0 +148,Electrive – Movilidad eléctrica,"Coches eléctricos, infraestructura, baterías.",https://www.electrive.net/feed,14,Tecnología,3,Alemania,de,True,0 +153,Energie.de – Energía y tecnología,"Energías renovables, smart grids, almacenamiento.",https://www.energie.de/rss,14,Tecnología,3,Alemania,de,False,30 +141,Funkschau – Redes y telecom,"Comunicaciones, redes, IoT y telecom.",https://www.funkschau.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +82,Golem.de – Tecnología,"Tecnología, informática, redes, Linux.",https://rss.golem.de/rss.php?feed=ATOM1.0,14,Tecnología,3,Alemania,de,True,0 +132,Heise Autos – Tecnología,"Tech en automoción, movilidad y vehículos eléctricos.",https://www.heise.de/autos/rss/news-atom.xml,14,Tecnología,3,Alemania,de,True,0 +81,Heise Online – Tecnología,"Noticias tecnológicas, software, hardware y seguridad.",https://www.heise.de/rss/heise-atom.xml,14,Tecnología,3,Alemania,de,True,0 +145,Hifi.de – Tecnología de audio,"Audio, HIFI, altavoces, análisis técnicos.",https://www.hifi.de/feed,14,Tecnología,3,Alemania,de,True,0 +140,IT-Business – Tecnología empresarial,"Digitalización, infraestructuras IT y canal profesional.",https://www.it-business.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +142,IT-Director – Tecnología corporativa,"Movilidad, sistemas IT, cloud, data centers.",https://www.it-director.de/rss.xml,14,Tecnología,3,Alemania,de,False,30 +123,ITespresso.de – Tecnología,"Tecnología empresarial, startups y ciberseguridad.",https://www.itespresso.de/feed,14,Tecnología,3,Alemania,de,False,30 +127,Industry of Things – IoT,"Internet de las cosas, industria inteligente.",https://www.industry-of-things.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +121,MacTechNews.de – Apple,"Noticias Apple, iOS y macOS.",https://www.mactechnews.de/rss/news.xml,14,Tecnología,3,Alemania,de,False,30 +147,Macwelt – Tecnología Apple,"Noticias sobre Apple, iOS y Mac en alemán.",https://www.macwelt.de/rss-feeds/macwelt.xml,14,Tecnología,3,Alemania,de,False,30 +155,MaschinenMarkt – Ingeniería mecánica,"Robótica, automatización, ingeniería industrial.",https://www.maschinenmarkt.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +118,Notebookcheck – Tecnología,"Análisis hardware, portátiles y móviles.",https://www.notebookcheck.com/RSS.111.0.html,14,Tecnología,3,Alemania,de,False,30 +124,OnlineMarketing.de – Tech/Medios,Tecnología aplicada al marketing digital.,https://onlinemarketing.de/feed,14,Tecnología,3,Alemania,de,True,0 +146,PC-Welt – Tecnología informática,"PC, software, trucos, seguridad.",https://www.pcwelt.de/rss-feeds/pcwelt.xml,14,Tecnología,3,Alemania,de,False,30 +143,ProMedia – Tecnología audiovisual,"Multimedia, broadcast, sonido, producción.",https://www.promedianews.de/rss,14,Tecnología,3,Alemania,de,True,0 +133,Robotik und Produktion – Robótica,"Robots, automatización e industria 4.0.",https://www.robotik-produktion.de/feed,14,Tecnología,3,Alemania,de,True,0 +126,Security Insider – Ciberseguridad,Seguridad informática y análisis de amenazas.,https://www.security-insider.de/rss/feed.xml,14,Tecnología,3,Alemania,de,False,30 +135,T3N Dev – Desarrollo Software,"Noticias DevOps, desarrollo web y programación.",https://t3n.de/news/feed/kategorie/dev,14,Tecnología,3,Alemania,de,False,30 +111,T3N – Tecnología e innovación,"Emprendimiento digital, AI, empresas tecnológicas.",https://t3n.de/news/feed,14,Tecnología,3,Alemania,de,True,0 +139,Telecom Handel – Telecomunicaciones,"Noticias sobre operadores móviles, 5G y redes.",https://www.telecom-handel.de/rss.xml,14,Tecnología,3,Alemania,de,False,30 +116,WinFuture.de – Tecnología,"Noticias de software, hardware y Windows.",https://www.winfuture.de/rss/news.xml,14,Tecnología,3,Alemania,de,False,30 +137,ZDNet.de – Tecnología,"Tendencias tecnológicas, software, hardware y seguridad.",https://www.zdnet.de/feed,14,Tecnología,3,Alemania,de,False,30 +161,Agenda.ad – Cultura,"Agenda cultural d'Andorra: arts, espectacles, cinema.",https://www.agenda.ad/rss,2,Cultura,4,Andorra,ca,False,30 +166,Govern d’Andorra – Economia,Actualitat econòmica i comunicats oficials.,https://www.govern.ad/economia?format=feed&type=rss,4,Economía,4,Andorra,ca,False,30 +168,Govern d’Andorra – Educació,Actualitat educativa i comunicats oficials.,https://www.govern.ad/educacio?format=feed&type=rss,5,Educación,4,Andorra,ca,False,30 +159,Andorra Difusió – Informatius,"Notícies de RTVA, ràdio i televisió d’Andorra.",https://www.andorradifusio.ad/rss/noticies,7,Internacional,4,Andorra,ca,False,30 +164,FEDA – Energia i Medi Ambient,Notícies de la companyia elèctrica d'Andorra.,https://www.feda.ad/sala-de-premsa/rss,8,Medio Ambiente,4,Andorra,ca,False,30 +167,Govern d’Andorra – Salut,Comunicats i actualitat sanitària.,https://www.govern.ad/salut?format=feed&type=rss,12,Salud,4,Andorra,ca,False,30 +162,ARA Andorra – Notícies,Secció andorrana del diari ARA.,https://www.ara.ad/rss,13,Sociedad,4,Andorra,ca,True,0 +158,Altaveu – Andorra,Notícies d'investigació i actualitat general.,https://www.altaveu.com/feed,13,Sociedad,4,Andorra,ca,False,30 +157,Bondia Andorra – Notícies,Notícies d'Andorra en català.,https://www.bondia.ad/rss.xml,13,Sociedad,4,Andorra,ca,True,0 +169,Comú d'Andorra la Vella – Notícies,Actualitat local de la capital andorrana.,https://www.andorralavella.ad/noticies/rss,13,Sociedad,4,Andorra,ca,False,30 +170,Comú d'Escaldes-Engordany – Notícies,"Municipi d’Escaldes: cultura, societat i projectes.",https://www.e-e.ad/noticies/rss,13,Sociedad,4,Andorra,ca,False,30 +156,Diari d'Andorra – Actualitat,"El principal diari del país, notícies generals.",https://www.diariandorra.ad/rss,13,Sociedad,4,Andorra,ca,False,30 +160,El Periòdic d'Andorra – Actualitat,"Notícies, política i societat.",https://www.elperiodic.ad/rss.xml,13,Sociedad,4,Andorra,ca,False,30 +163,Pirineus TV – Notícies Andorra,Televisió local amb notícies d'Andorra i Pirineus.,https://www.pirineustv.cat/feed,13,Sociedad,4,Andorra,ca,False,30 +165,VisitAndorra – Turisme,Informació turística i cultural del país.,https://visitandorra.com/rss,15,Viajes,4,Andorra,ca,False,30 +180,ANGOP – Ciência & Saúde,"Ciência, saúde pública e investigação.",https://www.angop.ao/angola/pt_pt/rss/saude.html,1,Ciencia,5,Angola,pt,False,30 +183,Nação Cultura – Angola,"Cultura, música, literatura e arte angolana.",https://www.nacaoangolana.com/cultura/feed,2,Cultura,5,Angola,pt,False,30 +179,ANGOP – Desporto,"Desporto angolano: futebol, basquete, seleções.",https://www.angop.ao/angola/pt_pt/rss/desporto.html,3,Deportes,5,Angola,pt,False,30 +187,Desporto em Angola,Portal desportivo independente.,https://desportoemangola.com/feed,3,Deportes,5,Angola,pt,False,30 +178,ANGOP – Economia,"Economia, empresas, mercados e petróleo em Angola.",https://www.angop.ao/angola/pt_pt/rss/economia.html,4,Economía,5,Angola,pt,False,30 +181,Revista Economia & Mercado,"Revista angolana de negócios, petróleo e finanças.",https://economiaemercado.co.ao/rss,4,Economía,5,Angola,pt,False,30 +177,ANGOP – Agência Angola Press,Agência oficial de notícias de Angola.,https://www.angop.ao/angola/pt_pt/rss/general.html,7,Internacional,5,Angola,pt,False,30 +171,Club-K – Angola Noticias,Portal de información independiente sobre Angola.,https://club-k.net/index.php?option=com_rss&view=feed&type=feed&id=1,7,Internacional,5,Angola,pt,False,30 +174,Folha 8 – Angola,"Periódico angolano: política, economía y cultura.",https://jornalf8.net/feed,7,Internacional,5,Angola,pt,True,0 +173,Notícias de Angola,Portal general de noticias angolano.,https://noticiasdeangola.co.ao/feed,7,Internacional,5,Angola,pt,True,0 +172,OPaís – Angola,"Diario angoleño: política, sociedad y economía.",https://opais.co.ao/feed,7,Internacional,5,Angola,pt,False,30 +175,Portal de Angola,Portal informativo de Angola y su diáspora.,https://portaldeangola.com/feed,7,Internacional,5,Angola,pt,True,0 +185,Revista Chocolate Angola – Lifestyle,"Moda, tendências e lifestyle em Angola.",https://www.revistachocolate.com/rss,9,Moda,5,Angola,pt,False,30 +189,Correio da Kianda – Atualidade,Política e atualidade angolana.,https://correiokianda.info/feed,11,Política,5,Angola,pt,False,30 +182,Maka Angola – Investigação,Jornalismo investigativo independente.,https://www.makaangola.org/feed,11,Política,5,Angola,pt,True,0 +186,Portal de Saúde Angola,"Notícias de saúde pública, campanhas e hospitais.",https://saudeangola.com/rss,12,Salud,5,Angola,pt,True,0 +176,Jornal de Angola – Atualidade,"Diário estatal angolano, notícias gerais.",https://www.jornaldeangola.ao/rss,13,Sociedad,5,Angola,pt,False,30 +188,Luanda Post – Notícias,Notícias gerais sobre Luanda e províncias.,https://luandapost.com/feed,13,Sociedad,5,Angola,pt,True,0 +190,Economia Digital Angola,"Tecnologia, transformação digital e negócios.",https://economiadigital.ao/feed,14,Tecnología,5,Angola,pt,False,30 +184,MenosFios – Tecnologia Angola,O maior portal de tecnologia de Angola.,https://www.menosfios.com/feed,14,Tecnología,5,Angola,pt,True,0 +193,Loop Caribbean – Antigua Edition,Actualidad regional y de Antigua.,https://www.loopnews.com/rss/caribbean/antigua,7,Internacional,6,Antigua y Barbuda,en,False,30 +196,SkNVibes – Antigua News,Noticias regionales de Antigua y el Caribe.,https://www.sknvibes.com/rss/antigua.xml,7,Internacional,6,Antigua y Barbuda,en,False,30 +197,The Daily Herald – Antigua Section,"Noticias del noreste del Caribe, incluye Antigua.",https://www.thedailyherald.sx/?format=feed&type=rss,7,Internacional,6,Antigua y Barbuda,en,False,30 +199,Caribbean Environment – Antigua,Noticias medioambientales de Antigua y región.,https://www.caribbeanenvironment.org/feed,8,Medio Ambiente,6,Antigua y Barbuda,en,False,30 +194,Caribbean News Global – Antigua,Noticias económicas y políticas de Antigua y Barbuda.,https://caribbeannewsglobal.com/category/antigua-and-barbuda/feed,11,Política,6,Antigua y Barbuda,en,True,0 +191,Antigua Newsroom – News,Noticias diarias de Antigua y Barbuda.,https://antiguanewsroom.com/feed,13,Sociedad,6,Antigua y Barbuda,en,True,0 +192,Antigua Observer – Local News,Periódico principal del país.,https://antiguaobserver.com/feed,13,Sociedad,6,Antigua y Barbuda,en,True,0 +198,Caribbean Broadcasting Union – Tech Caribbean,Tecnología y medios del Caribe.,https://www.cbu.org/cbu-news/feed,14,Tecnología,6,Antigua y Barbuda,en,False,30 +195,Caribbean Journal – Travel Antigua,Noticias de turismo y destinos de Antigua.,https://www.caribjournal.com/category/destinations/antigua/feed,15,Viajes,6,Antigua y Barbuda,en,False,30 +200,Visit Antigua – Tourism Updates,"Turismo, cultura y actividades.",https://visitantiguabarbuda.com/feed,15,Viajes,6,Antigua y Barbuda,en,True,0 +223,Babzman – Histoire & Culture,"Culture algérienne, histoire, patrimoine.",https://babzman.com/feed,2,Cultura,8,Argelia,fr,True,0 +228,Radio Algérienne – Culture,"Actualité culturelle, arts et musique.",https://www.radioalgerie.dz/chaine2/fr/rss.xml,2,Cultura,8,Argelia,fr,False,30 +218,Competition.dz – Football,Le site du championnat algérien de football.,https://www.competition.dz/component/k2/itemlist?format=feed&moduleID=32,3,Deportes,8,Argelia,fr,False,30 +219,DZFoot – Football Algérien,Actualité du football algérien.,https://www.dzfoot.com/feed,3,Deportes,8,Argelia,fr,True,0 +224,La Gazette du Fennec – Sport,Actualité sportive centrée sur les Fennecs.,https://lagazettedufennec.com/feed,3,Deportes,8,Argelia,fr,True,0 +217,Planète Sport – Algérie,"Actualité sportive algérienne: foot, hand, basket.",https://www.planetesport.dz/feed,3,Deportes,8,Argelia,fr,False,30 +208,Algérie Eco – Économie,"Actualité économique, entreprises et marchés en Algérie.",https://www.algerie-eco.com/feed,4,Economía,8,Argelia,fr,True,0 +231,Maghreb Emergent – Économie,"Économie, entreprises et marchés dans le Maghreb.",https://maghrebeconomique.com/feed,4,Economía,8,Argelia,fr,False,30 +201,APS – Algérie Presse Service,Agence de presse officielle d’Algérie.,https://www.aps.dz/fr/rss,7,Internacional,8,Argelia,fr,False,30 +230,Algeria Today – English News,Portal en inglés con noticias nacionales y regionales.,https://www.algeriatoday.com/feed,7,Internacional,8,Argelia,en,False,30 +212,Echorouk Online – Arabic,الشروق أونلاين: أخبار الجزائر والعالم.,https://www.echoroukonline.com/feed,7,Internacional,8,Argelia,ar,True,0 +215,Interlignes Algérie – News,"Actualité, société, politique et culture.",https://www.inter-lignes.com/feed,7,Internacional,8,Argelia,fr,False,30 +226,Radio Algérie Internationale – Actualité,Actualité nationale en continu via la radio publique.,https://www.radioalgerie.dz/rai/fr/rss.xml,7,Internacional,8,Argelia,fr,False,30 +205,TSA – Tout Sur l’Algérie,"Actualité générale, politique et économie.",https://www.tsa-algerie.com/feed,7,Internacional,8,Argelia,fr,True,0 +222,Algérie Green – Environnement,"Écologie, climat, environnement en Algérie.",https://algeriegreen.com/feed,8,Medio Ambiente,8,Argelia,fr,False,30 +225,Algérie Part – Enquêtes,"Investigations, politique, société.",https://algeriepart.com/feed,11,Política,8,Argelia,fr,False,30 +209,La Patrie News – Politique,"Journal axé sur politique, institutions et géopolitique.",https://www.lapatrienews.com/feed,11,Política,8,Argelia,fr,False,30 +221,Sante-DZ – Santé Algérie,Actualité médicale et santé publique en Algérie.,https://www.sante-dz.com/feed,12,Salud,8,Argelia,fr,False,30 +229,Algerie360 – Actualité,Un des plus grands portails généralistes du pays.,https://www.algerie360.com/feed,13,Sociedad,8,Argelia,fr,True,0 +210,Algérie Focus – Société,"Actualité sociale, culturelle et politique.",https://www.algerie-focus.com/feed,13,Sociedad,8,Argelia,fr,False,30 +216,Algérie Monde Infos – Actualité,Portal d’actualités de société et politique.,https://www.algeriemondeinfos.com/feed,13,Sociedad,8,Argelia,fr,True,0 +211,El Khabar – Arabic News,الخبر: جريدة جزائرية يومية مستقلة.,https://www.elkhabar.com/rss,13,Sociedad,8,Argelia,ar,False,30 +202,El Moudjahid – Actualité,Journal national algérien en français.,https://www.elmoudjahid.dz/fr/rss,13,Sociedad,8,Argelia,fr,False,30 +203,El Watan – Actualité,L’un des plus grands journaux indépendants d’Algérie.,https://www.elwatan.dz/feed,13,Sociedad,8,Argelia,fr,False,30 +213,Ennahar Online – Arabic,النهار أونلاين: أخبار، سياسة، رياضة.,https://www.ennaharonline.com/feed,13,Sociedad,8,Argelia,ar,True,0 +206,HuffPost Maghreb – Algérie,Section Algérie du HuffPost Maghreb.,https://www.huffpostmaghreb.com/feeds/index.xml,13,Sociedad,8,Argelia,fr,False,30 +232,Impact24 – Actualité Algérie,"Actualité sociale, politique et faits divers.",https://impact24.info/feed,13,Sociedad,8,Argelia,fr,False,30 +207,Le Soir d’Algérie – Actualité,Presse générale algérienne.,https://www.lesoirdalgerie.com/rss,13,Sociedad,8,Argelia,fr,False,30 +204,Liberté – Algérie News,"Journal indépendant couvrant société, économie et politique.",https://www.liberte-algerie.com/rss,13,Sociedad,8,Argelia,fr,False,30 +214,Observ'Algérie – Actualité,Actualité algérienne en continu.,https://www.observalgerie.com/feed,13,Sociedad,8,Argelia,fr,True,0 +227,Radio Algérienne – Chaîne 3,La chaîne d'information en français de la radio algérienne.,https://www.radioalgerie.dz/chaine3/fr/rss.xml,13,Sociedad,8,Argelia,fr,False,30 +220,Tech DZ – Technologie Algérie,Actualité tech algérienne et startups.,https://www.techdz.net/feed,14,Tecnología,8,Argelia,fr,False,30 +326,Agencia CyTA – Fundación Leloir,Comunicados y avances científicos de la Fundación Leloir.,https://www.agenciacyta.org.ar/feed/,1,Ciencia,9,Argentina,es,True,0 +323,Agencia TSS – Tecnología y Ciencia UNGS,Divulgación científica universitaria argentina.,https://www.agencia.tss.org.ar/feed/,1,Ciencia,9,Argentina,es,False,30 +329,Agencia UNCiencia – Universidad Nacional de Córdoba,Investigación y ciencia universitaria.,https://unciencia.unc.edu.ar/feed/,1,Ciencia,9,Argentina,es,True,0 +328,Agencia de Noticias UNQ – Ciencia,Ciencia e investigación de la Universidad Nacional de Quilmes.,https://agencia.unq.edu.ar/?feed=rss2,1,Ciencia,9,Argentina,es,True,0 +332,Argentina.gob.ar – Ciencia y Tecnología,Comunicados científicos y proyectos del Ministerio.,https://www.argentina.gob.ar/ciencia/noticias/feed,1,Ciencia,9,Argentina,es,False,30 +324,CONICET – Noticias de Ciencia,Consejo Nacional de Investigaciones Científicas y Técnicas.,https://www.conicet.gov.ar/feed/,1,Ciencia,9,Argentina,es,False,30 +325,Ciencia del Sur – Divulgación,Portal científico argentino especializado en divulgación y opinión.,https://www.cienciasdelsur.com/feed/,1,Ciencia,9,Argentina,es,True,0 +314,Clarín – Ciencia,"Noticias científicas, espacio, salud y descubrimientos.",https://www.clarin.com/rss/ciencia/,1,Ciencia,9,Argentina,es,False,30 +316,Infobae – Ciencia,"Avances científicos, salud, espacio e investigaciones.",https://www.infobae.com/ciencia/rss,1,Ciencia,9,Argentina,es,False,30 +330,Investigación y Ciencia – Argentina,Notas científicas adaptadas al país.,https://www.investigacionyciencia.es/rss,1,Ciencia,9,Argentina,es,False,30 +315,La Nación – Ciencia,"Ciencia, salud, medio ambiente e investigación.",https://www.lanacion.com.ar/rss/ciencia/,1,Ciencia,9,Argentina,es,False,30 +319,La Voz – Ciencia,Noticias científicas del diario cordobés La Voz.,https://www.lavoz.com.ar/rss/ciencia/,1,Ciencia,9,Argentina,es,False,30 +321,Los Andes – Ciencia,Sección científica del diario mendocino Los Andes.,https://www.losandes.com.ar/ciencia/feed/,1,Ciencia,9,Argentina,es,False,30 +322,Mdzol – Ciencia,"Ciencia, salud y curiosidades científicas.",https://www.mdzol.com/rss/ciencia/,1,Ciencia,9,Argentina,es,False,30 +318,Perfil – Ciencia,"Ciencia, medicina, biología y evolución.",https://www.perfil.com/rss/ciencia.xml,1,Ciencia,9,Argentina,es,False,30 +244,Página/12 – Ciencia,"Divulgación científica, salud e investigaciones.",https://www.pagina12.com.ar/rss/ciencia,1,Ciencia,9,Argentina,es,False,30 +331,Revista Ciencia Hoy – Argentina,Divulgación científica argentina desde 1988.,https://cienciahoy.org.ar/feed/,1,Ciencia,9,Argentina,es,False,30 +327,Revista Exactas UBA,Divulgación científica de la Facultad de Ciencias Exactas UBA.,https://exactas.uba.ar/feed/,1,Ciencia,9,Argentina,es,True,0 +320,Río Negro – Ciencia,"Ciencia, salud, investigaciones y espacio.",https://www.rionegro.com.ar/rss/ciencia,1,Ciencia,9,Argentina,es,False,30 +317,TN – Ciencia,"Descubrimientos, salud, medicina, espacio.",https://tn.com.ar/ciencia/rss.xml,1,Ciencia,9,Argentina,es,False,30 +252,Olé – Deportes,Principal diario deportivo del país.,https://www.ole.com.ar/rss/ole.xml,3,Deportes,9,Argentina,es,False,30 +360,Agencia Télam – Economía,Agencia pública de noticias económicas.,https://www.telam.com.ar/rss2/economia.xml,4,Economía,9,Argentina,es,False,30 +249,BAE Negocios – Economía,"Economía, empresas, mercado laboral, dólar.",https://www.baenegocios.com/rss/feed.xml,4,Economía,9,Argentina,es,False,30 +338,BAE Negocios – Economía,"Empresas, mercados, dólar, industria y finanzas.",https://www.baenegocios.com/rss/economia,4,Economía,9,Argentina,es,False,30 +358,Cadena 3 – Economía,Mercados y análisis económico desde Córdoba.,https://www.cadena3.com/rss/economia,4,Economía,9,Argentina,es,False,30 +352,Chequeado – Economía,Verificación y análisis económico.,https://chequeado.com/seccion/economia/feed/,4,Economía,9,Argentina,es,False,30 +234,Clarín – Economía,"Economía argentina, dólar, inflación y mercados.",https://www.clarin.com/rss/economia/,4,Economía,9,Argentina,es,True,0 +353,Diario AR – Economía,Economía y política económica.,https://www.diarioar.com/rss/economia,4,Economía,9,Argentina,es,True,0 +362,Diario Norte – Economía,Noticias económicas del nordeste argentino.,https://www.diarionorte.com/rss/economia,4,Economía,9,Argentina,es,False,30 +359,EcoJournal – Energía & Economía,"Energía, petróleo, minería, economía energética.",https://www.ecojournal.com.ar/feed/,4,Economía,9,Argentina,es,False,30 +337,El Cronista – Economía,"Economía, negocios, mercados e inflación.",https://www.cronista.com/rss/economia/,4,Economía,9,Argentina,es,False,30 +346,El Litoral – Economía,"Mercados, empresas y finanzas.",https://www.ellitoral.com/rss/economia,4,Economía,9,Argentina,es,True,0 +348,El Tribuno – Economía,Economía del NOA y del país.,https://www.eltribuno.com/rss/economia.xml,4,Economía,9,Argentina,es,True,0 +356,FM Millenium – Economía,Informe económico y finanzas personales.,https://fmmillenium.com.ar/feed,4,Economía,9,Argentina,es,False,30 +355,IP – Señal Informativa – Economía,Economía en el canal IP Noticias.,https://ipnoticias.ar/rss/economia,4,Economía,9,Argentina,es,False,30 +240,Infobae – Economía,"Economía, dólar, inflación, empresas y finanzas.",https://www.infobae.com/economia/rss,4,Economía,9,Argentina,es,False,30 +349,Infonegocios – Argentina,"Negocios, empresas y emprendedores.",https://infonegocios.info/rss,4,Economía,9,Argentina,es,False,30 +347,La Capital – Economía,Economía rosarina y nacional.,https://www.lacapital.com.ar/rss/economia.xml,4,Economía,9,Argentina,es,False,30 +237,La Nación – Economía,Actualidad económica nacional e internacional.,https://www.lanacion.com.ar/rss/economia/,4,Economía,9,Argentina,es,False,30 +342,La Voz – Economía,Economía argentina desde Córdoba.,https://www.lavoz.com.ar/rss/economia/,4,Economía,9,Argentina,es,False,30 +354,Letra P – Economía,"Economía provincial, nacional y negocios públicos.",https://www.letrap.com/rss/economia.xml,4,Economía,9,Argentina,es,False,30 +344,Los Andes – Economía,Mercados y negocios desde Mendoza.,https://www.losandes.com.ar/economia/feed/,4,Economía,9,Argentina,es,False,30 +345,Mdzol – Economía,Economía regional y nacional desde Mendoza.,https://www.mdzol.com/rss/economia/,4,Economía,9,Argentina,es,False,30 +341,Perfil – Economía,"Economía argentina y global, mercados, empresas.",https://www.perfil.com/rss/economia.xml,4,Economía,9,Argentina,es,False,30 +243,Página/12 – Economía,"Economía política, análisis y reportajes.",https://www.pagina12.com.ar/rss/economia,4,Economía,9,Argentina,es,False,30 +357,Radio Mitre – Economía,Economía nacional desde Radio Mitre.,https://radiomitre.cienradios.com/feed/economia,4,Economía,9,Argentina,es,False,30 +350,Revista Apertura – Negocios,"Negocios, empresas, management y economía.",https://www.apertura.com/rss/negocios.xml,4,Economía,9,Argentina,es,False,30 +343,Río Negro – Economía,"Economía, petróleo, Vaca Muerta, energía.",https://www.rionegro.com.ar/rss/economia,4,Economía,9,Argentina,es,False,30 +361,Sitio Andino – Economía,Economía de Mendoza y región.,https://www.sitioandino.com.ar/rss/economia,4,Economía,9,Argentina,es,False,30 +247,TN – Economía,Sección económica del canal de noticias TN.,https://tn.com.ar/economia/rss.xml,4,Economía,9,Argentina,es,False,30 +351,iProfesional – Economía,"Actualidad económica, dólar y empresas.",https://www.iprofesional.com/rss/economia,4,Economía,9,Argentina,es,True,0 +333,Ámbito Financiero – Economía,Diario especializado en economía y finanzas.,https://www.ambito.com/rss/economia.xml,4,Economía,9,Argentina,es,True,0 +248,Ámbito Financiero – Economía,"Diario especializado en economía, finanzas y mercados.",https://www.ambito.com/rss/portada.xml,4,Economía,9,Argentina,es,False,30 +282,Diario Río Negro – Opinión,Columnas de opinión política y análisis.,https://www.rionegro.com.ar/rss/opinion,10,Opinión,9,Argentina,es,False,30 +290,El Diario AR – Opinión,Análisis político crítico e independiente.,https://www.diarioar.com/rss/opinion,10,Opinión,9,Argentina,es,True,0 +292,Ámbito – Opinión,Columnas políticas y análisis económico-político.,https://www.ambito.com/rss/opiniones.xml,10,Opinión,9,Argentina,es,False,30 +285,C5N – Política,Noticias políticas del canal C5N.,https://www.c5n.com/rss/politica,11,Política,9,Argentina,es,False,30 +284,Canal 26 – Política,Noticias políticas del canal 26 TV.,https://www.canal26.com/rss/politica.xml,11,Política,9,Argentina,es,False,30 +235,Clarín – Política,Noticias políticas de Argentina y el mundo.,https://www.clarin.com/rss/politica/,11,Política,9,Argentina,es,True,0 +286,Continental – Política,Sección política de Radio Continental.,https://continental.com.ar/rss/politica,11,Política,9,Argentina,es,True,0 +265,Diario AR – Política,Sección política del medio independiente DiarioAR.,https://www.diarioar.com/rss/politica,11,Política,9,Argentina,es,True,0 +277,Diario Popular – Política,Actualidad política nacional y sindical.,https://www.diariopopular.com.ar/rss/politica.xml,11,Política,9,Argentina,es,True,0 +281,El Cordillerano – Política,Actualidad política en Bariloche y región andina.,https://www.elcordillerano.com.ar/rss/politica,11,Política,9,Argentina,es,True,0 +260,El Cronista – Política,"Actualidad política, economía política y decisiones del Gobierno.",https://www.cronista.com/rss/politica/,11,Política,9,Argentina,es,False,30 +264,El Destape – Política,Noticias políticas y análisis progresista.,https://www.eldestapeweb.com/rss/politica,11,Política,9,Argentina,es,False,30 +280,El Día – Política,"Diario de La Plata, actualidad política nacional y provincial.",https://www.eldia.com/rss/politica,11,Política,9,Argentina,es,False,30 +275,El Liberal – Política,Diario de Santiago del Estero: política y actualidad.,https://www.elliberal.com.ar/rss/politica,11,Política,9,Argentina,es,False,30 +269,El Litoral – Política,Política y actualidad desde Santa Fe.,https://www.ellitoral.com/rss/politica,11,Política,9,Argentina,es,True,0 +272,El Tribuno – Política,Política argentina y del NOA.,https://www.eltribuno.com/rss/politica.xml,11,Política,9,Argentina,es,True,0 +283,FM La Patriada – Política,Radio partidaria con noticias y análisis político.,https://lapatriada.com.ar/feed,11,Política,9,Argentina,es,False,30 +255,Infobae – Política,"Actualidad política nacional, Congreso y Gobierno.",https://www.infobae.com/politica/rss,11,Política,9,Argentina,es,False,30 +274,Infobrisas – Política,Actualidad política de Mar del Plata y región.,https://infobrisas.com/rss/politica,11,Política,9,Argentina,es,True,0 +273,Infocielo – Política,Noticias políticas de la provincia de Buenos Aires.,https://infocielo.com/rss/politica,11,Política,9,Argentina,es,False,30 +291,Infonews – Política,Portal progresista con actualidad política.,https://www.infonews.com/rss/politica,11,Política,9,Argentina,es,False,30 +279,LM Neuquén – Política,"Política en Neuquén, Patagonia y nación.",https://www.lmneuquen.com/rss/politica,11,Política,9,Argentina,es,False,30 +278,La Arena – Política,Diario de La Pampa: política provincial y nacional.,https://www.laarena.com.ar/rss/politica.xml,11,Política,9,Argentina,es,True,0 +271,La Capital – Política,Sección política del diario rosarino La Capital.,https://www.lacapital.com.ar/rss/politica.xml,11,Política,9,Argentina,es,False,30 +289,La Gaceta – Política,Diario tucumano: actualidad política y gobierno.,https://www.lagaceta.com.ar/rss/politica,11,Política,9,Argentina,es,False,30 +238,La Nación – Política,Cobertura política del diario La Nación.,https://www.lanacion.com.ar/rss/politica/,11,Política,9,Argentina,es,False,30 +262,La Política Online – Argentina,Portal especializado en política argentina.,https://www.lapoliticaonline.com/section/rss/argentina/,11,Política,9,Argentina,es,False,30 +266,La Voz – Política,Política argentina desde Córdoba.,https://www.lavoz.com.ar/rss/politica/,11,Política,9,Argentina,es,False,30 +263,Letra P – Política,"Análisis político, gobernadores y poder provincial.",https://www.letrap.com/rss/politica.xml,11,Política,9,Argentina,es,False,30 +268,Los Andes – Política,Sección política del diario Los Andes.,https://www.losandes.com.ar/politica/rss,11,Política,9,Argentina,es,False,30 +267,Mdzol – Política,Cobertura de política nacional desde Mendoza.,https://www.mdzol.com/rss/politica/,11,Política,9,Argentina,es,False,30 +261,Minutouno – Política,Noticias políticas nacionales e internacionales.,https://www.minutouno.com/rss/politica/,11,Política,9,Argentina,es,False,30 +251,Perfil – Política,Análisis y columnas políticas del diario Perfil.,https://www.perfil.com/rss/politica.xml,11,Política,9,Argentina,es,False,30 +256,Página/12 – Política,"Política, análisis y reportajes del diario Página/12.",https://www.pagina12.com.ar/rss/politica,11,Política,9,Argentina,es,False,30 +287,Radio Mitre – Política,Política nacional desde Radio Mitre.,https://radiomitre.cienradios.com/feed/politica,11,Política,9,Argentina,es,False,30 +288,RosarioPlus – Política,Actualidad política de Rosario y región litoral.,https://www.rosarioplus.com/rss/politica,11,Política,9,Argentina,es,False,30 +270,Río Negro – Política,Cobertura política de la Patagonia.,https://www.rionegro.com.ar/rss/politica,11,Política,9,Argentina,es,False,30 +246,TN – Política,Noticias políticas del canal Todo Noticias.,https://tn.com.ar/politica/rss.xml,11,Política,9,Argentina,es,False,30 +276,Tiempo Argentino – Política,Medio cooperativo con enfoque político.,https://www.tiempoar.com.ar/feed/?post_type=politica,11,Política,9,Argentina,es,True,0 +259,Ámbito – Política,Noticias políticas del diario Ámbito Financiero.,https://www.ambito.com/rss/politica.xml,11,Política,9,Argentina,es,True,0 +233,Clarín – Últimas Noticias,El diario más leído de Argentina: noticias generales.,https://www.clarin.com/rss/lo-ultimo/,13,Sociedad,9,Argentina,es,True,0 +239,Infobae – Últimas Noticias,Actualidad nacional e internacional.,https://www.infobae.com/feeds/rss/,13,Sociedad,9,Argentina,es,False,30 +236,La Nación – Últimas Noticias,Diario histórico argentino.,https://www.lanacion.com.ar/rss/inicio/,13,Sociedad,9,Argentina,es,False,30 +250,Perfil – Últimas Noticias,"Política, sociedad y opinión.",https://www.perfil.com/rss.xml,13,Sociedad,9,Argentina,es,False,30 +242,Página/12 – Últimas Noticias,Diario progresista argentino.,https://www.pagina12.com.ar/rss/portada,13,Sociedad,9,Argentina,es,False,30 +245,TN – Todo Noticias – Último Momento,Canal de noticias 24h.,https://tn.com.ar/rss.xml,13,Sociedad,9,Argentina,es,True,0 +301,BAE Negocios – Tecno,"Innovación, telecomunicaciones y startups.",https://www.baenegocios.com/rss/tecno,14,Tecnología,9,Argentina,es,False,30 +306,C5N – Tecnología,Noticias tecnológicas del canal C5N.,https://www.c5n.com/rss/tecno,14,Tecnología,9,Argentina,es,False,30 +307,Canal 26 – Tecnología,"Tecnología, redes sociales, IA y apps.",https://www.canal26.com/rss/tecnologia.xml,14,Tecnología,9,Argentina,es,False,30 +294,Clarín – Tecnología,"Noticias sobre ciencia, celulares, apps e innovación.",https://www.clarin.com/rss/tecnologia/,14,Tecnología,9,Argentina,es,True,0 +304,DataFactory – Tech,Tecnología deportiva y análisis estadístico.,https://datafactory.lat/feed,14,Tecnología,9,Argentina,es,False,30 +309,Diario Río Negro – Tecnología,"Sección de tecnología, ciencia e innovación.",https://www.rionegro.com.ar/rss/tecnologia,14,Tecnología,9,Argentina,es,False,30 +300,El Cronista – Tecnología,"Empresas tecnológicas, innovación y economía digital.",https://www.cronista.com/rss/tecnologia/,14,Tecnología,9,Argentina,es,False,30 +312,El Litoral – Tecnología,"Tecnología, redes, ciencia y cultura digital.",https://www.ellitoral.com/rss/tecnologia,14,Tecnología,9,Argentina,es,False,30 +241,Infobae – Tecno,"Sección de tecnología, ciencia e innovación.",https://www.infobae.com/tecno/rss,14,Tecnología,9,Argentina,es,False,30 +303,Infotechnology (IT Business Argentina),Medio argentino especializado en tecnología y negocios.,https://www.infotechnology.com/rss,14,Tecnología,9,Argentina,es,False,30 +295,La Nación – Tecnología,"Tecnología, IA, internet, seguridad, ciencia.",https://www.lanacion.com.ar/rss/tecnologia/,14,Tecnología,9,Argentina,es,False,30 +308,La Voz – Tecnología,Tech y ciencia desde Córdoba.,https://www.lavoz.com.ar/rss/tecnologia/,14,Tecnología,9,Argentina,es,False,30 +310,Los Andes – Tecnología,Noticias tech del diario mendocino Los Andes.,https://www.losandes.com.ar/tecnologia/feed/,14,Tecnología,9,Argentina,es,False,30 +311,Mdzol – Tecnología,Tecnología y ciencia desde Mendoza.,https://www.mdzol.com/rss/tecno/,14,Tecnología,9,Argentina,es,False,30 +302,Olé – E-Sports,"E-sports, gaming y deportes electrónicos.",https://www.ole.com.ar/rss/esports.xml,14,Tecnología,9,Argentina,es,False,30 +298,Perfil – Tecnología,"Tecnología, ciencia y ciberseguridad.",https://www.perfil.com/rss/tecnologia.xml,14,Tecnología,9,Argentina,es,False,30 +305,Revista Apertura – Tecnología,"Innovación, startups, fintech y empresas tech.",https://www.apertura.com/rss/tecnologia.xml,14,Tecnología,9,Argentina,es,False,30 +296,TN Tecno – Tecnología,"Novedades de tecnología, gaming e innovación.",https://tn.com.ar/tecno/rss.xml,14,Tecnología,9,Argentina,es,False,30 +297,iProfesional – Tecnología,"Tech, startups, economía digital y negocios IT.",https://www.iprofesional.com/rss/tecnologia,14,Tecnología,9,Argentina,es,True,0 +299,Ámbito – Tecnología,"Sección de tecnología, IA y empresas digitales.",https://www.ambito.com/rss/tecnologia.xml,14,Tecnología,9,Argentina,es,True,0 +398,Armenpress – Science,Noticias científicas y tecnológicas de Armenpress.,https://armenpress.am/eng/rss/science,1,Ciencia,10,Armenia,en,False,30 +400,CivilNet – Science & Tech,"Ciencia, investigación y tecnología en Armenia.",https://civilnet.am/news/category/science-technology/feed/?lang=en,1,Ciencia,10,Armenia,en,False,30 +404,News.am – Science,Descubrimientos científicos de Armenia y el mundo.,https://news.am/eng/rss/science,1,Ciencia,10,Armenia,en,False,30 +402,PanARMENIAN – Science,Avances científicos y educativas.,https://www.panarmenian.net/eng/rss/science,1,Ciencia,10,Armenia,en,False,30 +406,Tert.am – Science (Armenian),"Գիտություն, նոր բացահայտումներ, կրթություն.",https://www.tert.am/rss/hy/science,1,Ciencia,10,Armenia,hy,False,30 +412,YSU – Faculty of Physics – Science,Investigación científica de la Universidad Estatal de Ereván.,https://physics.ysu.am/feed/,1,Ciencia,10,Armenia,en,False,30 +390,Arka News Agency – Economy,"Economía, finanzas, energía, banca.",https://arka.am/en/rss/economy,4,Economía,10,Armenia,en,False,30 +374,Arka News Agency – Economía,"Negocios, banca, energía, economía armenia.",https://arka.am/en/rss/,4,Economía,10,Armenia,en,True,0 +391,Arka News Agency – Finance,Noticias financieras y mercados armenios.,https://arka.am/en/rss/finance,4,Economía,10,Armenia,en,False,30 +392,Armenpress – Economy,Sección económica de Armenpress.,https://armenpress.am/eng/rss/economy,4,Economía,10,Armenia,en,False,30 +393,CivilNet – Economy,"Economía, negocios y desarrollo.",https://civilnet.am/news/category/economy/feed/?lang=en,4,Economía,10,Armenia,en,True,0 +395,News.am – Economy,Economía y finanzas en Armenia.,https://news.am/eng/rss/economy,4,Economía,10,Armenia,en,False,30 +394,PanARMENIAN – Economy,Economía nacional y negocios armenios.,https://www.panarmenian.net/eng/rss/economy,4,Economía,10,Armenia,en,False,30 +377,ArmInfo – News,"Noticias financieras, políticas y sociales.",https://www.arminfo.info/rss/,7,Internacional,10,Armenia,en,False,30 +364,Armenpress – Armenian,Արմենպրես՝ պաշտոնական լրատվական գործակալություն.,https://armenpress.am/rss/,7,Internacional,10,Armenia,hy,False,30 +363,Armenpress – English,Agencia oficial de noticias de Armenia.,https://armenpress.am/eng/rss/,7,Internacional,10,Armenia,en,False,30 +367,Azatutyun (Radio Liberty Armenia) – English,Armenian Service of Radio Free Europe/Radio Liberty.,https://www.azatutyun.am/rss/?lang=en,7,Internacional,10,Armenia,en,False,30 +368,Azatutyun – Armenian,"Ազատություն ռադիոկայան՝ լուրեր, վերլուծություններ.",https://www.azatutyun.am/rss/?lang=hy,7,Internacional,10,Armenia,hy,False,30 +370,PanARMENIAN.net – English News,Noticias de Armenia y la diáspora.,https://www.panarmenian.net/eng/rss,7,Internacional,10,Armenia,en,False,30 +389,ArmInfo – Politics,"Política nacional, diplomacia y gobierno.",https://www.arminfo.info/rss/?section=politics,11,Política,10,Armenia,en,False,30 +382,Azatutyun – Politics (RFE/RL Armenia),"Política, instituciones, gobierno.",https://www.azatutyun.am/rss/?sid=198,11,Política,10,Armenia,hy,False,30 +372,CivilNet – Armenian,Քաղաքացիական լրատվամիջոց՝ վերլուծություններ ու նորություններ.,https://civilnet.am/news/feed/?lang=hy,11,Política,10,Armenia,hy,False,30 +371,CivilNet – English,"Medio independiente: política, sociedad, derechos humanos.",https://civilnet.am/news/feed/?lang=en,11,Política,10,Armenia,en,False,30 +378,CivilNet – Politics,Análisis político del medio independiente CivilNet.,https://civilnet.am/news/category/politics/feed/?lang=en,11,Política,10,Armenia,en,True,0 +379,CivilNet – Politics (Armenian),Քաղաքական լուրեր և վերլուծություն CivilNet-ից.,https://civilnet.am/news/category/politics/feed/?lang=hy,11,Política,10,Armenia,hy,False,30 +373,Hetq Online – Investigations,Periodismo investigativo de Armenia.,https://hetq.am/en/rss,11,Política,10,Armenia,en,True,0 +380,Hetq – Politics,Investigaciones y política nacional.,https://hetq.am/en/rss/politics,11,Política,10,Armenia,en,False,30 +385,News.am – Politics,Política nacional e internacional.,https://news.am/eng/rss/politics,11,Política,10,Armenia,en,False,30 +383,PanARMENIAN – Politics,Actualidad política y relaciones exteriores.,https://www.panarmenian.net/eng/rss/politics,11,Política,10,Armenia,en,False,30 +387,Tert.am – Politics (Armenian),Քաղաքական լուրեր Տերտ․ ամ-ից.,https://www.tert.am/rss/hy/politics,11,Política,10,Armenia,hy,False,30 +388,Tert.am – Politics (English),Political news from Armenia and the region.,https://www.tert.am/en/rss/politics,11,Política,10,Armenia,en,False,30 +397,ArmInfo – Society,"Sociedad, cultura, temas sociales.",https://www.arminfo.info/rss/?section=social,13,Sociedad,10,Armenia,en,False,30 +369,ArmeniaNow – News,Actualidad armenia en inglés.,https://www.armenianow.com/rss.xml,13,Sociedad,10,Armenia,en,False,30 +396,ArmeniaNow – Society,Noticias sociales y comunitarias.,https://www.armenianow.com/rss/society.xml,13,Sociedad,10,Armenia,en,False,30 +381,Hetq – Society,"Sociedad, derechos humanos, justicia social.",https://hetq.am/en/rss/society,13,Sociedad,10,Armenia,en,False,30 +365,News.am – Armenia News,Portal de noticias muy popular en Armenia.,https://news.am/eng/rss,13,Sociedad,10,Armenia,en,True,0 +366,News.am – Armenian Edition,"Հայկական լրատվություն՝ politics, society, region.",https://news.am/arm/rss,13,Sociedad,10,Armenia,hy,True,0 +386,News.am – Society,Noticias de sociedad de Armenia.,https://news.am/eng/rss/society,13,Sociedad,10,Armenia,en,False,30 +384,PanARMENIAN – Society,"Sociedad armenia, cultura y temas sociales.",https://www.panarmenian.net/eng/rss/society,13,Sociedad,10,Armenia,en,False,30 +375,Tert.am – Armenian News,Լուրեր Հայաստանից և տարածաշրջանից.,https://www.tert.am/rss/hy,13,Sociedad,10,Armenia,hy,False,30 +376,Tert.am – English,Armenian news in English.,https://www.tert.am/en/rss,13,Sociedad,10,Armenia,en,False,30 +409,Arloopa – AR/VR Tech News,"Realidad aumentada, VR y tecnología 3D desde Armenia.",https://arloopa.com/blog/feed/,14,Tecnología,10,Armenia,en,False,30 +410,ArmRobotics – Robotics & AI,"Robótica, IA y automatización en Armenia.",https://www.armrobotics.am/feed,14,Tecnología,10,Armenia,en,False,30 +411,ArmSoft – Software Engineering,Desarrollo de software y tecnologías IT.,https://www.armsoft.am/feed/,14,Tecnología,10,Armenia,en,False,30 +408,Armenia Engineering Week – Tech,"Noticias de ingeniería, innovación y tecnología.",https://www.engineering.am/feed/,14,Tecnología,10,Armenia,en,False,30 +399,Armenpress – IT & High Tech,"Tecnología, innovación y sector IT.",https://armenpress.am/eng/rss/hightech,14,Tecnología,10,Armenia,en,False,30 +407,IT Armenia – Tech News,Tecnología e industria IT armenia.,https://www.itarmenia.org/feed/,14,Tecnología,10,Armenia,en,False,30 +403,News.am – High Tech,"Tecnología, ciberseguridad, startups de Armenia.",https://news.am/eng/rss/tech,14,Tecnología,10,Armenia,en,False,30 +401,PanARMENIAN – High Tech,"Startups, TI, innovación y tecnología.",https://www.panarmenian.net/eng/rss/hightech,14,Tecnología,10,Armenia,en,False,30 +405,Tert.am – High Tech (Armenian),"Բարձր տեխնոլոգիաներ, նորարարություն, ստարտափներ.",https://www.tert.am/rss/hy/tech,14,Tecnología,10,Armenia,hy,False,30 +415,ABC Science – Australia,"Ciencia, espacio, salud e investigaciones.",https://www.abc.net.au/news/science/rss,1,Ciencia,11,Australia,en,False,30 +462,Australian Academy of Science – News,Investigación científica nacional.,https://www.science.org.au/news-and-events/news/rss,1,Ciencia,11,Australia,en,False,30 +457,CSIRO – Science News,Agencia científica nacional de Australia.,https://blog.csiro.au/feed/,1,Ciencia,11,Australia,en,False,30 +456,Cosmos Magazine – Science,Revista científica australiana.,https://cosmosmagazine.com/feed/,1,Ciencia,11,Australia,en,False,30 +460,HealthDirect – Medical Science,Evidencia médica y ciencia en Australia.,https://www.healthdirect.gov.au/news-feed,1,Ciencia,11,Australia,en,False,30 +459,Medical Journal of Australia – Research,Investigación médica australiana.,https://www.mja.com.au/rss.xml,1,Ciencia,11,Australia,en,True,0 +461,Royal Institution of Australia – Science,"Noticias científicas, premios y divulgación.",https://www.riaus.org.au/feed/,1,Ciencia,11,Australia,en,False,30 +431,ScienceAlert – Australia,Portal científico australiano reconocido mundialmente.,https://www.sciencealert.com/feed,1,Ciencia,11,Australia,en,True,0 +454,The Conversation Australia – Science,Investigación universitaria explicada por académicos.,https://theconversation.com/au/topics/science-8/articles.atom,1,Ciencia,11,Australia,en,False,30 +424,The Guardian Australia – Science,"Ciencia, salud y medio ambiente.",https://www.theguardian.com/science/rss,1,Ciencia,11,Australia,en,True,0 +773,ABC Arts – Australia,"Arte, cine, literatura, arquitectura y cultura.",https://www.abc.net.au/news/arts/rss,2,Cultura,11,Australia,en,False,30 +779,Art Almanac – Australia,Calendario nacional de arte y exposiciones.,https://www.art-almanac.com.au/feed/,2,Cultura,11,Australia,en,False,30 +781,Concrete Playground Sydney – Culture,Guía cultural y urbana de Sídney.,https://concreteplayground.com/sydney/feed,2,Cultura,11,Australia,en,True,0 +782,Limelight Magazine – Arts,"Música clásica, ópera y artes escénicas.",https://www.limelightmagazine.com.au/feed/,2,Cultura,11,Australia,en,False,30 +780,Pedestrian TV – Culture,Cultura pop y tendencias australianas.,https://www.pedestrian.tv/entertainment/feed/,2,Cultura,11,Australia,en,True,0 +777,SBS – Culture,Cultura multicultural australiana.,https://www.sbs.com.au/guide/rss,2,Cultura,11,Australia,en,True,0 +774,Sydney Morning Herald – Culture,"Cultura australiana: arte, libros, cine y TV.",https://www.smh.com.au/culture/rss,2,Cultura,11,Australia,en,False,30 +775,The Age – Culture,Noticias de arte y entretenimiento cultural.,https://www.theage.com.au/culture/rss,2,Cultura,11,Australia,en,False,30 +776,The Guardian Australia – Culture,"Arte, cine, literatura y crítica cultural.",https://www.theguardian.com/culture/australia-culture/rss,2,Cultura,11,Australia,en,False,30 +778,Time Out Sydney – Arts & Culture,"Eventos culturales, arte, teatro, exposiciones.",https://www.timeout.com/sydney/feed,2,Cultura,11,Australia,en,False,30 +763,ABC News – Sport,Noticias deportivas australianas.,https://www.abc.net.au/news/sport/rss,3,Deportes,11,Australia,en,False,30 +770,AFL – Australian Football League,Fútbol australiano profesional.,https://www.afl.com.au/rss,3,Deportes,11,Australia,en,True,0 +771,Cricket Australia – News,Noticias oficiales de Cricket Australia.,https://www.cricket.com.au/rss/news,3,Deportes,11,Australia,en,False,30 +768,FOX Sports Australia – News,Cobertura deportiva profesional.,https://www.foxsports.com.au/content-feeds/news,3,Deportes,11,Australia,en,False,30 +769,NRL – Rugby League Australia,Liga nacional de rugby australia.,https://www.nrl.com/news/rss,3,Deportes,11,Australia,en,False,30 +767,News.com.au – Sport,Últimas noticias deportivas.,https://www.news.com.au/sport/rss,3,Deportes,11,Australia,en,True,0 +764,Sydney Morning Herald – Sport,Deportes nacionales e internacionales.,https://www.smh.com.au/rss/sport.xml,3,Deportes,11,Australia,en,True,0 +772,Tennis Australia – News,Eventos y noticias de tenis de Australia.,https://www.tennis.com.au/feed,3,Deportes,11,Australia,en,False,30 +765,The Age – Sport,"Fútbol, rugby, cricket, tenis.",https://www.theage.com.au/sport/rss,3,Deportes,11,Australia,en,False,30 +766,The Guardian Australia – Sport,Cobertura deportiva australiana.,https://www.theguardian.com/au/sport/rss,3,Deportes,11,Australia,en,True,0 +443,ABC News – Business,"Economía australiana, empresas y mercados.",https://www.abc.net.au/news/business/rss,4,Economía,11,Australia,en,False,30 +428,Australian Financial Review – Markets,El mayor medio económico del país.,https://www.afr.com/rss,4,Economía,11,Australia,en,False,30 +451,InvestSMART – Financial Markets,"Mercados, inversiones, fondos, análisis.",https://www.investsmart.com.au/rss,4,Economía,11,Australia,en,False,30 +452,MacroBusiness – Economics,Análisis económico profundo australiano.,https://www.macrobusiness.com.au/feed/,4,Economía,11,Australia,en,True,0 +427,News.com.au – Finance,"Finanzas personales, impuestos, inversiones.",https://www.news.com.au/finance/rss,4,Economía,11,Australia,en,True,0 +449,SBS News – Business,Negocios y economía multicultural.,https://www.sbs.com.au/news/topic/business/feed,4,Economía,11,Australia,en,False,30 +450,SmartCompany – Startups & Negocios,"Startups, pymes y economía digital australianas.",https://www.smartcompany.com.au/feed/,4,Economía,11,Australia,en,True,0 +419,Sydney Morning Herald – Business,Economía nacional e internacional.,https://www.smh.com.au/rss/business.xml,4,Economía,11,Australia,en,True,0 +421,The Age – Business,"Mercados, finanzas, empresas locales.",https://www.theage.com.au/business/rss,4,Economía,11,Australia,en,False,30 +446,The Guardian Australia – Business,"Empresas, bancos y políticas económicas.",https://www.theguardian.com/australia-news/business/rss,4,Economía,11,Australia,en,False,30 +784,ABC – Education,"Escuelas, universidad, docentes y currículum.",https://www.abc.net.au/news/education/rss,5,Educación,11,Australia,en,False,30 +786,Australian Education Union – News,Noticias del sindicato docente australiano.,https://www.aeufederal.org.au/news/feed,5,Educación,11,Australia,en,False,30 +792,CSIRO Education – STEM,Educación científica y STEM para escuelas.,https://blog.csiro.au/category/education/feed/,5,Educación,11,Australia,en,False,30 +787,EdSurge AU – EdTech,Tecnología educativa y formación docente.,https://www.edsurge.com/research/feed,5,Educación,11,Australia,en,False,30 +791,Monash University – Education News,Noticias universitarias y educación superior.,https://www.monash.edu/news/news.rss,5,Educación,11,Australia,en,False,30 +785,SBS – Education,Educación multicultural en Australia.,https://www.sbs.com.au/news/topic/education/feed,5,Educación,11,Australia,en,False,30 +783,The Conversation AU – Education,Investigación universitaria sobre educación.,https://theconversation.com/au/topics/education-4/articles.atom,5,Educación,11,Australia,en,False,30 +790,UNSW Newsroom – Education,Noticias educativas y de investigación.,https://newsroom.unsw.edu.au/rss-feed,5,Educación,11,Australia,en,False,30 +788,University of Melbourne – News,Investigación científica y educativa.,https://newsroom.melbourne.edu/rss.xml,5,Educación,11,Australia,en,False,30 +789,University of Sydney – News,"Educación, ciencia e investigación.",https://www.sydney.edu.au/news-opinion/news.rss,5,Educación,11,Australia,en,False,30 +793,ABC – Environment,"Medio ambiente, clima y biodiversidad.",https://www.abc.net.au/news/environment/rss,8,Medio Ambiente,11,Australia,en,False,30 +458,Australian Geographic – Nature,"Naturaleza, fauna, flora y conservación.",https://www.australiangeographic.com.au/feed/,8,Medio Ambiente,11,Australia,en,True,0 +798,CSIRO – Environment,Investigación ambiental oficial.,https://blog.csiro.au/category/environment/feed/,8,Medio Ambiente,11,Australia,en,False,30 +800,Dept. of Climate Change – Australia,Políticas climáticas y ambientales oficiales.,https://www.dcceew.gov.au/news-media/rss,8,Medio Ambiente,11,Australia,en,False,5 +802,Environment Victoria – Climate & Policy,Clima y políticas ambientales estatales.,https://environmentvictoria.org.au/feed/,8,Medio Ambiente,11,Australia,en,True,0 +799,Nature Conservancy Australia – News,Conservación y clima.,https://www.natureaustralia.org.au/newsroom/rss/,8,Medio Ambiente,11,Australia,en,False,30 +796,SBS – Environment,Noticias ambientales multiculturales.,https://www.sbs.com.au/news/topic/environment/feed,8,Medio Ambiente,11,Australia,en,False,30 +795,The Conversation AU – Environment,Ciencia del clima y naturaleza.,https://theconversation.com/au/topics/environment-7/articles.atom,8,Medio Ambiente,11,Australia,en,False,30 +794,The Guardian AU – Environment,Cambio climático y ecología australiana.,https://www.theguardian.com/environment/australia-news/rss,8,Medio Ambiente,11,Australia,en,False,30 +801,WWF Australia – News,"Medio ambiente, fauna y conservación.",https://www.wwf.org.au/rss.xml,8,Medio Ambiente,11,Australia,en,False,30 +809,Elle Australia – Fashion,Noticias y tendencias de moda en Australia.,https://www.elle.com.au/rss,9,Moda,11,Australia,en,False,30 +810,Harper's Bazaar Australia – Fashion,"Pasarelas, estilo, celebridades.",https://www.harpersbazaar.com.au/rss,9,Moda,11,Australia,en,True,0 +808,Vogue Australia – Fashion,"Moda, diseño y tendencias.",https://www.vogue.com.au/rss,9,Moda,11,Australia,en,True,0 +414,ABC News – Politics,"Política federal, elecciones y parlamento.",https://www.abc.net.au/news/politics/rss,11,Política,11,Australia,en,False,30 +439,Crikey – Politics,Periodismo independiente australiano de investigación política.,https://www.crikey.com.au/feed/,11,Política,11,Australia,en,True,0 +442,Michael West Media – Politics,Investigación política y corrupción en Australia.,https://www.michaelwest.com.au/feed/,11,Política,11,Australia,en,True,0 +437,News.com.au – Politics,"Noticias de política federal, partidos y debates.",https://www.news.com.au/national/politics/rss,11,Política,11,Australia,en,True,0 +438,SBS News – Politics,Política multicultural y nacional.,https://www.sbs.com.au/news/topic/politics/feed,11,Política,11,Australia,en,False,30 +418,Sydney Morning Herald – Politics,"Política australiana, opinión y análisis.",https://www.smh.com.au/rss/politics.xml,11,Política,11,Australia,en,False,30 +435,The Age – Politics,Política estatal y nacional.,https://www.theage.com.au/politics/federal/rss,11,Política,11,Australia,en,False,30 +441,The Australian – Politics,Política y análisis conservador australiano.,https://www.theaustralian.com.au/nation/politics/rss,11,Política,11,Australia,en,True,0 +440,The Conversation AU – Politics,Análisis académico sobre política australiana.,https://theconversation.com/au/topics/politics-124/articles.atom,11,Política,11,Australia,en,False,30 +423,The Guardian Australia – Politics,Política australiana y debates legislativos.,https://www.theguardian.com/australia-news/australian-politics/rss,11,Política,11,Australia,en,True,0 +811,ABC Health & Wellbeing,Salud pública y bienestar.,https://www.abc.net.au/news/health/rss,12,Salud,11,Australia,en,False,30 +812,The Medical Republic – Australia,Noticias médicas y de salud profesional.,https://www.medicalrepublic.com.au/feed,12,Salud,11,Australia,en,True,0 +479,7News Australia – Society,Noticias sociales de la cadena Seven.,https://7news.com.au/news/australia/rss,13,Sociedad,11,Australia,en,True,0 +473,ABC News – Australia,Noticias nacionales de sociedad.,https://www.abc.net.au/news/australia/rss,13,Sociedad,11,Australia,en,False,30 +482,NCA NewsWire – Society,Noticias de sociedad y emergencias.,https://www.news.com.au/national/breaking-news/rss,13,Sociedad,11,Australia,en,True,0 +426,News.com.au – National,Sociedad australiana e historias humanas.,https://www.news.com.au/national/rss,13,Sociedad,11,Australia,en,True,0 +429,SBS News – Australia,Noticias multicultural society.,https://www.sbs.com.au/news/feed,13,Sociedad,11,Australia,en,True,0 +430,SBS Persian – افغانستان/ایران مرتبط,Sección persa del servicio multicultural SBS.,https://www.sbs.com.au/language/persian/en/feed,13,Sociedad,11,Australia,en,False,30 +475,Sydney Morning Herald – National,"Sociedad, temas humanos, comunidad.",https://www.smh.com.au/rss/national.xml,13,Sociedad,11,Australia,en,True,0 +417,Sydney Morning Herald – Top News,Uno de los periódicos más importantes de Australia.,https://www.smh.com.au/rss/feed.xml,13,Sociedad,11,Australia,en,True,0 +476,The Age – National,"Sociedad australiana, comunidad, crímenes.",https://www.theage.com.au/rss/national.xml,13,Sociedad,11,Australia,en,True,0 +420,The Age – News,Noticias nacionales e internacionales.,https://www.theage.com.au/rss/feed.xml,13,Sociedad,11,Australia,en,True,0 +480,The Australian – National,"Comunidad, sociedad y reportajes.",https://www.theaustralian.com.au/nation/rss,13,Sociedad,11,Australia,en,True,0 +481,The Conversation AU – Society,Análisis social y académico.,https://theconversation.com/au/topics/society-182/articles.atom,13,Sociedad,11,Australia,en,False,30 +477,The Guardian AU – Society,"Temas sociales, comunidad, derechos.",https://www.theguardian.com/australia-news/community/rss,13,Sociedad,11,Australia,en,False,30 +422,The Guardian Australia – News,Edición australiana del periódico británico.,https://www.theguardian.com/australia-news/rss,13,Sociedad,11,Australia,en,True,0 +416,ABC – Technology,Tecnología australiana y global.,https://www.abc.net.au/news/technology/rss,14,Tecnología,11,Australia,en,False,30 +469,APC Magazine – Tech,"Hardware, PCs, reviews, tecnología.",https://www.apcmag.com/feed/,14,Tecnología,11,Australia,en,False,30 +470,AustCyber – Cybersecurity,Ciberseguridad y protección digital en Australia.,https://www.austcyber.com/news/feed,14,Tecnología,11,Australia,en,False,30 +468,CRN Australia,"Industria IT, empresas, ciberseguridad.",https://www.crn.com.au/rss,14,Tecnología,11,Australia,en,False,30 +432,Gizmodo Australia – Technology,"Gadgets, innovación y cultura digital.",https://www.gizmodo.com.au/feed/,14,Tecnología,11,Australia,en,False,30 +465,ITNews Australia,Noticias del sector IT australiano.,https://www.itnews.com.au/rss,14,Tecnología,11,Australia,en,False,30 +472,Kotaku Australia – Gaming Tech,"Gaming, esports y tecnología.",https://www.kotaku.com.au/feed/,14,Tecnología,11,Australia,en,True,0 +467,SmartCompany – Tech,"Startups australianas, SaaS, innovación.",https://www.smartcompany.com.au/technology/feed/,14,Tecnología,11,Australia,en,True,0 +466,TechAU – IT & Innovation,"Tecnología australiana, autos eléctricos, IA.",https://techau.com.au/feed/,14,Tecnología,11,Australia,en,False,30 +425,The Guardian Australia – Technology,Tecnología e innovación.,https://www.theguardian.com/technology/rss,14,Tecnología,11,Australia,en,True,0 +471,ZDNet Australia,Tecnología y negocios digitales.,https://www.zdnet.com/topic/australian-news/rss.xml,14,Tecnología,11,Australia,en,False,30 +805,Australian Traveller Magazine – Travel,Turismo interno en Australia.,https://www.australiantraveller.com/feed/,15,Viajes,11,Australia,en,False,30 +807,Concrete Playground Sydney – Travel,"Turismo urbano, escapadas, lugares.",https://concreteplayground.com/sydney/travel/feed,15,Viajes,11,Australia,en,False,30 +804,Time Out Melbourne – Travel,"Qué hacer en Melbourne, escapadas y turismo.",https://www.timeout.com/melbourne/travel/feed,15,Viajes,11,Australia,en,False,30 +803,Time Out Sydney – Travel,Guías de viaje y actividades en Sídney.,https://www.timeout.com/sydney/travel/feed,15,Viajes,11,Australia,en,False,30 +806,Traveller.com.au – News,"Viajes, destinos, aerolíneas.",https://www.traveller.com.au/rss,15,Viajes,11,Australia,en,False,30 +872,APA Science – Austria Presse Agentur,Divulgación científica y salud.,https://science.apa.at/wp-json/wp/v2/posts?per_page=10,1,Ciencia,12,Austria,de,False,30 +816,Der Standard – Wissenschaft,"Ciencia, investigación, espacio, medicina.",https://www.derstandard.at/rss/wissenschaft,1,Ciencia,12,Austria,de,True,0 +819,Die Presse – Wissen,Ciencia y educación.,https://www.diepresse.com/rss/wissen,1,Ciencia,12,Austria,de,False,30 +868,IST Austria – Institute of Science and Technology,Investigación científica avanzada.,https://ist.ac.at/en/news/rss/,1,Ciencia,12,Austria,en,True,0 +856,Kleine Zeitung – Wissenschaft,Avances científicos en Austria.,https://www.kleinezeitung.at/_services/rss/wissen,1,Ciencia,12,Austria,de,False,30 +855,Kurier – Wissenschaft,"Investigación, salud, ciencia.",https://kurier.at/wissen/rss,1,Ciencia,12,Austria,de,False,30 +827,ORF – Wissenschaft,Noticias científicas desde ORF.,https://rss.orf.at/wissenschaft.xml,1,Ciencia,12,Austria,de,False,30 +869,Symposium Lindau – Science,Ciencia y conferencias científicas europeas.,https://www.lindau-nobel.org/feed/,1,Ciencia,12,Austria,en,True,0 +865,University of Graz – Science News,Ciencia e investigación de Graz.,https://news.uni-graz.at/en/rss/,1,Ciencia,12,Austria,en,False,30 +861,Univie – Research News,Noticias científicas de la Universidad de Viena.,https://medienportal.univie.ac.at/rss/,1,Ciencia,12,Austria,de,False,30 +863,ÖAW – Austrian Academy of Sciences,Investigación de la Academia Austríaca de Ciencias.,https://www.oeaw.ac.at/en/rss,1,Ciencia,12,Austria,en,False,30 +874,Der Standard – Kultur,"Arte, música, teatro, literatura.",https://www.derstandard.at/rss/kultur,2,Cultura,12,Austria,de,True,0 +890,Die Bühne – Kulturmagazin,"Revista cultural austriaca con arte, teatro y ópera.",https://www.buehne-magazin.com/feed/,2,Cultura,12,Austria,de,False,30 +876,Die Presse – Kultur,"Noticias culturales, arte y crítica.",https://www.diepresse.com/rss/kultur,2,Cultura,12,Austria,de,True,0 +889,Falter – Kultur,"Crítica cultural, arte contemporáneo.",https://www.falter.at/rss/kultur,2,Cultura,12,Austria,de,False,30 +880,Kleine Zeitung – Kultur,Arte y cultura en Austria.,https://www.kleinezeitung.at/_services/rss/kultur,2,Cultura,12,Austria,de,False,30 +878,Kurier – Kultur,"Cine, música, teatro, exposiciones.",https://kurier.at/kultur/rss,2,Cultura,12,Austria,de,False,30 +892,Museumsbund Austria – News,Actualidad de museos y exposiciones.,https://www.museumsbund.at/feed/,2,Cultura,12,Austria,de,False,30 +882,ORF – Kultur,"Arte, música, literatura, espectáculos.",https://rss.orf.at/kultur.xml,2,Cultura,12,Austria,de,False,30 +885,OÖ Nachrichten – Kultur,Cultura desde Baja Austria.,https://www.nachrichten.at/kultur/?r=rss,2,Cultura,12,Austria,de,False,30 +883,Salzburger Nachrichten – Kultur,Noticias culturales regionales y nacionales.,https://www.sn.at/kultur/rss,2,Cultura,12,Austria,de,False,30 +887,Tiroler Tageszeitung – Kultur,Eventos y noticias culturales del Tirol.,https://www.tt.com/rss/kultur,2,Cultura,12,Austria,de,False,30 +891,Wiener Zeitung – Kultur,"Cultura clásica, música, arte y literatura.",https://www.wienerzeitung.at/_export/rss/kultur,2,Cultura,12,Austria,de,False,30 +828,ORF Sport – Sportschau,Noticias deportivas austriacas.,https://rss.orf.at/sport.xml,3,Deportes,12,Austria,de,True,0 +815,Der Standard – Wirtschaft,"Economía, negocios y mercados.",https://www.derstandard.at/rss/wirtschaft,4,Economía,12,Austria,de,True,0 +818,Die Presse – Wirtschaft,Economía austríaca y global.,https://www.diepresse.com/rss/wirtschaft,4,Economía,12,Austria,de,True,0 +824,Kleine Zeitung – Wirtschaft,"Economía austríaca, empresas y mercados.",https://www.kleinezeitung.at/_services/rss/wirtschaft,4,Economía,12,Austria,de,False,30 +832,Profil – Wirtschaft,Economía y negocios desde perspectiva crítica.,https://www.profil.at/rss/wirtschaft,4,Economía,12,Austria,de,False,30 +934,Der Standard – International,Internacional y Europa.,https://www.derstandard.at/rss/international,7,Internacional,12,Austria,de,True,0 +936,Die Presse – International,Noticias del mundo y diplomacia.,https://www.diepresse.com/rss/ausland,7,Internacional,12,Austria,de,True,0 +951,Heute – Ausland,Internacional popular y accesible.,https://www.heute.at/rss/ausland,7,Internacional,12,Austria,de,False,30 +940,Kleine Zeitung – International,Internacional y Europa del Este.,https://www.kleinezeitung.at/_services/rss/ausland,7,Internacional,12,Austria,de,False,30 +938,Kurier – Welt,"Internacional, geopolítica y conflictos.",https://kurier.at/politik/ausland/rss,7,Internacional,12,Austria,de,False,30 +941,ORF – Ausland,Noticias internacionales desde ORF News.,https://rss.orf.at/ausland.xml,7,Internacional,12,Austria,de,False,30 +946,OÖ Nachrichten – Ausland,Internacional desde Alta Austria.,https://www.nachrichten.at/politik/ausland/?r=rss,7,Internacional,12,Austria,de,False,30 +950,Profil – International,Política internacional y análisis profundo.,https://www.profil.at/rss/international,7,Internacional,12,Austria,de,False,30 +944,Salzburger Nachrichten – Welt,Noticias internacionales.,https://www.sn.at/politik/weltpolitik/rss,7,Internacional,12,Austria,de,False,30 +948,Tiroler Tageszeitung – Ausland,Internacional y geopolítica.,https://www.tt.com/rss/ausland,7,Internacional,12,Austria,de,False,30 +952,Wiener Zeitung – International,Noticias y análisis internacionales.,https://www.wienerzeitung.at/_export/rss/international,7,Internacional,12,Austria,de,False,30 +910,BUND – Klima & Umwelt Österreich,Protección ecológica y clima.,https://bund.at/feed/,8,Medio Ambiente,12,Austria,de,True,0 +903,Der Standard – Umwelt,"Cambio climático, ecología y sostenibilidad.",https://www.derstandard.at/rss/umwelt,8,Medio Ambiente,12,Austria,de,False,30 +905,Die Presse – Umwelt,"Medio ambiente, energía y políticas verdes.",https://www.diepresse.com/rss/klima,8,Medio Ambiente,12,Austria,de,True,0 +911,Global 2000 – Umwelt & Klima,ONG ambientalista más activa del país.,https://www.global2000.at/rss,8,Medio Ambiente,12,Austria,de,False,30 +907,Kleine Zeitung – Umwelt,Noticias ambientales regionales y nacionales.,https://www.kleinezeitung.at/_services/rss/umwelt,8,Medio Ambiente,12,Austria,de,False,30 +906,Kurier – Umwelt,"Clima, naturaleza y energía.",https://kurier.at/umwelt/rss,8,Medio Ambiente,12,Austria,de,False,30 +904,ORF – Umwelt,Noticias ambientales y climáticas oficiales.,https://rss.orf.at/umwelt.xml,8,Medio Ambiente,12,Austria,de,False,30 +871,Tech & Nature – Green Tech,"Tecnología verde, energías renovables.",https://techandnature.com/feed/,8,Medio Ambiente,12,Austria,de,False,30 +908,Umweltbundesamt Österreich – News,Agencia Ambiental de Austria.,https://www.umweltbundesamt.at/rss,8,Medio Ambiente,12,Austria,de,False,30 +909,WWF Österreich – Umwelt News,"Conservación, biodiversidad y clima.",https://www.wwf.at/rss.xml,8,Medio Ambiente,12,Austria,de,False,30 +933,Der Standard – Meinung,"Opinión, columnas, análisis y comentarios.",https://www.derstandard.at/rss/meinung,10,Opinión,12,Austria,de,False,30 +935,Die Presse – Meinung,"Columnas, editoriales, análisis críticos.",https://www.diepresse.com/rss/meinung,10,Opinión,12,Austria,de,True,0 +949,Falter – Kommentar,Opinión crítica e investigación.,https://www.falter.at/rss/kommentar,10,Opinión,12,Austria,de,False,30 +939,Kleine Zeitung – Meinung,"Cartas al director, análisis, columnas.",https://www.kleinezeitung.at/_services/rss/meinung,10,Opinión,12,Austria,de,False,30 +937,Kurier – Meinung,"Opinión, columnas y análisis.",https://kurier.at/meinung/rss,10,Opinión,12,Austria,de,False,30 +942,ORF – Kommentare,Opinión y editoriales de ORF.,https://rss.orf.at/kommentare.xml,10,Opinión,12,Austria,de,False,30 +945,OÖ Nachrichten – Meinung,Opinión y análisis político-social.,https://www.nachrichten.at/meinung/?r=rss,10,Opinión,12,Austria,de,False,30 +943,Salzburger Nachrichten – Meinung,Opinión regional y nacional.,https://www.sn.at/meinung/rss,10,Opinión,12,Austria,de,False,30 +947,Tiroler Tageszeitung – Meinung,"Comentarios, columnas y análisis.",https://www.tt.com/rss/meinung,10,Opinión,12,Austria,de,False,30 +850,Außenministerium Österreich – Politik,Ministerio de Asuntos Exteriores: política exterior.,https://www.bmeia.gv.at/rss,11,Política,12,Austria,de,False,30 +849,Bundeskanzleramt – Politik News,Oficina del Canciller Federal – temas políticos y comunicados.,https://www.bundeskanzleramt.gv.at/feeds/rss.xml,11,Política,12,Austria,de,False,30 +841,Der Falter – Politik,Investigación política y reportajes.,https://www.falter.at/rss/politik,11,Política,12,Austria,de,False,30 +814,Der Standard – Politik,"Política nacional, europea e internacional.",https://www.derstandard.at/rss/politik,11,Política,12,Austria,de,False,30 +852,Die Grünen – Political News,Partido verde austríaco – actualidad política.,https://www.gruene.at/presse/rss,11,Política,12,Austria,de,True,0 +817,Die Presse – Inland Politik,"Política austríaca: gobierno, parlamento, partidos.",https://www.diepresse.com/rss/politik/inland,11,Política,12,Austria,de,False,30 +846,Die Wiener Zeitung – Politik,"Política e instituciones, uno de los diarios más antiguos del mundo.",https://www.wienerzeitung.at/_export/rss/die-wiener-zeitung/politik,11,Política,12,Austria,de,False,30 +830,Heute.at – Politik,Política nacional rápida y accesible.,https://www.heute.at/rss/politik,11,Política,12,Austria,de,False,30 +823,Kleine Zeitung – Politik,Política austriaca y europea.,https://www.kleinezeitung.at/_services/rss/politik,11,Política,12,Austria,de,False,30 +821,Kurier – Politik,Noticias políticas nacionales y regionales.,https://kurier.at/politik/rss,11,Política,12,Austria,de,False,30 +851,NEOS – Party News,Partido liberal austriaco NEOS – noticias y comunicados.,https://www.neos.eu/rss,11,Política,12,Austria,de,False,30 +845,NÖN – Politik,Noticias políticas desde Baja Austria.,https://www.noen.at/rss/politik,11,Política,12,Austria,de,False,30 +826,ORF News – Politik,Política oficial desde la televisión pública ORF.,https://rss.orf.at/politik.xml,11,Política,12,Austria,de,False,30 +844,Oberösterreichische Nachrichten – Politik,Política regional y nacional desde Linz.,https://www.nachrichten.at/politik/?r=rss,11,Política,12,Austria,de,False,30 +848,Parlament Österreich – News,Comunicación oficial del Parlamento austriaco.,https://www.parlament.gv.at/PAKT/RSS/,11,Política,12,Austria,de,False,30 +831,Profil – Politik,Revista política de análisis profundo.,https://www.profil.at/rss/politik,11,Política,12,Austria,de,False,30 +843,Salzburger Nachrichten – Politik,Política nacional desde Salzburgo.,https://www.sn.at/politik/rss,11,Política,12,Austria,de,False,30 +840,Tiroler Tageszeitung – Politik,Política desde Tirol y Austria.,https://www.tt.com/rss/politik,11,Política,12,Austria,de,False,30 +842,Volksblatt – Politik,Noticias políticas del Österreichisches Volksblatt.,https://volksblatt.at/feed/?cat=politik,11,Política,12,Austria,de,False,30 +847,Zeit im Bild (ORF ZIB) – Politik,Noticias políticas del noticiero más visto de Austria.,https://rss.orf.at/zib.xml,11,Política,12,Austria,de,False,30 +899,APA – Science Gesundheit,Noticias médicas de Austria Presse Agentur.,https://science.apa.at/wp-json/wp/v2/posts?per_page=20,12,Salud,12,Austria,de,False,30 +902,Allgemeines Krankenhaus Wien (AKH) – News,Uno de los mayores hospitales europeos.,https://www.akhwien.at/rss,12,Salud,12,Austria,de,False,5 +894,Der Standard – Gesundheit,"Salud, ciencia médica y bienestar.",https://www.derstandard.at/rss/gesundheit,12,Salud,12,Austria,de,True,0 +895,Die Presse – Gesundheit,Noticias médicas y salud pública.,https://www.diepresse.com/rss/gesundheit,12,Salud,12,Austria,de,False,30 +900,Gesundheit.gv.at – Offizielle Gesundheit,Portal oficial de salud del Gobierno de Austria.,https://www.gesundheit.gv.at/rss,12,Salud,12,Austria,de,True,0 +897,Kleine Zeitung – Gesundheit,Salud y temas médicos en Austria.,https://www.kleinezeitung.at/_services/rss/gesundheit,12,Salud,12,Austria,de,False,30 +896,Kurier – Gesundheit,"Salud, medicina y prevención.",https://kurier.at/gesundheit/rss,12,Salud,12,Austria,de,False,30 +864,MedUni Wien – Forschung & Medizin,Investigación médica oficial de Viena.,https://www.meduniwien.ac.at/web/en/feeds/rssnews/,12,Salud,12,Austria,en,False,30 +893,ORF – Gesundheit,"Salud pública, medicina, bienestar.",https://rss.orf.at/gesundheit.xml,12,Salud,12,Austria,de,False,30 +901,Österreichische Ärztekammer – News,Colegio Médico de Austria: salud y medicina.,https://www.aerztekammer.at/web/news;jsessionid=feed,12,Salud,12,Austria,de,False,30 +820,Der Kurier – News,Actualidad general de Austria.,https://kurier.at/rss,13,Sociedad,12,Austria,de,False,30 +873,Der Standard – Gesellschaft,"Reportajes sociales, igualdad, temas humanos.",https://www.derstandard.at/rss/gesellschaft,13,Sociedad,12,Austria,de,False,30 +813,Der Standard – Österreich,Noticias nacionales de Austria.,https://www.derstandard.at/rss,13,Sociedad,12,Austria,de,True,0 +875,Die Presse – Panorama,"Sociedad, reportajes, estilo de vida.",https://www.diepresse.com/rss/panorama,13,Sociedad,12,Austria,de,True,0 +829,Heute.at – News,Noticias populares y breves de Austria.,https://www.heute.at/rss,13,Sociedad,12,Austria,de,False,30 +879,Kleine Zeitung – Society,"Temas sociales, vida cotidiana, comunidad.",https://www.kleinezeitung.at/_services/rss/chronik,13,Sociedad,12,Austria,de,False,30 +822,Kleine Zeitung – Österreich,Noticias regionales y nacionales.,https://www.kleinezeitung.at/_services/rss/at,13,Sociedad,12,Austria,de,False,30 +877,Kurier – Chronik,"Sociedad, sucesos y temas humanos.",https://kurier.at/chronik/rss,13,Sociedad,12,Austria,de,False,30 +825,ORF News – Top News,Noticias oficiales de la radiotelevisión pública ORF.,https://rss.orf.at/news.xml,13,Sociedad,12,Austria,de,True,0 +881,ORF – Society,Noticias sociales de Austria.,https://rss.orf.at/chronik.xml,13,Sociedad,12,Austria,de,False,30 +886,OÖ Nachrichten – Society,"Reportajes, sociedad y sucesos.",https://www.nachrichten.at/oberoesterreich/?r=rss,13,Sociedad,12,Austria,de,False,30 +884,Salzburger Nachrichten – Society,"Sociedad, eventos y reportajes.",https://www.sn.at/panorama/rss,13,Sociedad,12,Austria,de,False,30 +888,Tiroler Tageszeitung – Gesellschaft,"Sociedad, vida local, comunidad.",https://www.tt.com/rss/chronik,13,Sociedad,12,Austria,de,False,30 +867,AIT – Austrian Institute of Technology,"Investigación aplicada, movilidad, energía, IA.",https://www.ait.ac.at/news-events/rss,14,Tecnología,12,Austria,en,False,30 +870,Ars Electronica – Digital Art & Tech,Festival global de arte tecnológico con sede en Linz.,https://ars.electronica.art/feed/,14,Tecnología,12,Austria,en,True,0 +859,Der Brutkasten – Technologie,"Startups, innovación y tecnología.",https://brutkasten.com/feed,14,Tecnología,12,Austria,de,True,0 +858,Futurezone.at – Technologie,Medio tecnológico más importante de Austria.,https://futurezone.at/feed,14,Tecnología,12,Austria,de,False,30 +866,JKU Linz – Research & Tech,"Tecnología, digitalización y ciencia en Linz.",https://www.jku.at/newsroom/feed/,14,Tecnología,12,Austria,de,False,30 +862,TU Wien – Research & Innovation,"Tecnología, ingeniería e investigación.",https://www.tuwien.at/en/tu-wien/news/rss,14,Tecnología,12,Austria,en,False,30 +860,Trending Topics – Tech,"Startups, IA, blockchain y tech en Austria.",https://www.trendingtopics.eu/feed/,14,Tecnología,12,Austria,en,False,30 +1029,AzVision – Science,"Ciencia, educación e investigación.",https://en.azvision.az/rss.php?cat=science,1,Ciencia,13,Azerbaiyán,en,False,30 +1027,AzerNews – Science,Investigación y descubrimientos.,https://www.azernews.az/rss.php?cat=science,1,Ciencia,13,Azerbaiyán,en,False,30 +1030,Day.az – Science (RU),Ciencia e innovación (ruso).,https://news.day.az/science/rss,1,Ciencia,13,Azerbaiyán,ru,False,30 +1036,APA – Culture,"Cultura, música, arte, literatura.",https://apa.az/en/culture/rss,2,Cultura,13,Azerbaiyán,en,False,30 +1052,AzCulture – Arts & Heritage,"Arte, museos, patrimonio cultural.",https://azculture.org/feed/,2,Cultura,13,Azerbaiyán,en,False,30 +1051,AzerNews – Culture,Cultura en inglés.,https://www.azernews.az/rss.php?cat=culture,2,Cultura,13,Azerbaiyán,en,False,30 +1040,Azertag – Culture (EN),"Artes, patrimonio, literatura, museos.",https://azertag.az/en/rss/culture,2,Cultura,13,Azerbaiyán,en,False,30 +1041,Azertag – Mədəniyyət (AZ),Cultura en idioma azerí.,https://azertag.az/rss/medeniyyet,2,Cultura,13,Azerbaiyán,az,False,30 +1050,Day.az – Culture (RU),Cultura y entretenimiento (ruso).,https://news.day.az/culture/rss,2,Cultura,13,Azerbaiyán,ru,False,30 +1046,Modern.az – Mədəniyyət,Cultura y eventos culturales.,https://modern.az/rss/medeniyyet,2,Cultura,13,Azerbaiyán,az,True,0 +1048,News.az – Culture,"Cultura, arte y patrimonio.",https://news.az/culture/rss,2,Cultura,13,Azerbaiyán,en,False,30 +1044,Oxu.az – Mədəniyyət,Cultura y arte en azerí.,https://oxu.az/culture/rss,2,Cultura,13,Azerbaiyán,az,False,30 +1038,Report.az – Culture,"Cultura, museos y patrimonio.",https://report.az/en/culture/rss/,2,Cultura,13,Azerbaiyán,en,False,30 +1034,Trend – Culture,"Cultura, arte, patrimonio y eventos.",https://en.trend.az/culture/rss,2,Cultura,13,Azerbaiyán,en,False,30 +961,APA – Sports,"Fútbol, lucha, boxeo, deportes locales.",https://apa.az/en/sports/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1061,AzerNews – Sports,"Fútbol, federaciones, resultados.",https://www.azernews.az/rss.php?cat=sports,3,Deportes,13,Azerbaiyán,en,False,30 +1071,Azerbaijan Boxing Federation – News,Boxeo profesional y amateur.,https://www.fbu.az/en/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1072,Azerbaijan Gymnastics Federation – News,Gimnasia rítmica y artística.,https://agf.az/en/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1070,Azerbaijan Wrestling Federation – News,Federación de lucha: deporte nacional.,https://awf.az/en/rss,3,Deportes,13,Azerbaiyán,en,True,0 +1056,Azertag – Sports (EN),Deportes oficiales: federaciones y resultados.,https://azertag.az/en/rss/sports,3,Deportes,13,Azerbaiyán,en,False,30 +1057,Azertag – İdman (AZ),Deportes oficiales en azerí.,https://azertag.az/rss/idman,3,Deportes,13,Azerbaiyán,az,False,30 +1063,Caliber.az – Sports,Deportes y competiciones internacionales.,https://caliber.az/en/rss/sports,3,Deportes,13,Azerbaiyán,en,False,30 +1062,Day.az – Sports (RU),"Deportes en ruso: fútbol, lucha, boxeo.",https://news.day.az/sport/rss,3,Deportes,13,Azerbaiyán,ru,False,30 +1059,Modern.az – İdman,Noticias deportivas en azerí.,https://modern.az/rss/idman,3,Deportes,13,Azerbaiyán,az,True,0 +1065,Neftçi PFK – Football News,Club histórico de Bakú.,https://neftchipfk.com/en/rss,3,Deportes,13,Azerbaiyán,en,True,0 +1060,News.az – Sports,Deportes nacionales e internacionales.,https://news.az/sports/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1058,Oxu.az – İdman,Portal líder: deportes en azerí.,https://oxu.az/sport/rss,3,Deportes,13,Azerbaiyán,az,False,30 +1066,Qarabağ FK – News,Equipo de la Premier League de Azerbaiyán.,https://qarabagh.com/en/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1055,Report.az – Sports,Noticias deportivas en inglés.,https://report.az/en/sports/rss/,3,Deportes,13,Azerbaiyán,en,False,30 +1064,Revan FC – News,Noticias del club de fútbol Revan FC.,https://revanfc.az/feed/,3,Deportes,13,Azerbaiyán,az,False,30 +1068,Sumqayıt FK – News,Noticias oficiales del club.,https://sfc.sumqayit.az/en/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1067,Səbail FK – News,Equipo de Premier League.,https://sabailfc.az/feed/,3,Deportes,13,Azerbaiyán,az,False,30 +1053,Trend – Sports,Deportes nacionales e internacionales.,https://en.trend.az/sports/rss,3,Deportes,13,Azerbaiyán,en,False,30 +1069,Zira FK – News,Club de la Premier League.,https://www.fczire.az/rss,3,Deportes,13,Azerbaiyán,az,False,30 +960,APA – Economy,Economía nacional y mercados.,https://apa.az/en/economy/rss,4,Economía,13,Azerbaiyán,en,False,30 +997,APA – Energy,Noticias energéticas y sector hidrocarburos.,https://apa.az/en/energy/rss,4,Economía,13,Azerbaiyán,en,False,30 +969,AzVision – Economics,Economía nacional e internacional.,https://en.azvision.az/rss.php?cat=economy,4,Economía,13,Azerbaiyán,en,False,30 +1012,Finport.az – Market & Finance,"Mercados financieros, banca e inversión.",https://finport.az/rss,4,Economía,13,Azerbaiyán,az,False,30 +1009,Modern.az – İqtisadiyyat,Noticias económicas locales.,https://modern.az/rss/iqtisadiyyat,4,Economía,13,Azerbaiyán,az,True,0 +1011,Oxu.az – İqtisadiyyat,Portal líder en azerí: economía y negocios.,https://oxu.az/economy/rss,4,Economía,13,Azerbaiyán,az,False,30 +1008,Qafqazinfo – İqtisadiyyat (AZ),Economía en idioma azerí.,https://qafqazinfo.az/rss/iqtisadiyyat,4,Economía,13,Azerbaiyán,az,False,30 +965,Report.az – Economy,"Economía nacional, inversión y negocios.",https://report.az/en/finance/rss/,4,Economía,13,Azerbaiyán,en,False,30 +966,Report.az – Energy,"Petróleo, gas, SOCAR, energía.",https://report.az/en/energy/rss/,4,Economía,13,Azerbaiyán,en,False,30 +1007,State Oil Fund of Azerbaijan (SOFAZ) – News,Fondo soberano de Azerbaiyán.,https://www.oilfund.az/en/rss,4,Economía,13,Azerbaiyán,en,True,0 +955,Trend – Economy,"Economía nacional, empresas y mercados.",https://en.trend.az/business/economy/rss,4,Economía,13,Azerbaiyán,en,False,30 +994,Trend – Energy,"Petróleo, gas, renovables y energía.",https://en.trend.az/business/energy/rss,4,Economía,13,Azerbaiyán,en,False,30 +995,Trend – Oil & Gas,Noticias del sector petrolero y gasífero.,https://en.trend.az/business/energy/oil-gas/rss,4,Economía,13,Azerbaiyán,en,False,30 +1096,APA – World,Noticias globales de APA News.,https://apa.az/en/world/rss,7,Internacional,13,Azerbaiyán,en,False,30 +1100,AzerNews – World,"Internacional, diplomacia y conflictos.",https://www.azernews.az/rss.php?cat=world,7,Internacional,13,Azerbaiyán,en,False,30 +1098,Azertag – World (EN),Internacional desde agencia estatal.,https://azertag.az/en/rss/world,7,Internacional,13,Azerbaiyán,en,False,30 +972,News.az – World,Internacional en inglés.,https://news.az/world/rss,7,Internacional,13,Azerbaiyán,en,False,30 +1097,Report.az – World,Internacional y asuntos globales.,https://report.az/en/foreign-politics/rss/,7,Internacional,13,Azerbaiyán,en,False,30 +1094,Trend – Iran News,Cobertura internacional enfocada en Irán.,https://en.trend.az/iran/rss,7,Internacional,13,Azerbaiyán,en,False,30 +1095,Trend – Russia & CIS,Noticias internacionales del espacio postsoviético.,https://en.trend.az/region/russia/rss,7,Internacional,13,Azerbaiyán,en,False,30 +1093,Trend – World News,Internacional y geopolítica.,https://en.trend.az/world/rss,7,Internacional,13,Azerbaiyán,en,False,30 +959,APA – Politics,Política nacional e internacional.,https://apa.az/en/politics/rss,11,Política,13,Azerbaiyán,en,False,30 +968,AzVision – Politics,Política en inglés desde Bakú.,https://en.azvision.az/rss.php?cat=politics,11,Política,13,Azerbaiyán,en,False,30 +980,AzerNews – Politics,Política nacional e internacional.,https://www.azernews.az/rss.php?cat=politics,11,Política,13,Azerbaiyán,en,False,30 +984,Azertag – Azerbaijan Politics (AZ),Política nacional en idioma azerí.,https://azertag.az/rss/siyaset,11,Política,13,Azerbaiyán,az,False,30 +983,Azertag – Official State News – Politics,Agencia oficial estatal: política y gobierno.,https://azertag.az/en/rss/politics,11,Política,13,Azerbaiyán,en,False,30 +992,Azpolitika.info – Politics,Portal especializado en análisis político (AZ).,https://azpolitika.info/rss,11,Política,13,Azerbaiyán,az,True,0 +991,Caliber.az – Geopolitics,Geopolítica y temas de seguridad.,https://caliber.az/en/rss/geopolitics,11,Política,13,Azerbaiyán,en,False,30 +981,Caliber.az – Politics,"Geopolítica, seguridad, análisis político.",https://caliber.az/en/rss/politics,11,Política,13,Azerbaiyán,en,False,30 +985,Day.az – Politik (RU),Portal ruso local: política.,https://news.day.az/politics/rss,11,Política,13,Azerbaiyán,ru,False,30 +988,Ministry of Foreign Affairs of Azerbaijan – News,Comunicados y diplomacia oficial.,https://mfa.gov.az/en/rss,11,Política,13,Azerbaiyán,en,False,30 +986,Modern.az – Siyasət,Política nacional en azerí.,https://modern.az/rss/siyaset,11,Política,13,Azerbaiyán,az,True,0 +979,News.az – Politics,"Gobierno, elecciones, diplomacia.",https://news.az/politics/rss,11,Política,13,Azerbaiyán,en,False,30 +982,Oxu.az – Siyasət,Portal líder en azerí: política nacional.,https://oxu.az/politics/rss,11,Política,13,Azerbaiyán,az,False,30 +990,Parliament of Azerbaijan – Milli Majlis – News,Noticias oficiales del Parlamento.,https://meclis.gov.az/en/news/rss,11,Política,13,Azerbaiyán,en,True,0 +989,President.az – Official News,Presidencia de Azerbaiyán: comunicados oficiales.,https://president.az/en/rss,11,Política,13,Azerbaiyán,en,True,0 +987,Qafqazinfo – Siyasət,"Política, gobierno, decisiones oficiales.",https://qafqazinfo.az/rss/siyaset,11,Política,13,Azerbaiyán,az,False,30 +964,Report.az – Politics,Noticias políticas del país.,https://report.az/en/politics/rss/,11,Política,13,Azerbaiyán,en,False,30 +974,Trend – Karabakh Politics,Conflicto y situación política en Karabaj.,https://en.trend.az/azerbaijan/karabakh/rss,11,Política,13,Azerbaiyán,en,False,30 +954,Trend – Politics,"Política nacional, gobierno, parlamento.",https://en.trend.az/politics/rss,11,Política,13,Azerbaiyán,en,False,30 +975,Trend – South Caucasus Politics,Cáucaso Sur y geopolítica regional.,https://en.trend.az/region/caucasus/rss,11,Política,13,Azerbaiyán,en,False,30 +958,APA News – General,Agencia APA: noticias generales.,https://apa.az/en/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +962,APA – Social,"Sociedad, educación, actualidad social.",https://apa.az/en/social/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +967,AzVision – News,Noticias generales en inglés.,https://en.azvision.az/rss.php,13,Sociedad,13,Azerbaiyán,en,False,30 +970,AzerNews – General,El diario en inglés más antiguo de Azerbaiyán.,https://www.azernews.az/rss.php,13,Sociedad,13,Azerbaiyán,en,False,30 +1124,AzerNews – Lifestyle,"Cultura urbana, tendencias y viajes.",https://www.azernews.az/rss.php?cat=lifestyle,13,Sociedad,13,Azerbaiyán,en,False,30 +1042,Azertag – Cəmiyyət (AZ),Sociedad en azerí.,https://azertag.az/rss/cemiyyet,13,Sociedad,13,Azerbaiyán,az,False,30 +1120,Azertag – Life & Culture,"Lifestyle, eventos y sociedad.",https://azertag.az/en/rss/life,13,Sociedad,13,Azerbaiyán,en,False,30 +1039,Azertag – Society (EN),Noticias sociales oficiales.,https://azertag.az/en/rss/society,13,Sociedad,13,Azerbaiyán,en,False,30 +1125,Baku Magazine – Culture & Lifestyle,"Arte, sociedad, alta cultura de Bakú.",https://bakumagazine.com/feed/,13,Sociedad,13,Azerbaiyán,en,False,30 +1127,Caliber.az – Culture & Lifestyle,Estilo de vida y cultura moderna.,https://caliber.az/en/rss/lifestyle,13,Sociedad,13,Azerbaiyán,en,False,30 +1049,Day.az – Society (RU),"Sociedad, educación, temas públicos.",https://news.day.az/society/rss,13,Sociedad,13,Azerbaiyán,ru,False,30 +1045,Modern.az – Cəmiyyət,"Sociedad, vida pública, educación.",https://modern.az/rss/cemiyyet,13,Sociedad,13,Azerbaiyán,az,True,0 +1121,Modern.az – Life,Estilo de vida y sociedad.,https://modern.az/rss/life,13,Sociedad,13,Azerbaiyán,az,True,0 +971,News.az – General,Noticias generales de Azerbaijan.,https://news.az/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +1123,News.az – Lifestyle,Lifestyle en inglés.,https://news.az/lifestyle/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +1047,News.az – Society,Sociedad y actualidad social en inglés.,https://news.az/society/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +1043,Oxu.az – Cəmiyyət,Noticias sociales en azerí.,https://oxu.az/society/rss,13,Sociedad,13,Azerbaiyán,az,False,30 +1122,Oxu.az – Life,"Estilo de vida, ocio, consejos.",https://oxu.az/life/rss,13,Sociedad,13,Azerbaiyán,az,False,30 +963,Report.az – All News,Noticias generales en inglés.,https://report.az/en/rss/,13,Sociedad,13,Azerbaiyán,en,False,30 +1117,Report.az – Lifestyle,"Cultura urbana, estilo de vida y eventos.",https://report.az/en/lifestyle/rss/,13,Sociedad,13,Azerbaiyán,en,False,30 +1037,Report.az – Society,Sociedad y vida pública.,https://report.az/en/society/rss/,13,Sociedad,13,Azerbaiyán,en,False,30 +953,Trend News Agency – Azerbaijan,Noticias nacionales en inglés.,https://en.trend.az/rss,13,Sociedad,13,Azerbaiyán,en,True,0 +957,Trend – Society,Noticias sociales y comunitarias en inglés.,https://en.trend.az/society/rss,13,Sociedad,13,Azerbaiyán,en,False,30 +1028,AzVision – Tech,Noticias tecnológicas desde Bakú.,https://en.azvision.az/rss.php?cat=tech,14,Tecnología,13,Azerbaiyán,en,False,30 +1026,AzerNews – Tech,"Nuevas tecnologías, startups y TI.",https://www.azernews.az/rss.php?cat=hightech,14,Tecnología,13,Azerbaiyán,en,False,30 +1031,Day.az – Tech (RU),Tecnología e IT (ruso).,https://news.day.az/tech/rss,14,Tecnología,13,Azerbaiyán,ru,False,30 +1032,ITstep Azerbaijan – Tech Education,Escuela tecnológica: programación y educación IT.,https://azerbaijan.itstep.org/feed,14,Tecnología,13,Azerbaiyán,az,False,30 +956,Trend – Tech,"Tecnología, telecomunicaciones y startups.",https://en.trend.az/tech/rss,14,Tecnología,13,Azerbaiyán,en,False,30 +1116,APA – Travel,"Turismo, viajes, eventos culturales.",https://apa.az/en/tourism/rss,15,Viajes,13,Azerbaiyán,en,False,30 +1118,Azertag – Tourism (EN),Turismo oficial y promoción cultural.,https://azertag.az/en/rss/tourism,15,Viajes,13,Azerbaiyán,en,False,30 +1119,Azertag – Turizm (AZ),Turismo en azerí.,https://azertag.az/rss/turizm,15,Viajes,13,Azerbaiyán,az,False,30 +1126,Visit Azerbaijan – Tourism Blog,Destinos turísticos y guías de viaje (inglés).,https://azerbaijan.travel/feed,15,Viajes,13,Azerbaiyán,en,False,30 +1137,EyeWitness News – Sports,Deportes nacionales.,https://ewnews.com/category/sports/feed,3,Deportes,14,Bahamas,en,True,0 +1141,Our News Bahamas – Sports,Deportes nacionales.,https://ournews.bs/category/sports/feed/,3,Deportes,14,Bahamas,en,False,30 +1130,The Nassau Guardian – Sports,Deportes bahameños.,https://thenassauguardian.com/category/sports/feed/,3,Deportes,14,Bahamas,en,False,30 +1133,The Tribune 242 – Sports,Deportes locales e internacionales.,https://www.tribune242.com/sports/rss/,3,Deportes,14,Bahamas,en,False,30 +1136,EyeWitness News – Business,Negocios y economía.,https://ewnews.com/category/business/feed,4,Economía,14,Bahamas,en,True,0 +1140,Our News Bahamas – Business,"Economía, mercado laboral, inversión.",https://ournews.bs/category/business/feed/,4,Economía,14,Bahamas,en,False,30 +1129,The Nassau Guardian – Business,Economía nacional y regional.,https://thenassauguardian.com/category/business/feed/,4,Economía,14,Bahamas,en,False,30 +1132,The Tribune 242 – Business,"Economía, bancos y empresas.",https://www.tribune242.com/business/rss/,4,Economía,14,Bahamas,en,False,30 +1144,Bahamas Press – Opinion,Opinión y análisis político-social.,https://bahamaspress.com/category/editorial/feed/,10,Opinión,14,Bahamas,en,True,0 +1154,Bahamas Local – Government News,Política y anuncios oficiales (agregado).,https://www.bahamaslocal.com/news/government_rss.xml,11,Política,14,Bahamas,en,False,30 +1153,Bahamas Press – Government,Cobertura de actividades gubernamentales.,https://bahamaspress.com/tag/government/feed/,11,Política,14,Bahamas,en,True,0 +1143,Bahamas Press – News,Blog político muy conocido en Bahamas.,https://bahamaspress.com/feed/,11,Política,14,Bahamas,en,True,0 +1152,Bahamas Press – Politics,El blog político más influyente de Bahamas.,https://bahamaspress.com/category/politics/feed/,11,Política,14,Bahamas,en,True,0 +1155,Bahamas Weekly – Politics,Notas políticas y comunicados oficiales.,https://www.thebahamasweekly.com/rss.php?cat=Politics,11,Política,14,Bahamas,en,False,30 +1159,Caribbean National Weekly – Bahamas Politics,Cobertura regional centrada en política bahameña.,https://www.cnwnetwork.com/tag/bahamas-politics/feed/,11,Política,14,Bahamas,en,False,30 +1161,Caribbean News – Bahamas Politics,Noticias políticas del Caribe con RSS válido.,https://www.caribbeannewsglobal.com/category/bahamas/feed/,11,Política,14,Bahamas,en,True,0 +1135,EyeWitness News – Politics,"Política nacional, gobierno y parlamento.",https://ewnews.com/category/politics/feed,11,Política,14,Bahamas,en,True,0 +1162,International Investment – Bahamas Policy,Política económica y regulatoria (Bahamas).,https://internationalinvestment.net/tag/bahamas/feed/,11,Política,14,Bahamas,en,False,30 +1156,Ministry of Finance Bahamas – Press Releases,Comunicados gubernamentales sobre política económica.,https://www.bahamas.gov.bs/wps/portal/public/rss/pressreleases,11,Política,14,Bahamas,en,False,30 +1158,Ministry of Foreign Affairs Bahamas – News,Política exterior y diplomacia.,https://mofa.gov.bs/feed/,11,Política,14,Bahamas,en,False,30 +1157,Office of the Prime Minister – News,Comunicados oficiales del Primer Ministro.,https://opm.gov.bs/feed/,11,Política,14,Bahamas,en,True,0 +1139,Our News Bahamas – Politics,Noticias políticas desde la TV nacional.,https://ournews.bs/category/politics/feed/,11,Política,14,Bahamas,en,False,30 +1160,Repeating Islands – Bahamas Politics,Política y sociedad caribeña (con sección Bahamas).,https://repeatingislands.com/category/bahamas-politics/feed/,11,Política,14,Bahamas,en,True,0 +1150,The Nassau Guardian – Politics,"Política nacional, partidos y gobierno.",https://thenassauguardian.com/category/politics/feed/,11,Política,14,Bahamas,en,False,30 +1149,The Tribune 242 – Politics,Noticias políticas del principal diario del país.,https://www.tribune242.com/politics/rss/,11,Política,14,Bahamas,en,False,30 +1142,Bahamas Local – News,Agregador de noticias locales.,https://www.bahamaslocal.com/news/news_rss.xml,13,Sociedad,14,Bahamas,en,False,30 +1146,Bahamas Weekly – News,Noticias nacionales y comunicados oficiales.,https://www.thebahamasweekly.com/rss.php,13,Sociedad,14,Bahamas,en,False,30 +1134,EyeWitness News Bahamas – General,Medio digital popular de Bahamas.,https://ewnews.com/feed,13,Sociedad,14,Bahamas,en,True,0 +1138,Our News Bahamas – General,Televisión y noticias nacionales.,https://ournews.bs/feed/,13,Sociedad,14,Bahamas,en,False,30 +1145,The Eleutheran – Local News,Noticias locales de Eleuthera.,https://eleutheranews.com/feed/,13,Sociedad,14,Bahamas,en,False,30 +1128,The Nassau Guardian – News,El diario más antiguo de Bahamas.,https://thenassauguardian.com/feed/,13,Sociedad,14,Bahamas,en,False,30 +1131,The Tribune 242 – News,Noticias locales y nacionales.,https://www.tribune242.com/news/rss/,13,Sociedad,14,Bahamas,en,False,30 +1147,Bahamas Ministry of Tourism – Travel Updates,Información turística oficial.,https://www.bahamas.com/rss.xml,15,Viajes,14,Bahamas,en,False,30 +1228,BDNews24 – Science,Noticias científicas en inglés.,https://bdnews24.com/science/rss,1,Ciencia,15,Bangladés,en,False,30 +1238,Bangla Tribune – Science,Ciencia en idioma bengalí.,https://banglatribune.com/feed/science,1,Ciencia,15,Bangladés,bn,False,30 +1226,Dhaka Tribune – Science,Ciencia y descubrimientos.,https://www.dhakatribune.com/science/feed,1,Ciencia,15,Bangladés,en,False,30 +1170,New Age BD – Science & Tech,"Ciencia, tecnología, investigación.",https://www.newagebd.net/section/science-technology/rss,1,Ciencia,15,Bangladés,en,False,30 +1236,Prothom Alo – Science,"Ciencia, curiosidades, física, salud (bn).",https://www.prothomalo.com/science/feed,1,Ciencia,15,Bangladés,bn,False,30 +1231,The Business Standard – Science,"Ciencia, investigación y salud.",https://www.tbsnews.net/science/feed,1,Ciencia,15,Bangladés,en,False,30 +1224,The Daily Star – Science & Health,"Ciencia, salud, investigación.",https://www.thedailystar.net/health/rss,1,Ciencia,15,Bangladés,en,False,30 +1234,UNB News – Science,Ciencia y medio ambiente.,https://unb.com.bd/category/35/Environment/rss,1,Ciencia,15,Bangladés,en,False,30 +1256,Bangla Tribune – Culture,"Arte, eventos y medios culturales.",https://banglatribune.com/feed/entertainment,2,Cultura,15,Bangladés,bn,True,0 +1247,Dhaka Tribune – Arts & Culture,"Cultura, cine, teatro y arte.",https://www.dhakatribune.com/showtime/feed,2,Cultura,15,Bangladés,en,False,30 +1262,Jago News – Entertainment,"Cultura, cine, celebridades (bn).",https://www.jagonews24.com/entertainment/rss.xml,2,Cultura,15,Bangladés,bn,False,30 +1258,Kaler Kantho – Entertainment,Cultura pop y cine (bn).,https://www.kalerkantho.com/rss/online/entertainment,2,Cultura,15,Bangladés,bn,False,30 +1249,New Age BD – Arts,"Arte, literatura y cine.",https://www.newagebd.net/section/arts/rss,2,Cultura,15,Bangladés,en,False,30 +1254,Prothom Alo – Culture,"Cultura bengalí: cine, música, literatura.",https://www.prothomalo.com/entertainment/feed,2,Cultura,15,Bangladés,bn,False,30 +1260,Samakal – Entertainment,"Cine, música y cultura bengalí.",https://samakal.com/entertainment/rss.xml,2,Cultura,15,Bangladés,bn,False,30 +1252,The Business Standard – Arts & Culture,"Cine, fotografía, literatura.",https://www.tbsnews.net/splash/feed,2,Cultura,15,Bangladés,en,False,30 +1244,The Daily Star – Culture,"Arte, literatura, cine, música.",https://www.thedailystar.net/arts-entertainment/rss,2,Cultura,15,Bangladés,en,False,30 +1177,BDNews24 – Cricket,Cricket nacional y BPL League.,https://bdnews24.com/cricket/rss,3,Deportes,15,Bangladés,en,False,30 +1267,BDNews24 – Sports,Portales deportivos en inglés.,https://bdnews24.com/sport/rss,3,Deportes,15,Bangladés,en,False,30 +1276,Bangla Tribune (BN) – Sports,Deportes locales y globales.,https://banglatribune.com/feed/sports,3,Deportes,15,Bangladés,bn,False,30 +1280,Cricket Archive – Bangladesh,"Historial, resultados y estadísticas.",https://cricketarchive.com/rss.xml,3,Deportes,15,Bangladés,en,False,30 +1266,Dhaka Tribune – Cricket,Cricket nacional e internacional.,https://www.dhakatribune.com/sport/cricket/feed,3,Deportes,15,Bangladés,en,False,30 +1173,Dhaka Tribune – Sports,"Cricket, fútbol, hockey y más.",https://www.dhakatribune.com/sport/feed,3,Deportes,15,Bangladés,en,False,30 +1282,ESPNcricinfo – Bangladesh,Cobertura internacional del cricket de Bangladés.,https://www.espncricinfo.com/rss/content/story/feeds/bangladesh.xml,3,Deportes,15,Bangladés,en,False,30 +1279,Jago News (BN) – Sports,"Cricket, fútbol y deportes.",https://www.jagonews24.com/sports/rss.xml,3,Deportes,15,Bangladés,bn,False,30 +1277,Kaler Kantho (BN) – Sports,Deportes nacionales (bn).,https://www.kalerkantho.com/rss/online/sports,3,Deportes,15,Bangladés,bn,False,30 +1270,New Age BD – Cricket,Cricket profesional y amateur.,https://www.newagebd.net/section/cricket/rss,3,Deportes,15,Bangladés,en,False,30 +1169,New Age BD – Sports,Deportes bangladesíes y globales.,https://www.newagebd.net/section/sport/rss,3,Deportes,15,Bangladés,en,False,30 +1180,Prothom Alo (BN) – Sports,Deportes en bengalí.,https://www.prothomalo.com/sports/feed,3,Deportes,15,Bangladés,bn,False,30 +1275,Prothom Alo – Cricket,Cricket (bn).,https://www.prothomalo.com/sports/cricket/feed,3,Deportes,15,Bangladés,bn,False,30 +1278,Samakal (BN) – Sports,Noticias deportivas en bengalí.,https://samakal.com/sports/rss.xml,3,Deportes,15,Bangladés,bn,False,30 +1272,The Business Standard – Cricket,"Cricket, análisis y selecciones.",https://www.tbsnews.net/sports/cricket/feed,3,Deportes,15,Bangladés,en,False,30 +1271,The Business Standard – Sports,Deporte profesional y ligas nacionales.,https://www.tbsnews.net/sports/feed,3,Deportes,15,Bangladés,en,False,30 +1264,The Daily Star – Cricket,"Cricket: ligas, selecciones y análisis.",https://www.thedailystar.net/sports/cricket/rss,3,Deportes,15,Bangladés,en,False,30 +1165,The Daily Star – Sports,"Cricket, fútbol y deportes nacionales.",https://www.thedailystar.net/sports/rss,3,Deportes,15,Bangladés,en,False,30 +1281,Tiger Cricket – BCB Official,Noticias oficiales del Bangladesh Cricket Board.,https://tigercricket.com.bd/feed/,3,Deportes,15,Bangladés,en,False,30 +1273,UNB News – Sports,Deportes nacionales en inglés.,https://unb.com.bd/category/17/Sports/rss,3,Deportes,15,Bangladés,en,False,30 +1208,BDNews24 – Apparel & RMG,"Sector textil, fábricas, exportación.",https://bdnews24.com/economy/rmg/rss,4,Economía,15,Bangladés,en,False,30 +1176,BDNews24 – Business,"Economía, comercio, industria textil, banca.",https://bdnews24.com/business/rss,4,Economía,15,Bangladés,en,False,30 +1172,Dhaka Tribune – Business,"Empresas, finanzas y exportación textil.",https://www.dhakatribune.com/business/feed,4,Economía,15,Bangladés,en,False,30 +1206,Dhaka Tribune – Economy,"Mercados, banca e indicadores económicos.",https://www.dhakatribune.com/business/economy/feed,4,Economía,15,Bangladés,en,False,30 +1219,Ittefaq (BN) – Business,Negocios y mercados (bn).,https://www.ittefaq.com.bd/business/rss.xml,4,Economía,15,Bangladés,bn,False,30 +1222,Jago News (BN) – Business,"Comercio, banca, exportaciones (bn).",https://www.jagonews24.com/business/rss.xml,4,Economía,15,Bangladés,bn,False,30 +1220,Kaler Kantho (BN) – Economy,"Industria, comercio, empleo (bn).",https://www.kalerkantho.com/rss/online/economy,4,Economía,15,Bangladés,bn,False,30 +1168,New Age BD – Business,"Economía, empresas, sectores productivos.",https://www.newagebd.net/section/business/rss,4,Economía,15,Bangladés,en,False,30 +1210,New Age BD – RMG/Factories,"Sector textil, fábricas y mercados.",https://www.newagebd.net/section/rmg/rss,4,Economía,15,Bangladés,en,False,30 +1181,Prothom Alo (BN) – Business,Economía nacional en bengalí.,https://www.prothomalo.com/business/feed,4,Economía,15,Bangladés,bn,False,30 +1218,Prothom Alo – RMG/Export,"Sector textil, exportación (bn).",https://www.prothomalo.com/economy/rmg/feed,4,Economía,15,Bangladés,bn,False,30 +1221,Samakal (BN) – Business,Noticias económicas en bengalí.,https://samakal.com/economics/rss.xml,4,Economía,15,Bangladés,bn,False,30 +1182,The Business Standard – Economy,"Macroeconomía, exportación, industria.",https://www.tbsnews.net/economy/feed,4,Economía,15,Bangladés,en,False,30 +1212,The Business Standard – RMG,Industria textil y exportación.,https://www.tbsnews.net/economy/rmg/feed,4,Economía,15,Bangladés,en,False,30 +1164,The Daily Star – Business,"Negocios, empresas, banca, exportaciones.",https://www.thedailystar.net/business/rss,4,Economía,15,Bangladés,en,False,30 +1204,The Daily Star – Economy,"Economía nacional, inflación y mercados.",https://www.thedailystar.net/economy/rss,4,Economía,15,Bangladés,en,False,30 +1213,The Financial Express – Economy,"Economía, bolsa, inversión, comercio.",https://thefinancialexpress.com.bd/economy/rss,4,Economía,15,Bangladés,en,False,30 +1214,The Financial Express – Trade,Comercio exterior y exportaciones.,https://thefinancialexpress.com.bd/trade/rss,4,Economía,15,Bangladés,en,False,30 +1215,UNB News – Business,Economía en inglés de United News BD.,https://unb.com.bd/category/19/Business/rss,4,Economía,15,Bangladés,en,False,30 +1216,UNB News – Industry,"Industria, fábricas, energía.",https://unb.com.bd/category/32/Industry/rss,4,Economía,15,Bangladés,en,False,30 +1200,BDNews24 – World,Noticias globales y regionales.,https://bdnews24.com/world/rss,7,Internacional,15,Bangladés,en,False,30 +1299,Bangla Tribune (BN) – World,Internacional en bengalí.,https://banglatribune.com/feed/world,7,Internacional,15,Bangladés,bn,False,30 +1286,Dhaka Tribune – World,Internacional en inglés.,https://www.dhakatribune.com/world/feed,7,Internacional,15,Bangladés,en,False,30 +1290,New Age BD – International,Noticias internacionales críticas.,https://www.newagebd.net/section/world/rss,7,Internacional,15,Bangladés,en,False,30 +1297,Prothom Alo (BN) – International,Internacional en bengalí.,https://www.prothomalo.com/international/feed,7,Internacional,15,Bangladés,bn,False,30 +1292,The Business Standard – World,Internacional económico y político.,https://www.tbsnews.net/world/feed,7,Internacional,15,Bangladés,en,False,30 +1284,The Daily Star – Asia,Asia Sur y sudeste asiático.,https://www.thedailystar.net/asia/rss,7,Internacional,15,Bangladés,en,False,30 +1283,The Daily Star – World,Internacional y geopolítica.,https://www.thedailystar.net/world/rss,7,Internacional,15,Bangladés,en,False,30 +1294,The Financial Express – World,Noticias internacionales económicas.,https://thefinancialexpress.com.bd/world/rss,7,Internacional,15,Bangladés,en,False,30 +1296,UNB News – World,Internacional en inglés.,https://unb.com.bd/category/15/World/rss,7,Internacional,15,Bangladés,en,False,30 +1289,BDNews24 – Opinion,Análisis y columnas en inglés.,https://bdnews24.com/opinion/rss,10,Opinión,15,Bangladés,en,False,30 +1300,Bangla Tribune – Opinion,"Opinión, columnas y análisis (bn).",https://banglatribune.com/feed/opinion,10,Opinión,15,Bangladés,bn,False,30 +1287,Dhaka Tribune – Opinion & Editorial,Opinión y columnas.,https://www.dhakatribune.com/op-ed/feed,10,Opinión,15,Bangladés,en,False,30 +1291,New Age BD – Opinion,Opinión política y social.,https://www.newagebd.net/section/opinion/rss,10,Opinión,15,Bangladés,en,False,30 +1298,Prothom Alo – Editorial,Opinión editorial (bn).,https://www.prothomalo.com/opinion/editorial/feed,10,Opinión,15,Bangladés,bn,False,30 +1293,The Business Standard – Opinion,Opiniones económicas y análisis.,https://www.tbsnews.net/thoughts/feed,10,Opinión,15,Bangladés,en,False,30 +1285,The Daily Star – Opinion,"Editoriales, columnas y análisis.",https://www.thedailystar.net/opinion/rss,10,Opinión,15,Bangladés,en,False,30 +1295,The Financial Express – Editorial & Opinion,Opiniones económicas.,https://thefinancialexpress.com.bd/views/rss,10,Opinión,15,Bangladés,en,False,30 +1188,BDNews24 – Bangladesh,"Gobierno, administración estatal.",https://bdnews24.com/bangladesh/rss,11,Política,15,Bangladés,en,False,30 +1187,BDNews24 – Politics,Cobertura política nacional e internacional.,https://bdnews24.com/politics/rss,11,Política,15,Bangladés,en,False,30 +1194,Bangla Tribune (BN) – Politics,Política nacional en bengalí.,https://banglatribune.com/feed/politics,11,Política,15,Bangladés,bn,True,0 +1186,Dhaka Tribune – Bangladesh Affairs,"Gobierno, instituciones y seguridad.",https://www.dhakatribune.com/bangladesh/feed,11,Política,15,Bangladés,en,False,30 +1185,Dhaka Tribune – Politics,"Política nacional, elecciones, parlamento.",https://www.dhakatribune.com/bangladesh/politics/feed,11,Política,15,Bangladés,en,False,30 +1196,Ittefaq (BN) – National Politics,Noticias políticas nacionales (bn).,https://www.ittefaq.com.bd/politics/rss.xml,11,Política,15,Bangladés,bn,False,30 +1199,Jago News 24 (BN) – Politics,Portal digital muy fuerte en política.,https://www.jagonews24.com/politics/rss.xml,11,Política,15,Bangladés,bn,False,30 +1198,Kaler Kantho (BN) – National Affairs,"Gobierno, seguridad y política (bn).",https://www.kalerkantho.com/rss/online/national,11,Política,15,Bangladés,bn,False,30 +1190,New Age BD – Bangladesh Affairs,Asuntos estatales y administración pública.,https://www.newagebd.net/section/bangladesh/rss,11,Política,15,Bangladés,en,False,30 +1189,New Age BD – Politics,Opinión y noticias políticas críticas.,https://www.newagebd.net/section/politics/rss,11,Política,15,Bangladés,en,False,30 +1195,Prothom Alo (BN) – Politics,El mayor diario bengalí — política.,https://www.prothomalo.com/politics/feed,11,Política,15,Bangladés,bn,False,30 +1197,Samakal (BN) – Politics,Periódico con sección política (bn).,https://samakal.com/politics/rss.xml,11,Política,15,Bangladés,bn,False,30 +1192,The Business Standard – Governance,"Gobernanza, instituciones, reformas.",https://www.tbsnews.net/bangladesh/governance/feed,11,Política,15,Bangladés,en,False,30 +1191,The Business Standard – Politics,Noticias políticas en inglés.,https://www.tbsnews.net/bangladesh/politics/feed,11,Política,15,Bangladés,en,False,30 +1184,The Daily Star – Bangladesh,Asuntos nacionales y gubernamentales.,https://www.thedailystar.net/bangladesh/rss,11,Política,15,Bangladés,en,False,30 +1183,The Daily Star – Politics,Política nacional y gobierno.,https://www.thedailystar.net/politics/rss,11,Política,15,Bangladés,en,False,30 +1193,The Financial Express – Politics,Economía política y regulación.,https://thefinancialexpress.com.bd/national/politics/rss,11,Política,15,Bangladés,en,False,30 +1202,UNB News – Bangladesh,Asuntos nacionales y gobierno.,https://unb.com.bd/category/14/Bangladesh/rss,11,Política,15,Bangladés,en,False,30 +1201,UNB News – Politics,United News of Bangladesh — política en inglés.,https://unb.com.bd/category/18/Politics/rss,11,Política,15,Bangladés,en,False,30 +1248,BDNews24 – Lifestyle,"Cultura urbana, sociedad y tendencias.",https://bdnews24.com/lifestyle/rss,13,Sociedad,15,Bangladés,en,False,30 +1175,BDNews24 – Top Stories,Primer portal digital de Bangladesh.,https://bdnews24.com/rss,13,Sociedad,15,Bangladés,en,False,30 +1255,Bangla Tribune (BN) – Lifestyle,"Moda, sociedad, tendencias (bn).",https://banglatribune.com/feed/lifestyle,13,Sociedad,15,Bangladés,bn,True,0 +1246,Dhaka Tribune – Lifestyle,"Tendencias, moda, belleza.",https://www.dhakatribune.com/lifestyle/feed,13,Sociedad,15,Bangladés,en,False,30 +1171,Dhaka Tribune – News,Periódico en inglés muy influyente.,https://www.dhakatribune.com/feed,13,Sociedad,15,Bangladés,en,False,30 +1261,Jago News (BN) – Lifestyle,"Moda, tendencias, estilo de vida.",https://www.jagonews24.com/lifestyle/rss.xml,13,Sociedad,15,Bangladés,bn,False,30 +1257,Kaler Kantho (BN) – Lifestyle,"Lifestyle, moda y belleza (bn).",https://www.kalerkantho.com/rss/online/lifestyle,13,Sociedad,15,Bangladés,bn,False,30 +1250,New Age BD – Metro,Sociedad urbana y vida diaria.,https://www.newagebd.net/section/metro/rss,13,Sociedad,15,Bangladés,en,False,30 +1167,New Age BD – News,Periódico nacional en inglés.,https://www.newagebd.net/rss,13,Sociedad,15,Bangladés,en,False,30 +1253,Prothom Alo (BN) – Lifestyle,"Moda, hogar, estilo de vida bengalí.",https://www.prothomalo.com/lifestyle/feed,13,Sociedad,15,Bangladés,bn,False,30 +1179,Prothom Alo (BN) – News,Diario más leído del país (bengalí).,https://www.prothomalo.com/feed,13,Sociedad,15,Bangladés,bn,True,0 +1259,Samakal (BN) – Lifestyle,Estilo de vida en Bengalí.,https://samakal.com/lifestyle/rss.xml,13,Sociedad,15,Bangladés,bn,False,30 +1251,The Business Standard – Lifestyle,Lifestyle moderno y cultura urbana.,https://www.tbsnews.net/lifestyle/feed,13,Sociedad,15,Bangladés,en,False,30 +1245,The Daily Star – Education,"Educación, vida estudiantil.",https://www.thedailystar.net/youth/education/rss,13,Sociedad,15,Bangladés,en,False,30 +1243,The Daily Star – Lifestyle,"Moda, estilo de vida, cultura urbana.",https://www.thedailystar.net/lifestyle/rss,13,Sociedad,15,Bangladés,en,False,30 +1163,The Daily Star – News,El periódico en inglés más conocido del país.,https://www.thedailystar.net/rss,13,Sociedad,15,Bangladés,en,False,30 +1178,BDNews24 – Technology,"TI, digitalización, telecom.",https://bdnews24.com/technology/rss,14,Tecnología,15,Bangladés,en,False,30 +1237,Bangla Tribune (BN) – Tech,Tecnología digital y startups (bn).,https://banglatribune.com/feed/tech,14,Tecnología,15,Bangladés,bn,False,30 +1174,Dhaka Tribune – Tech,"Tecnología, TI, análisis digital.",https://www.dhakatribune.com/tech/feed,14,Tecnología,15,Bangladés,en,False,30 +1241,Jago News (BN) – Tech,"Tecnología, telecom y digital (bn).",https://www.jagonews24.com/technology/rss.xml,14,Tecnología,15,Bangladés,bn,False,30 +1239,Kaler Kantho (BN) – Science & Tech,Ciencia y tecnología en bengalí.,https://www.kalerkantho.com/rss/online/tech,14,Tecnología,15,Bangladés,bn,False,30 +1235,Prothom Alo (BN) – Technology,Tecnología en bengalí.,https://www.prothomalo.com/technology/feed,14,Tecnología,15,Bangladés,bn,False,30 +1240,Samakal (BN) – Science & Tech,Tecnología y ciencia (bn).,https://samakal.com/technology/rss.xml,14,Tecnología,15,Bangladés,bn,False,30 +1242,TechBangla – BD IT News,Blog tecnológico popular en Bengala.,https://www.techbangla.net/feed/,14,Tecnología,15,Bangladés,bn,False,30 +1230,The Business Standard – Tech,"Tecnología, startups, IA, fintech.",https://www.tbsnews.net/tech/feed,14,Tecnología,15,Bangladés,en,False,30 +1166,The Daily Star – Tech,"Tecnología, innovación, startups.",https://www.thedailystar.net/tech-startup/rss,14,Tecnología,15,Bangladés,en,False,30 +1232,The Financial Express – Tech,Noticias tecnológicas en inglés.,https://thefinancialexpress.com.bd/sci-tech/rss,14,Tecnología,15,Bangladés,en,False,30 +1233,UNB News – Tech,Tecnología e innovación.,https://unb.com.bd/category/33/Tech/rss,14,Tecnología,15,Bangladés,en,False,30 +1321,ChutiBD – Travel & Tourism,Blog de viajes bangladesí.,https://chutibd.com/feed/,15,Viajes,15,Bangladés,bn,False,30 +1396,CARPHA – Caribbean Health Science,"Epidemiología, ciencia médica del Caribe.",https://carpha.org/Portals/0/RSS.aspx,1,Ciencia,16,Barbados,en,True,0 +1395,Caribbean AI & Data Initiative,"IA, datos, innovación científica regional.",https://caribbean-ai.org/feed/,1,Ciencia,16,Barbados,en,False,30 +1385,Caribbean Climate – Climate Science,"Ciencia climática del Caribe, Barbados incluido.",https://www.caribbeanclimate.bz/feed/,1,Ciencia,16,Barbados,en,False,30 +1389,Caribbean Development Bank – Research,"Ciencia aplicada, economía, desarrollo STEM.",https://www.caribank.org/rss.xml,1,Ciencia,16,Barbados,en,True,0 +1391,Caribbean Disaster Emergency Management – Science,"Tecnología de emergencias, análisis climático.",https://www.cdema.org/feed,1,Ciencia,16,Barbados,en,False,30 +1390,Caribbean Meteorological Organisation – Science,Ciencia del clima y meteorología.,https://www.cmo.org.bb/feed/,1,Ciencia,16,Barbados,en,False,30 +1388,Caribbean Renewable Energy Forum – Clean Tech,"Energía limpia, innovación verde.",https://newenergyevents.com/cref/feed/,1,Ciencia,16,Barbados,en,False,30 +1381,Caribbean Science & Innovation News,Ciencia y tecnología en el Caribe.,https://www.caribbeannewsglobal.com/category/science/feed/,1,Ciencia,16,Barbados,en,True,0 +1383,UWI Cave Hill – Faculty of Science & Tech,Noticias científicas universitarias.,https://www.cavehill.uwi.edu/fst/news-rss.aspx,1,Ciencia,16,Barbados,en,False,30 +1399,Barbados Today – Entertainment,"Música, cine, cultura caribeña.",https://barbadostoday.bb/category/entertainment/feed/,2,Cultura,16,Barbados,en,True,0 +1330,Nation News – Entertainment,"Cultura, música y celebridades.",https://www.nationnews.com/category/entertainment/feed/,2,Cultura,16,Barbados,en,True,0 +1337,Barbados Advocate – Sports,"Cricket, atletismo y competiciones locales.",https://www.barbadosadvocate.com/sports/rss.xml,3,Deportes,16,Barbados,en,False,30 +1427,Barbados Athletics Association – News,"Atletismo local, competiciones juveniles.",https://www.athletics.org.bb/feed/,3,Deportes,16,Barbados,en,False,30 +1436,Barbados Badminton Association – Updates,Badminton local y competiciones.,https://barbadosbadminton.org/feed/,3,Deportes,16,Barbados,en,False,30 +1424,Barbados Cricket Association – News,Noticias oficiales del cricket barbadense.,https://barbadoscricket.org/feed/,3,Deportes,16,Barbados,en,True,0 +1433,Barbados Football Association – News,Fútbol barbadense y competiciones.,https://barbadosfa.com/feed/,3,Deportes,16,Barbados,en,True,0 +1437,Barbados Hockey Federation – News,Hockey sobre césped y torneos juveniles.,https://barbadoshockey.org/feed/,3,Deportes,16,Barbados,en,False,30 +1429,Barbados Netball Association – News,Netball femenino en Barbados.,https://www.barbadosnetball.com/feed/,3,Deportes,16,Barbados,en,False,30 +1426,Barbados Olympic Association – Updates,"Atletismo, juegos regionales, comités olímpicos.",https://olympic.org.bb/feed/,3,Deportes,16,Barbados,en,True,0 +1428,Barbados Rugby Football Union – News,Rugby barbadense y ligas regionales.,https://brfu.bb/feed/,3,Deportes,16,Barbados,en,False,30 +1326,Barbados Today – Sports,"Cricket, atletismo y deportes nacionales.",https://barbadostoday.bb/category/sports/feed/,3,Deportes,16,Barbados,en,True,0 +1431,CaribSport – Caribbean Sports Center,Noticias deportivas del Caribe y Barbados.,https://www.caribsportslive.com/feed/,3,Deportes,16,Barbados,en,False,30 +1434,Caribbean Cycling – News,"Ciclismo caribeño, Barbados incluido.",https://www.caribbeancycling.com/feed/,3,Deportes,16,Barbados,en,False,30 +1425,Caribbean Premier League – CPL T20,Liga de cricket T20 caribeña.,https://www.cplt20.com/rss.xml,3,Deportes,16,Barbados,en,False,30 +1430,Caribbean Sports – Regional,Cobertura deportiva regional Caribe.,https://www.caribbeannewsglobal.com/category/sports/feed/,3,Deportes,16,Barbados,en,True,0 +1435,"Caribbean Swimming – Regional""",Natación y torneos del Caribe.,https://www.swimcaribbean.com/feed/,3,Deportes,16,Barbados,en,False,30 +1422,Cricket West Indies – Barbados News,Cobertura oficial del cricket caribeño.,https://www.windiescricket.com/news/feed/,3,Deportes,16,Barbados,en,False,30 +1423,ESPN Cricinfo – Barbados Section,"Cricket profesional, estadísticas.",https://www.espncricinfo.com/rss/content/story/feeds/westindies.xml,3,Deportes,16,Barbados,en,False,30 +1333,Loop Barbados – Sports,"Deportes, ligas y torneos del Caribe.",https://barbados.loopnews.com/sports/rss,3,Deportes,16,Barbados,en,False,30 +1329,Nation News – Sports,Sección deportiva del mayor periódico del país.,https://www.nationnews.com/category/sports/feed/,3,Deportes,16,Barbados,en,True,0 +1432,West Indies High Performance Centre – Sports,Centro deportivo del Caribe (con sede en Barbados).,https://uwihpc.com/feed/,3,Deportes,16,Barbados,en,False,30 +1373,Barbados Advocate – Business,Noticias económicas generales.,https://www.barbadosadvocate.com/business/rss.xml,4,Economía,16,Barbados,en,False,30 +1364,Barbados Chamber of Commerce – News,"Cámara de Comercio: economía, pymes.",https://bcci.org.bb/feed/,4,Economía,16,Barbados,en,False,30 +1369,Barbados International Finance & Business,Sector financiero internacional del país.,https://biba.bb/feed/,4,Economía,16,Barbados,en,True,0 +1376,Barbados Renewable Energy Association – News,"Energía renovable, economía verde.",https://brea.bb/feed/,4,Economía,16,Barbados,en,True,0 +1324,Barbados Today – Business,"Economía, empresas y mercado laboral.",https://barbadostoday.bb/business/feed/,4,Economía,16,Barbados,en,False,30 +1359,Barbados Today – Tourism Business,"Turismo, hoteles, cruceros, economía turística.",https://barbadostoday.bb/category/business/tourism/feed/,4,Economía,16,Barbados,en,True,0 +1370,Captive Insurance Times – Barbados,Barbados es hub global de seguros cautivos.,https://www.captiveinsurancetimes.com/rss/index.php,4,Economía,16,Barbados,en,False,30 +1375,Caribbean National Weekly – Barbados Business,Economía regional desde Florida-Caribe.,https://www.cnwnetwork.com/tag/barbados-business/feed/,4,Economía,16,Barbados,en,False,30 +1363,Caribbean News Global – Barbados Business,Economía barbadense desde visión regional.,https://www.caribbeannewsglobal.com/category/barbados/business/feed/,4,Economía,16,Barbados,en,False,30 +1341,Central Bank of Barbados – Economic News,"Política económica, finanzas, informes.",https://www.centralbank.org.bb/rss,4,Economía,16,Barbados,en,False,30 +1368,International Business – Barbados,"Finanzas, banca offshore, seguros internacionales.",https://www.businessbarbados.com/feed/,4,Economía,16,Barbados,en,False,30 +1357,Invest Barbados – Business,"Inversiones, clima económico, desarrollo.",https://www.investbarbados.org/feed/,4,Economía,16,Barbados,en,True,0 +1332,Loop Barbados – Business,"Mercados, inversiones y empresas.",https://barbados.loopnews.com/business/rss,4,Economía,16,Barbados,en,False,30 +1374,Loop Caribbean – Finance & Economy,Economía del Caribe (Barbados incluida).,https://caribbean.loopnews.com/business/rss,4,Economía,16,Barbados,en,False,30 +1328,Nation News – Business,Economía y finanzas nacionales.,https://www.nationnews.com/category/business/feed/,4,Economía,16,Barbados,en,True,0 +1361,Nation News – Economy,"Comercio, banca, impuestos.",https://www.nationnews.com/category/business/economy/feed/,4,Economía,16,Barbados,en,False,30 +1340,BGIS Barbados – International Relations,Relaciones exteriores y diplomacia.,https://gisbarbados.gov.bb/feed/,7,Internacional,16,Barbados,en,False,30 +1459,Barbados Today – World News,Internacional y Caribe.,https://barbadostoday.bb/category/world/feed/,7,Internacional,16,Barbados,en,True,0 +1477,Caribbean Intelligence – Global Affairs,Geopolítica y análisis regional/global.,https://www.caribbeanintelligence.com/rss.xml,7,Internacional,16,Barbados,en,True,0 +1352,Caribbean National Weekly – Barbados International,Internacional desde perspectiva caribeña.,https://www.cnwnetwork.com/tag/barbados/feed/,7,Internacional,16,Barbados,en,False,30 +1334,Caribbean News Global – Barbados World,Internacional con enfoque barbadense.,https://www.caribbeannewsglobal.com/category/barbados/feed/,7,Internacional,16,Barbados,en,True,0 +1353,Caribbean News Service – Barbados World,Noticias globales que afectan al Caribe.,https://caribbeannewsservice.com/tag/barbados/feed/,7,Internacional,16,Barbados,en,False,30 +1462,Loop Barbados – World News,Internacional y Caribe anglófono.,https://barbados.loopnews.com/world/rss,7,Internacional,16,Barbados,en,False,30 +1349,Ministry of Foreign Affairs Barbados – Updates,Noticias oficiales diplomáticas.,https://foreign.gov.bb/feed/,7,Internacional,16,Barbados,en,True,0 +1461,Nation News – World,"Internacional, región Caribe y global.",https://www.nationnews.com/category/world/feed/,7,Internacional,16,Barbados,en,True,0 +1397,UN Caribbean – International News,Internacional ONU para el Caribe.,https://news.un.org/feed/region/caribbean,7,Internacional,16,Barbados,en,True,0 +1473,Barbados Advocate – Editorial,Opinión y análisis en prensa tradicional.,https://www.barbadosadvocate.com/editorial/rss.xml,10,Opinión,16,Barbados,en,False,30 +1458,Barbados Today – Opinion,"Editoriales, columnas y análisis.",https://barbadostoday.bb/category/opinion/feed/,10,Opinión,16,Barbados,en,True,0 +1474,Caribbean Investigative Journalism Network – Barbados,Investigación y análisis del Caribe.,https://www.cijn.org/feed/,10,Opinión,16,Barbados,en,True,0 +1464,Caribbean News Global – Barbados Opinion,Opinión política del Caribe sobre Barbados.,https://www.caribbeannewsglobal.com/category/barbados/opinion/feed/,10,Opinión,16,Barbados,en,False,30 +1471,Caribbean Policy & Opinion Forum,Análisis regional y opinión pública.,https://www.caribbeanpolicy.org/feed/,10,Opinión,16,Barbados,en,False,30 +1463,Loop Barbados – Opinion,Artículos de opinión del Caribe.,https://barbados.loopnews.com/opinion/rss,10,Opinión,16,Barbados,en,False,30 +1460,Nation News – Editorial,Opinión del principal diario impreso.,https://www.nationnews.com/category/editorial/feed/,10,Opinión,16,Barbados,en,False,30 +1382,UWI Cave Hill – International Affairs,Opinión académica internacional.,https://www.cavehill.uwi.edu/news-events/news-rss.aspx,10,Opinión,16,Barbados,en,False,30 +1345,Barbados Advocate – Politics,Política y asuntos de gobierno.,https://www.barbadosadvocate.com/politics/rss.xml,11,Política,16,Barbados,en,False,30 +1356,Barbados Electoral and Boundaries Commission – News,Actualizaciones electorales oficiales.,https://www.electoral.gov.bb/feed/,11,Política,16,Barbados,en,False,30 +1355,Barbados Parliament – Press Releases,Comunicados del Parlamento de Barbados.,https://www.barbadosparliament.com/rss,11,Política,16,Barbados,en,False,30 +1325,Barbados Today – Politics,"Política nacional, gobierno, parlamento.",https://barbadostoday.bb/category/local-news/politics/feed/,11,Política,16,Barbados,en,True,0 +1346,Loop Barbados – Politics,Política y temas legislativos.,https://barbados.loopnews.com/politics/rss,11,Política,16,Barbados,en,False,30 +1354,Loop Caribbean – Politics (Barbados Section),Cobertura política del Caribe en general.,https://caribbean.loopnews.com/politics/rss,11,Política,16,Barbados,en,False,30 +1344,Nation News – Politics,Noticias políticas del principal diario impreso del país.,https://www.nationnews.com/category/politics/feed/,11,Política,16,Barbados,en,True,0 +1348,Office of the Prime Minister – Barbados,Comunicados del Primer Ministro.,https://opm.gov.bb/feed/,11,Política,16,Barbados,en,False,30 +1456,Bajan Living – Lifestyle,Blog de estilo de vida barbadense.,https://bajanliving.com/feed/,13,Sociedad,16,Barbados,en,False,30 +1336,Barbados Advocate – News,Uno de los periódicos más antiguos del país.,https://www.barbadosadvocate.com/rss.xml,13,Sociedad,16,Barbados,en,False,30 +1453,Barbados Food & Rum Festival – Events,Eventos gastronómicos oficiales.,https://foodandrum.com/feed/,13,Sociedad,16,Barbados,en,True,0 +1398,Barbados Today – Lifestyle,"Estilo de vida caribeño, moda, salud.",https://barbadostoday.bb/category/lifestyle/feed/,13,Sociedad,16,Barbados,en,True,0 +1323,Barbados Today – News,El medio digital más leído del país.,https://barbadostoday.bb/feed/,13,Sociedad,16,Barbados,en,True,0 +1342,Barbados Weather Service – Forecasts,Clima y alertas meteorológicas.,https://barbadosweather.org/rss.xml,13,Sociedad,16,Barbados,en,False,30 +1457,Caribbean Fashion Blog – Barbados Section,"Moda caribeña, diseñadores locales.",https://www.caribbeanfashionblog.com/feed/,13,Sociedad,16,Barbados,en,False,30 +1331,Loop Barbados – News,Portal moderno de noticias caribeñas.,https://barbados.loopnews.com/rss,13,Sociedad,16,Barbados,en,False,30 +1400,Nation News – Lifestyle,"Sociedad, bienestar, estilo de vida.",https://www.nationnews.com/category/lifestyle/feed/,13,Sociedad,16,Barbados,en,True,0 +1327,Nation News – Top Stories,El periódico tradicional más grande del país.,https://www.nationnews.com/feed/,13,Sociedad,16,Barbados,en,True,0 +1338,The Barbados Independent – News,Portal digital privado.,https://www.thebarbadosindependent.com/feed/,13,Sociedad,16,Barbados,en,False,30 +1378,Barbados Today – Tech,"Tecnología, digitalización y TI en Barbados.",https://barbadostoday.bb/category/technology/feed/,14,Tecnología,16,Barbados,en,True,0 +1386,Caribbean ICT News – Tech,"Ciberseguridad, redes, telecomunicaciones.",https://www.caribbeanict.org/feed/,14,Tecnología,16,Barbados,en,False,30 +1392,Caribbean Tech News (ICT Pulse),Análisis tecnológico del Caribe.,https://www.ict-pulse.com/feed/,14,Tecnología,16,Barbados,en,True,0 +1387,Caribbean Telecommunications Union – ICT News,Digitalización y telecomunicaciones en el Caribe.,https://ctu.int/feed/,14,Tecnología,16,Barbados,en,True,0 +1394,Caribbean eGovernment – ICT Policy,Transformación digital de gobiernos caribeños.,https://www.egovcaribbean.org/feed/,14,Tecnología,16,Barbados,en,False,30 +1380,Loop Barbados – Tech,Tecnología caribeña y tendencias digitales.,https://barbados.loopnews.com/tech/rss,14,Tecnología,16,Barbados,en,False,30 +1379,Nation News – Sci/Tech,"Tecnología, ciencia, innovación local.",https://www.nationnews.com/category/nationnews/scitech/feed/,14,Tecnología,16,Barbados,en,False,30 +1393,SiliconCaribe – Caribbean Tech & Startups,Startups digitales del Caribe.,https://www.siliconcaribe.com/feed/,14,Tecnología,16,Barbados,en,True,0 +1377,Barbados Hotel & Tourism Association – Industry,"Hoteles, cruceros, gastronomía.",https://barbadoshotelassociation.com/feed/,15,Viajes,16,Barbados,en,False,30 +1439,Barbados Today – Travel,"Noticias de turismo, vuelos, eventos.",https://barbadostoday.bb/category/lifestyle/travel/feed/,15,Viajes,16,Barbados,en,True,0 +1335,CaribJournal – Barbados Travel,"Destinos, hoteles, reportajes.",https://www.caribjournal.com/category/destinations/barbados/feed/,15,Viajes,16,Barbados,en,False,30 +1371,Caribbean Hotel & Tourism Association – News,Turismo regional del Caribe.,https://www.caribbeanhotelandtourism.com/news-center/feed/,15,Viajes,16,Barbados,en,True,0 +1446,Caribbean Journal – Travel,Turismo caribeño con foco en BGI.,https://www.caribjournal.com/feed/,15,Viajes,16,Barbados,en,True,0 +1452,Caribbean Travel Marketplace – Tourism,"Hotelería, vuelos, tendencias de viaje.",https://www.caribbeanhotelandtourism.com/feed/,15,Viajes,16,Barbados,en,True,0 +1455,Cruise Barbados – Port Updates,"Llegadas de cruceros, turismo marítimo.",https://www.barbadosport.com/feed/,15,Viajes,16,Barbados,en,False,30 +1444,Loop Barbados – Travel,Noticias turísticas del Caribe y Barbados.,https://barbados.loopnews.com/travel/rss,15,Viajes,16,Barbados,en,False,30 +1441,Nation News – Travel,Turismo local y regional.,https://www.nationnews.com/category/lifestyle/travel/feed/,15,Viajes,16,Barbados,en,False,30 +1451,Travel Weekly – Caribbean (Barbados),Noticias de viajes profesionales.,https://www.travelweekly.com/rss/Caribbean,15,Viajes,16,Barbados,en,False,30 +1339,Visit Barbados – Travel News,"Turismo oficial, destinos, eventos.",https://www.visitbarbados.org/rss.xml,15,Viajes,16,Barbados,en,False,30 +1593,BAPCO – Research & Energy News,Ciencia de energía y petroquímica.,https://www.bapco.net/en/feed/,1,Ciencia,17,Baréin,en,False,30 +1592,Energy Voice – Middle East Science,"Energía, gas, investigación técnica.",https://www.energyvoice.com/feed/,1,Ciencia,17,Baréin,en,False,30 +1632,Bahrain Cultural Hall – Events,"Eventos artísticos, danza, teatro.",https://culture.gov.bh/en/rss,2,Cultura,17,Baréin,en,False,30 +1639,Caribbean? no → Middle East Culture Review – Bahrain Section,Reseñas culturales del Golfo con foco en Baréin.,https://mideastculture.com/feed/,2,Cultura,17,Baréin,en,False,30 +1558,Visit Bahrain – Culture,"Cultura, monumentos y patrimonio de Baréin.",https://visitbahrain.bh/feed/,2,Cultura,17,Baréin,en,True,0 +1651,Bahrain Athletics Association – Reports,"Pruebas de pista, maratones y atletas.",https://baa.bh/feed/,3,Deportes,17,Baréin,en,False,30 +1649,Bahrain Basketball Association – Updates,Liga de baloncesto y selecciones.,https://www.bahrainbasketball.com/feed/,3,Deportes,17,Baréin,en,False,30 +1652,Bahrain Cycling Association – News,Ciclismo y competiciones nacionales.,https://www.bahraincycling.org/feed/,3,Deportes,17,Baréin,en,False,30 +1647,Bahrain Football Association – News,Noticias oficiales del fútbol de Baréin.,https://bfa.bh/feed/,3,Deportes,17,Baréin,en,False,30 +1650,Bahrain Handball Association – News,Balonmano nacional y competiciones.,https://www.bahrainhandball.com/feed/,3,Deportes,17,Baréin,en,False,30 +1646,Bahrain Mirror – Sports,"Eventos, resultados y análisis locales.",https://bahrainmirror.com/feed/sports/,3,Deportes,17,Baréin,ar,False,30 +1654,Bahrain Motorsport – Official,Automovilismo nacional y eventos de motor.,https://motorsportbahrain.com/feed/,3,Deportes,17,Baréin,en,False,30 +1648,Bahrain Olympic Committee – News,"Atletismo, federaciones olímpicas, torneos.",https://boc.bh/feed/,3,Deportes,17,Baréin,en,False,30 +1653,Bahrain Volleyball Association – News,Voleibol masculino y femenino.,https://www.bahrainvolleyball.com/feed/,3,Deportes,17,Baréin,en,False,30 +1559,Formula 1 – Bahrain GP,Información oficial del Gran Premio de Baréin.,https://www.formula1.com/rss-feed.html,3,Deportes,17,Baréin,en,False,30 +1542,Gulf Daily News – Sports,Deportes de Baréin y del Golfo.,https://www.gdnonline.com/RSS/3/Sports,3,Deportes,17,Baréin,en,False,30 +1595,Gulf Insider – Bahrain Sports,Resumen deportivo del Golfo incl. Baréin.,https://www.gulf-insider.com/feed/,3,Deportes,17,Baréin,en,True,0 +1599,BNG – Bahrain National Gas Company,"Gas natural, petroquímicos, energía.",https://www.bngc.com.bh/feed/,4,Economía,17,Baréin,en,False,30 +1586,Bahrain Bourse – Market Updates,"Bolsa de Baréin: precios, índices y anuncios.",https://www.bahrainbourse.com/rss,4,Economía,17,Baréin,en,False,30 +1588,Bahrain Chamber of Commerce – News,"Comercio, industria y pequeñas empresas.",https://www.bcci.bh/feed/,4,Economía,17,Baréin,en,False,30 +1587,Central Bank of Bahrain – Press Releases,"Política monetaria, banca islámica y regulación financiera.",https://www.cbb.gov.bh/feed/,4,Economía,17,Baréin,en,False,30 +1596,Construction Week – Bahrain,"Construcción, infraestructura, proyectos del Golfo.",https://www.constructionweekonline.com/feed,4,Economía,17,Baréin,en,False,30 +1541,Gulf Daily News – Business,"Economía nacional, banca, petróleo y mercado laboral.",https://www.gdnonline.com/RSS/2/Business,4,Economía,17,Baréin,en,False,30 +1597,Insurance Middle East – Bahrain,"Aseguradoras, reaseguros, mercados islámicos.",https://www.insurancemena.com/rss,4,Economía,17,Baréin,en,False,30 +1589,Invest in Bahrain – FDI News,Noticias de inversión extranjera directa en Baréin.,https://www.investinbahrain.com/feed/,4,Economía,17,Baréin,en,False,30 +1591,Oil & Gas Middle East – Bahrain,"Petróleo, gas, seguridad energética.",https://www.oilandgasmiddleeast.com/rss,4,Economía,17,Baréin,en,False,30 +1550,Trade Arabia – Bahrain Business,"Negocios, energía y finanzas del Golfo.",https://www.tradearabia.com/rss/feed.asp?sec=BUS,4,Economía,17,Baréin,en,False,30 +1551,Zawya – Bahrain Economy,"Economía, inversión, petróleo, banca islámica.",https://www.zawya.com/rss/middle-east/bahrain/,4,Economía,17,Baréin,en,False,30 +1575,Amnesty International – Bahrain,Derechos humanos y política exterior.,https://www.amnesty.org/en/location/middle-east-and-north-africa/bahrain/feed/,7,Internacional,17,Baréin,en,True,0 +1572,Arab News – Bahrain,Baréin visto desde Arabia Saudí.,https://www.arabnews.com/rss,7,Internacional,17,Baréin,en,False,30 +1674,BBC Arabic – Middle East (incluye Bahrain),Análisis MENA y diplomacia.,https://www.bbc.com/arabic/index.xml,7,Internacional,17,Baréin,ar,True,0 +1543,Bahrain News Agency – International,Reportes exterior y diplomacia.,https://bna.bh/en/rss.aspx,7,Internacional,17,Baréin,en,False,30 +1678,Carnegie Middle East – Gulf States,Análisis profundo de política exterior.,https://carnegie-mec.org/rss,7,Internacional,17,Baréin,en,False,30 +1578,ECFR – Gulf & Bahrain,Think tank europeo: geopolítica del Golfo.,https://ecfr.eu/rss,7,Internacional,17,Baréin,en,True,0 +1576,Human Rights Watch – Bahrain,Informes internacionales sobre política y sociedad.,https://www.hrw.org/middle-east/n-africa/bahrain/feed,7,Internacional,17,Baréin,en,False,30 +1553,Khaleej Times – Bahrain International,Geopolítica del Golfo.,https://www.khaleejtimes.com/rss,7,Internacional,17,Baréin,en,False,30 +1555,Middle East Monitor – Bahrain,Análisis MENA (Baréin incluido).,https://www.middleeastmonitor.com/feed/,7,Internacional,17,Baréin,en,False,30 +1547,News of Bahrain – World,Internacional y política exterior.,https://newsofbahrain.com/rss,7,Internacional,17,Baréin,en,False,30 +1673,Reuters – Middle East (incluye Bahrain),Cobertura internacional profesional y neutra.,https://www.reuters.com/tools/rss,7,Internacional,17,Baréin,en,False,30 +1556,The National (UAE) – Bahrain,Sección internacional del Golfo.,https://www.thenationalnews.com/rss/,7,Internacional,17,Baréin,en,False,30 +1579,UN News – Bahrain,Informes y política internacional de la ONU.,https://news.un.org/feed/country/bhr,7,Internacional,17,Baréin,en,True,0 +1564,Akhbar Al Khaleej – Opinion,Editoriales árabes sobre política y sociedad.,https://www.akhbar-alkhaleej.com/rss.aspx,10,Opinión,17,Baréin,ar,False,30 +1545,Al Ayam – Opinion,Columnas y análisis en árabe.,https://www.alayam.com/rss/default.aspx,10,Opinión,17,Baréin,ar,False,30 +1546,Al Bilad – Opinion,Opinión política y social.,https://albiladpress.com/feed/,10,Opinión,17,Baréin,ar,False,30 +1664,Bahrain Mirror – Opinion,Análisis político y social independiente.,https://bahrainmirror.com/feed/opinion/,10,Opinión,17,Baréin,ar,False,30 +1660,Gulf Daily News – Opinion,Editoriales y columnas sobre Baréin y el Golfo.,https://www.gdnonline.com/RSS/5/Opinion,10,Opinión,17,Baréin,en,False,30 +1568,Bahrain Mirror – Human Rights,Derechos civiles y política interna.,https://bahrainmirror.com/feed/human-rights/,11,Política,17,Baréin,ar,False,30 +1548,Bahrain Mirror – Politics,Análisis político independiente y crítico.,https://bahrainmirror.com/feed/,11,Política,17,Baréin,ar,False,30 +1544,Bahrain News Agency (AR) – Política,Noticias gubernamentales oficiales en árabe.,https://bna.bh/ar/rss.aspx,11,Política,17,Baréin,ar,False,30 +1577,CARPO – Gulf Politics (incluye Bahrein),Think tank alemán especializado en el Golfo.,https://carpo-bonn.org/feed/,11,Política,17,Baréin,en,False,30 +6349,crisisgroup.org Jordan,,https://www.crisisgroup.org/rss/86,11,Política,17,Baréin,,True,0 +1549,Bahrain Mirror – Society,Sociedad civil y temas sociales.,https://bahrainmirror.com/feed/society/,13,Sociedad,17,Baréin,ar,False,30 +1557,Bahrain This Month – Lifestyle,"Cultura, vida social, eventos, arte.",https://www.bahrainthismonth.com/rss,13,Sociedad,17,Baréin,en,False,30 +1540,Gulf Daily News – Local News,"Sociedad bahreiní, eventos y vida diaria.",https://www.gdnonline.com/RSS/1/News,13,Sociedad,17,Baréin,en,False,30 +1635,Love Bahrain – Community News,"Comunidad, sociedad y noticias humanas.",https://lovebahrain.co/feed/,13,Sociedad,17,Baréin,en,False,30 +1552,Arabian Business – Tech,Tecnología del Golfo y startups.,https://www.arabianbusiness.com/feed,14,Tecnología,17,Baréin,en,True,0 +1585,Bahrain Economic Board – Innovation,"Tecnología, startups, emprendimiento digital.",https://www.bahrainedb.com/feed/,14,Tecnología,17,Baréin,en,False,30 +1594,Bahrain FinTech Bay – News,Centro de innovación financiera del Golfo.,https://www.bahrainfintechbay.com/blog?format=rss,14,Tecnología,17,Baréin,en,False,30 +1598,Gulf Business – Technology,Tecnología empresarial y digitalización.,https://gulfbusiness.com/feed/,14,Tecnología,17,Baréin,en,True,0 +1600,Gulf Daily News – Sci/Tech,"Tecnología, digitalización y ciencia.",https://www.gdnonline.com/RSS/4/Life-Style,14,Tecnología,17,Baréin,en,False,30 +1590,MEED – Technology & Innovation,"Infraestructura, innovación y proyectos.",https://www.meed.com/feed/,14,Tecnología,17,Baréin,en,False,30 +1497,Love FM – Health & Science,Sección salud y ciencia del portal de radio.,https://lovefm.com/category/health-science/,1,Ciencia,19,Belice,en,False,30 +1499,Mongabay – Belize Conservation News,"Conservación, medioambiente y ciencia en Belice.",https://news.mongabay.com/list/belize/,1,Ciencia,19,Belice,en,False,30 +1498,UB-ERI – News,Instituto de investigación ambiental de Belice.,https://www.uberibz.org/news,1,Ciencia,19,Belice,en,False,30 +1496,University of Belize – Research Office,Noticias de investigación y ciencia de la universidad.,https://old.ub.edu.bz/news/,1,Ciencia,19,Belice,en,False,30 +1519,Statistical Institute of Belize – Publications RSS,"Datos, economía y sociedad.",https://sib.org.bz/rss/,4,Economía,19,Belice,en,True,0 +1538,Belize Ministry of Education,Science & Technology – News & Events,"Educación, ciencia y tecnología gubernamental.",5,Educación,19,Belice,en,False,30 +1521,Breaking Belize News – Education,Sección educación del portal BBN.,https://www.breakingbelizenews.com/category/education/,5,Educación,19,Belice,en,False,30 +1522,University of Belize / BFREE – Educator News,Noticias de educación desde ONG’s y universidad.,https://www.bfreebz.org/category/educator/feed/,5,Educación,19,Belice,en,True,0 +1482,Amandala – Politics,Sección política del periódico Amandala.,https://amandala.com.bz/category/politics/feed/,11,Política,19,Belice,en,False,30 +1488,Belize Electoral & Boundaries Commission – News,"Información electoral, política de circunscripciones.",https://www.electoral.gov.bz/feed/,11,Política,19,Belice,en,False,30 +1486,Belize Times – Politics,Órgano oficial de un partido político: sección política.,https://www.belizetimes.bz/category/politics/feed/,11,Política,19,Belice,en,False,30 +1483,Breaking Belize News – Politics,Portal digital: noticias políticas y elecciones.,https://www.breakingbelizenews.com/category/politics/feed/,11,Política,19,Belice,en,True,0 +1372,CARICOM – Belize Politics,Noticias regionales del Caribe sobre política de Belice.,https://today.caricom.org/feed/,11,Política,19,Belice,en,False,30 +1489,Caribbean News Global – Belize Politics,Cobertura política del Caribe centrada en Belice.,https://www.caribbeannewsglobal.com/category/belize/politics/feed/,11,Política,19,Belice,en,False,30 +1480,Channel 5 Belize – Politics,Sección de noticias políticas del canal 5.,https://edition.channel5belize.com/category/local-news/politics/feed,11,Política,19,Belice,en,False,30 +1481,Channel 7 Belize – Politics,Sección política del 7 News.,https://7newsbelize.com/rss.php,11,Política,19,Belice,en,False,30 +1492,Global Voices – Belize Politics,Cobertura internacional de política beliceña.,https://globalvoices.org/feeds/,11,Política,19,Belice,en,False,30 +1478,Government of Belize – Press Office,Comunicados oficiales del gobierno de Belice.,https://www.pressoffice.gov.bz/press-releases/,11,Política,19,Belice,en,False,30 +1485,Love FM – Politics & Government,Noticias de radio sobre política y gobierno.,https://lovefm.com/category/politics/feed/,11,Política,19,Belice,en,True,0 +1479,Ministry of Foreign Affairs – Belize,Política exterior y diplomacia.,https://mfa.gov.bz/press-releases/,11,Política,19,Belice,en,False,30 +1487,Office of the Prime Minister – News,Comunicados del Primer Ministro.,https://www.opm.gov.bz/feed/,11,Política,19,Belice,en,False,30 +1484,The Reporter Belize – Politics,Análisis político y reportajes de política.,https://reporter.bz/category/politics/feed/,11,Política,19,Belice,en,False,30 +1494,Believe in Belize – RSS Feed by Google,Agregado de noticias de Belice diversas.,https://believeinbelize.org/page/rss-feed-by-google,13,Sociedad,19,Belice,en,False,30 +1322,Belize Food & Culture – Blog,Gastronomía y cultura local.,https://www.foodiez.com/feed/,13,Sociedad,19,Belice,en,True,0 +1454,Caribbean Food & Lifestyle – Caribbean Focus (Belize incluida),"Gastronomía, estilo de vida y cultura caribeña.",https://www.caribbeanfood.com/feed/,13,Sociedad,19,Belice,en,False,30 +1476,Commonwealth Portal – Belize News,Noticias generales desde la Commonwealth.,https://thecommonwealth.org/media/rss,13,Sociedad,19,Belice,en,False,30 +1493,Greater Belize Media – Technology,Sección tecnología de portal de noticias en Belice.,https://www.greaterbelize.com/category/technology/,14,Tecnología,19,Belice,en,False,30 +1767,24h au Bénin – Santé & Science,"Salud, ciencia y medio ambiente.",https://24haubenin.info/spip.php?page=backend&mot=34,1,Ciencia,20,Benín,fr,True,0 +1771,Actu Bénin – Science,Ciencia y salud pública.,https://actubenin.com/category/sante/feed/,1,Ciencia,20,Benín,fr,False,30 +1768,Benin Intelligent – Science,"Ciencia, medicina y educación STEM.",https://beninintelligent.com/category/science/feed/,1,Ciencia,20,Benín,fr,False,30 +1764,Benin Web TV – Science & Santé,"Ciencia, medicina, investigación local.",https://beninwebtv.com/category/sante/feed/,1,Ciencia,20,Benín,fr,False,30 +1769,Crystal News – Environnement,"Ciencia ambiental, clima, sostenibilidad.",https://www.crystal-news.com/category/environnement/feed/,1,Ciencia,20,Benín,fr,False,30 +1779,Fondation de Recherche du Bénin – Actualités,Proyectos científicos y publicaciones.,https://frb.bj/feed/,1,Ciencia,20,Benín,fr,False,30 +1770,Frissons Media – Santé/Science,Salud y reportajes científicos.,https://frissonsmedia.bj/category/sante/feed/,1,Ciencia,20,Benín,fr,False,30 +1762,Institut National des Recherches Agricoles du Bénin (INRAB),"Ciencia agrícola, proyectos, innovación.",https://inrab.bj/feed/,1,Ciencia,20,Benín,fr,False,30 +1765,La Nation – Science & Environnement,"Ciencia ambiental, clima y biodiversidad.",https://lanation.bj/category/environnement/feed/,1,Ciencia,20,Benín,fr,False,30 +1760,Université d'Abomey-Calavi – Recherche,Investigación académica en Benín.,https://uac.bj/feed/,1,Ciencia,20,Benín,fr,False,30 +1761,Université de Parakou – Actualités scientifiques,Noticias universitarias y de ciencia.,https://univ-parakou.bj/feed/,1,Ciencia,20,Benín,fr,False,30 +1778,WHO Africa – Benin Health & Research,"Ciencia médica, salud pública e investigación.",https://www.afro.who.int/rss,1,Ciencia,20,Benín,fr,False,30 +1695,Africa Top Success – Benin Culture,"Cultura, entretenimiento y arte africano.",https://africatopsuccess.com/feed/,2,Cultura,20,Benín,fr,True,0 +1794,Direction du Patrimoine Culturel du Bénin,"Patrimonio, museos, historia.",https://patrimoinebenin.bj/feed/,2,Cultura,20,Benín,fr,False,30 +1796,Festival International des Arts du Bénin – News,Eventos culturales y artísticos.,https://fiab.bj/feed/,2,Cultura,20,Benín,fr,False,30 +1756,Jeune Afrique – Culture (Benin),Cultura africana y del país.,https://www.jeuneafrique.com/feed/,2,Cultura,20,Benín,fr,True,0 +1795,Musées du Bénin – Actualités,Museos y eventos culturales.,https://musees.bj/feed/,2,Cultura,20,Benín,fr,False,30 +1792,ORTB – Culture,Cultura y patrimonio en la televisión pública.,https://ortb.bj/category/culture/feed/,2,Cultura,20,Benín,fr,False,30 +1696,AfrikMag – Sports Afrique,Cobertura deportiva africana.,https://www.afrikmag.com/feed/,3,Deportes,20,Benín,fr,True,0 +1800,Benin Web TV – Sports,Noticias deportivas del principal portal digital.,https://beninwebtv.com/category/sport/feed/,3,Deportes,20,Benín,fr,False,30 +1804,Bénin Intelligent – Sports,Deporte nacional y resultados locales.,https://beninintelligent.com/category/sport/feed/,3,Deportes,20,Benín,fr,True,0 +1805,Crystal News – Sports,"Deportes, ligas y eventos locales.",https://www.crystal-news.com/category/sport/feed/,3,Deportes,20,Benín,fr,False,30 +1806,Frissons Media – Sports,Programas y coberturas deportivas en radio.,https://frissonsmedia.bj/category/sport/feed/,3,Deportes,20,Benín,fr,False,30 +1801,La Nation – Sports,Sección deportiva del diario oficial.,https://lanation.bj/category/sport/feed/,3,Deportes,20,Benín,fr,False,30 +1698,VOA Afrique – Sports,Resultados y análisis deportivos africanos.,https://www.voaafrique.com/rss,3,Deportes,20,Benín,fr,False,30 +1744,24h au Bénin – Économie,"Economía, emprendimiento, finanzas.",https://24haubenin.info/spip.php?page=backend&mot=11,4,Economía,20,Benín,fr,True,0 +1749,Actu Bénin – Économie,"Economía, inflación, proyectos de desarrollo.",https://actubenin.com/category/economie/feed/,4,Economía,20,Benín,fr,False,30 +1745,Benin Intelligent – Économie,Análisis económico y noticias financieras.,https://beninintelligent.com/category/economie/feed/,4,Economía,20,Benín,fr,True,0 +1684,Benin Web TV – Économie,Economía nacional del principal medio digital.,https://beninwebtv.com/category/economie/feed/,4,Economía,20,Benín,fr,False,30 +1752,Chambre de Commerce et d’Industrie du Bénin – News,"Empresas, industria y comercio.",https://cci.bj/feed/,4,Economía,20,Benín,fr,False,30 +1746,Crystal News – Économie,"Economía regional, empresas locales.",https://www.crystal-news.com/category/economie/feed/,4,Economía,20,Benín,fr,False,30 +1689,Le Journal de l’Économie du Bénin,El periódico económico más importante del país.,https://leconomistedubenin.com/feed/,4,Economía,20,Benín,fr,False,30 +1750,Ministère de l’Économie et des Finances – Bénin,Comunicados oficiales del ministerio (RSS general).,https://finances.bj/feed/,4,Economía,20,Benín,fr,True,0 +1751,Port Autonome de Cotonou – Nouvelles,Logística portuaria y comercio exterior.,https://www.portcotonou.com/feed/,4,Economía,20,Benín,fr,False,30 +1734,Actu Bénin – Politique,Portal de noticias generales con categoría política.,https://actubenin.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1736,Africa24 – Benin Politics,"Televisión panafricana, sección Benín.",https://africa24tv.com/feed/,11,Política,20,Benín,fr,True,0 +1735,Benin Times – Politique,Noticias políticas actualizadas.,https://benintimes.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1683,Benin Web TV – Politique,Sección política del principal medio digital del país.,https://beninwebtv.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1727,Crystal News – Politique,Actualidad política y comunicados oficiales.,https://www.crystal-news.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1729,Frissons Radio – Politique,Cobertura política en radio y medios digitales.,https://frissonsmedia.bj/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1733,Le Journal de l’Économie du Bénin – Politique,Relación entre política y economía.,https://leconomistedubenin.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1730,Libre Express – Politique,Medio joven centrado en la crítica política.,https://www.libreexpress.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1687,Matin Libre – Politique,"Política nacional, gobierno y elecciones.",https://matinlibre.com/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1728,ORTB – Politique,Noticias políticas desde la TV pública nacional.,https://ortb.bj/category/politique/feed/,11,Política,20,Benín,fr,False,30 +1699,UN News – Benin Politics,Política institucional y desarrollo gobernanza.,https://news.un.org/feed/country/ben,11,Política,20,Benín,fr,True,0 +1685,24h au Bénin – Actualités,Medio digital con noticias nacionales.,https://24haubenin.info/?rss,13,Sociedad,20,Benín,fr,False,30 +1783,24h au Bénin – Société,"Noticias sociales, educación, cultura local.",https://24haubenin.info/spip.php?page=backend&mot=5,13,Sociedad,20,Benín,fr,True,0 +1789,Actu Bénin – Société,Historias sociales y vida comunitaria.,https://actubenin.com/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1690,Banouto – Actualités,Portal digital independiente.,https://www.banouto.info/feed/,13,Sociedad,20,Benín,fr,False,30 +1788,Banouto – Société,Investigación social y reportajes culturales.,https://www.banouto.info/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1682,Benin Web TV – Actualités,Medio digital de noticias generales.,https://beninwebtv.com/feed/,13,Sociedad,20,Benín,fr,False,30 +1782,Benin Web TV – Société,Sección social del mayor portal digital.,https://beninwebtv.com/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1688,Bénin Intelligent – Actualités,Noticias generales y análisis.,https://beninintelligent.com/feed/,13,Sociedad,20,Benín,fr,True,0 +1784,Bénin Intelligent – Société,"Vida cotidiana, cultura, sociedad civil.",https://beninintelligent.com/category/societe/feed/,13,Sociedad,20,Benín,fr,True,0 +1693,Crystal News – Bénin,Actualidad general del país.,https://www.crystal-news.com/feed/,13,Sociedad,20,Benín,fr,False,30 +1785,Crystal News – Société,"Cultura, sociedad y tendencias locales.",https://www.crystal-news.com/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1786,Frissons Media – Société,Noticias sociales y culturales en radio.,https://frissonsmedia.bj/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1694,Frissons Radio – Bénin,"Radio conocida, noticias en francés.",https://frissonsmedia.bj/feed/,13,Sociedad,20,Benín,fr,False,30 +1681,La Nation Bénin – Actualités,El diario oficial del gobierno.,https://lanation.bj/feed/,13,Sociedad,20,Benín,fr,False,30 +1780,La Nation – Société,"Noticias sociales, vida comunitaria, cultura.",https://lanation.bj/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1692,Le Potentiel – Actualités,Periódico nacional muy leído.,https://lepotentiel.bj/feed/,13,Sociedad,20,Benín,fr,True,0 +1787,Le Potentiel – Société,Temas sociales y comunitarios en diario nacional.,https://lepotentiel.bj/category/societe/feed/,13,Sociedad,20,Benín,fr,True,0 +1686,Matin Libre – News,Periódico digital importante del país.,https://matinlibre.com/feed/,13,Sociedad,20,Benín,fr,True,0 +1781,Matin Libre – Société,"Sociedad, cultura, vida urbana.",https://matinlibre.com/category/societe/feed/,13,Sociedad,20,Benín,fr,False,30 +1680,ORTB – Office de Radiodiffusion et Télévision du Bénin,Televisión y radio pública nacional.,https://ortb.bj/feed/,13,Sociedad,20,Benín,fr,False,30 +1691,Radio Tokpa – Société,Radio importante con contenidos culturales y sociales.,https://radiotokpa.com/feed/,13,Sociedad,20,Benín,fr,False,30 +1763,Agence Béninoise d'Électrification Rurale – Innovations,Tecnología energética y desarrollo.,https://aberme.bj/feed/,14,Tecnología,20,Benín,fr,False,30 +1766,Matin Libre – Technologie,"Tecnología local, telecomunicaciones.",https://matinlibre.com/category/technologie/feed/,14,Tecnología,20,Benín,fr,False,30 +1876,AgriScience Belarus – Agricultura científica,"Agricultura, agrobiotecnología.",https://belagro.by/rss,1,Ciencia,21,Bielorrusia,ru,False,30 +1863,Belarusian State University – Research,Noticias científicas universitarias.,https://www.bsu.by/en/rss/,1,Ciencia,21,Bielorrusia,en,False,30 +1865,Belta – Science & Tech (EN),"Ciencia estatal, TI, innovación.",https://eng.belta.by/scitech/rss,1,Ciencia,21,Bielorrusia,en,False,30 +1866,Belta – Наука и технологии (RU),Ciencia oficial en ruso.,https://www.belta.by/science/rss,1,Ciencia,21,Bielorrusia,ru,False,30 +1879,Eurasian Science & Tech – Belarus Section,Ciencia y tecnología del espacio euroasiático.,https://euroasia-science.ru/feed/,1,Ciencia,21,Bielorrusia,ru,False,30 +1875,Gismeteo Belarus – Climate Science,"Meteorología, clima y data science atmosférica.",https://www.gismeteo.by/news/rss/,1,Ciencia,21,Bielorrusia,ru,False,5 +1867,Nasha Niva – Science,Ciencia e innovación en bielorruso.,https://nashaniva.com/rss/navuka,1,Ciencia,21,Bielorrusia,be,False,30 +1864,National Academy of Sciences of Belarus – News,"Academia de ciencias: física, química, biología.",https://nasb.gov.by/rss.php,1,Ciencia,21,Bielorrusia,ru,False,30 +1871,Radio Svaboda (RFE/RL) – Science,"Ciencia, salud y medio ambiente en BY.",https://www.svaboda.org/api/zipit$pps,1,Ciencia,21,Bielorrusia,be,False,30 +1874,Science.by – Research Updates,Resultados científicos y proyectos estatales.,https://scienceportal.org.by/rss,1,Ciencia,21,Bielorrusia,ru,False,30 +1878,WHO Europe – Belarus Health Science,Epidemiología y ciencia médica.,https://www.who.int/feeds/entity/countries/blr/rss.xml,1,Ciencia,21,Bielorrusia,en,False,30 +1895,Belarusian Theatre Union – News,"Teatro, festivales, artes escénicas.",https://tuz.by/feed/,2,Cultura,21,Bielorrusia,ru,False,30 +1889,Belsat TV – Culture,"Cultura nacional, música, artes escénicas.",https://belsat.eu/be/tag/kultura/feed/,2,Cultura,21,Bielorrusia,be,False,30 +1827,Euroradio – Culture,"Cultura, música alternativa y arte.",https://euroradio.fm/en/rss,2,Cultura,21,Bielorrusia,en,False,30 +1896,National Library of Belarus – Culture,"Biblioteca nacional, cultura escrita.",https://www.nlb.by/rss/,2,Cultura,21,Bielorrusia,ru,False,30 +1894,State Museum of History of Belarus – News,"Museos, historia, patrimonio.",https://histmuseum.by/feed/,2,Cultura,21,Bielorrusia,ru,False,30 +1891,TUT.by (Exilio) – Culture,Sección cultural histórica del portal.,https://news.tut.by/rss/section/culture.html,2,Cultura,21,Bielorrusia,ru,False,30 +1848,Belsat TV – Economy,Economía nacional y efectos de sanciones.,https://belsat.eu/ru/news/feed/,4,Economía,21,Bielorrusia,ru,False,5 +1856,Eurasia Daily – Belarus Economy,Geoeconomía y comercio del espacio ruso.,https://eadaily.com/ru/news/rss,4,Economía,21,Bielorrusia,ru,False,30 +1853,Eurasian Economic Union – Belarus Section,Noticias económicas de la Unión Euroasiática.,https://eec.eaeunion.org/feed,4,Economía,21,Bielorrusia,ru,False,30 +1854,IMF – Belarus Reports,Informes económicos formales.,https://www.imf.org/external/country/blr/index.htm,4,Economía,21,Bielorrusia,en,False,30 +1851,Myfin.by – Finance News,"Finanzas, créditos, bancos, tipos de cambio.",https://myfin.by/rss,4,Economía,21,Bielorrusia,ru,True,0 +1846,Nasha Niva – Economy,"Economía en bielorruso, análisis del mercado.",https://nashaniva.com/rss/ekanomika,4,Economía,21,Bielorrusia,be,False,30 +1850,Naviny.online – Economy,"Economía, empresas, inflación, comercio.",https://naviny.online/economics/rss,4,Economía,21,Bielorrusia,ru,False,30 +1858,RFE/RL Belarus Service – Economy,Impacto de sanciones y economía paralela.,https://www.svaboda.org/api/zipit$ppm,4,Economía,21,Bielorrusia,be,False,30 +1857,Russian Economic Gazette – Belarus,Impacto económico ruso en Bielorrusia.,https://www.economy.gov.ru/rss/,4,Economía,21,Bielorrusia,ru,False,30 +1845,TUT.by (Exilio) – Economy,Histórico portal opositor (RSS económico manteniéndose).,https://news.tut.by/rss/section/finance.html,4,Economía,21,Bielorrusia,ru,False,30 +1855,World Bank – Belarus,"Economía, desarrollo, indicadores macro.",https://www.worldbank.org/en/country/belarus/rss,4,Economía,21,Bielorrusia,en,False,30 +1852,Банковский журнал Беларусь – Finance,Sector bancario y financiero.,https://www.kfjournal.by/rss,4,Economía,21,Bielorrusia,ru,False,30 +1837,UN News – Belarus,Información institucional ONU.,https://news.un.org/feed/country/blr,7,Internacional,21,Bielorrusia,en,True,0 +1838,Charter97 – Новости Belarus,Portal de oposición política histórica.,https://charter97.org/ru/news/rss/,11,Política,21,Bielorrusia,ru,False,30 +1890,Belsat TV – Society,"Sociedad, identidad nacional y tradiciones.",https://belsat.eu/be/tag/gramadstva/feed/,13,Sociedad,21,Bielorrusia,be,False,30 +1880,Belta – Society (EN),"Sociedad oficial: eventos, vida social.",https://eng.belta.by/society/rss,13,Sociedad,21,Bielorrusia,en,False,30 +1882,Belta – Грамадства (BY),Versión bielorrusa de sociedad.,https://blr.belta.by/society/rss,13,Sociedad,21,Bielorrusia,be,False,30 +1881,Belta – Общество (RU),Sección social oficial en ruso.,https://www.belta.by/society/rss,13,Sociedad,21,Bielorrusia,ru,False,30 +1888,Euroradio – Грамадства,Sección social en bielorruso.,https://euroradio.fm/gramadstva/rss,13,Sociedad,21,Bielorrusia,be,False,30 +1826,Nasha Niva – News,Periódico independiente en bielorruso.,https://nashaniva.com/rss,13,Sociedad,21,Bielorrusia,be,False,30 +1883,Nasha Niva – Society,"Sociedad civil, cultura y lengua.",https://nashaniva.com/rss/gramadstva,13,Sociedad,21,Bielorrusia,be,False,30 +1892,Naviny.online – Society,Temas sociales desde medio independiente.,https://naviny.online/society/rss,13,Sociedad,21,Bielorrusia,ru,False,30 +1893,Onliner.by – People & Lifestyle,"Estilo de vida, cultura urbana.",https://people.onliner.by/feed,13,Sociedad,21,Bielorrusia,ru,True,0 +1825,Reform.by – Belarus News,Medio independiente en exilio (actualizado).,https://reform.by/feed,13,Sociedad,21,Bielorrusia,ru,False,30 +1886,Reform.by – Society,Medio independiente: temas sociales.,https://reform.by/category/society/feed,13,Sociedad,21,Bielorrusia,ru,False,30 +1836,VOA Russian – Society Belarus,"Sociedad, derechos humanos, cultura.",https://www.golosameriki.com/api/,13,Sociedad,21,Bielorrusia,ru,True,0 +1885,Zerkalo.io – Society,"Historias humanas, vida cotidiana.",https://news.zerkalo.io/society/rss-full.xml,13,Sociedad,21,Bielorrusia,ru,False,30 +1873,Belarus IT Union – News,"Asociación TI, startups, outsourcing.",https://itunion.by/feed/,14,Tecnología,21,Bielorrusia,ru,False,30 +1872,Belsat TV – Tech,Tecnología y avances digitales.,https://belsat.eu/ru/tag/tehnologii/feed/,14,Tecnología,21,Bielorrusia,ru,False,5 +1861,Dev.by – IT News,"Portal IT independiente, central en la comunidad.",https://dev.by/feed,14,Tecnología,21,Bielorrusia,ru,False,30 +1862,High-Tech Park Belarus – News,Parque tecnológico nacional (HTP).,https://www.htp.by/news?rss,14,Tecnología,21,Bielorrusia,en,False,30 +1860,Onliner.by – Tech,"El mayor medio tech del país: gadgets, IT, startups.",https://tech.onliner.by/feed,14,Tecnología,21,Bielorrusia,ru,True,0 +1869,Reform.by – Technology,Tecnología y digitalización desde medio independiente.,https://reform.by/category/technology/feed,14,Tecnología,21,Bielorrusia,ru,False,30 +1868,Zerkalo.io – Technology,"Tecnología independiente, reportajes críticos.",https://news.zerkalo.io/tech/rss-full.xml,14,Tecnología,21,Bielorrusia,ru,False,30 +1919,Asian Development Bank – Myanmar,"Economía, desarrollo, informes.",https://www.adb.org/rss/feed,4,Economía,22,Birmania,en,False,30 +1913,France24 – Myanmar,Cobertura internacional.,https://www.france24.com/en/tag/myanmar/rss,7,Internacional,22,Birmania,en,True,0 +1915,Reuters – Myanmar,Cobertura global de Reuters.,https://www.reuters.com/rssFeed/worldNews,7,Internacional,22,Birmania,en,False,30 +1916,UN News – Myanmar,Noticias institucionales de Naciones Unidas.,https://news.un.org/feed/country/mmr,7,Internacional,22,Birmania,en,True,0 +1918,Amnesty International – Myanmar,Situación social y DDHH.,https://www.amnesty.org/en/location/asia-and-the-pacific/south-east-asia-and-the-pacific/myanmar/feed/,13,Sociedad,22,Birmania,en,True,0 +1911,BBC Burmese – News,BBC en birmano.,http://www.bbc.co.uk/burmese/index.xml,13,Sociedad,22,Birmania,my,True,0 +1903,DVB – Democratic Voice of Burma,Radio/TV opositora desde Noruega.,https://www.dvb.no/en/feed/,13,Sociedad,22,Birmania,en,False,30 +1905,Eleven Media Group – Myanmar News,Uno de los portales nacionales más grandes.,https://elevenmyanmar.com/rss.xml,13,Sociedad,22,Birmania,en,True,0 +1902,Frontier Myanmar – Latest News,Reportajes profundos sobre sociedad y política.,https://www.frontiermyanmar.net/en/feed/,13,Sociedad,22,Birmania,en,True,0 +1907,Global New Light of Myanmar – Official,Medio estatal oficial.,https://www.gnlm.com.mm/feed/,13,Sociedad,22,Birmania,en,True,0 +1917,Human Rights Watch – Myanmar,Derechos humanos y sociedad.,https://www.hrw.org/asia/myanmar/feed,13,Sociedad,22,Birmania,en,False,30 +1908,Ministry of Information – News,Noticias oficiales del gobierno.,https://www.moi.gov.mm/en/rss,13,Sociedad,22,Birmania,en,False,30 +1904,Mizzima News – English,Medio independiente con amplia cobertura.,https://mizzima.com/rss,13,Sociedad,22,Birmania,en,False,30 +1900,Myanmar Now – News,Uno de los medios independientes más importantes.,https://myanmar-now.org/en/feed,13,Sociedad,22,Birmania,en,True,0 +1906,Myanmar Times – News,Periódico histórico (RSS aún operativo).,https://www.mmtimes.com/feed,13,Sociedad,22,Birmania,en,False,30 +1909,RFA Burmese – News,Radio Free Asia en birmano.,https://www.rfa.org/burmese/rss2.xml,13,Sociedad,22,Birmania,my,True,0 +1910,RFA English – Myanmar,Cobertura internacional sobre Myanmar.,https://www.rfa.org/english/news/myanmar/rss2.xml,13,Sociedad,22,Birmania,en,True,0 +1901,The Irrawaddy – News,Medio independiente desde el exilio.,https://www.irrawaddy.com/feed,13,Sociedad,22,Birmania,en,True,0 +1912,VOA Burmese – News,Voz de América en birmano.,https://www.voaburmese.com/rss,13,Sociedad,22,Birmania,my,False,30 +2020,El Día – Economía,"Economía nacional, empresas y mercados desde Santa Cruz",https://eldia.com.bo/rss/5,4,Economía,23,Bolivia,es,False,30 +2025,Erbol – Economía,"Economía, sindicatos, empresas estatales y políticas productivas",https://erbol.com.bo/rss/economia.xml,4,Economía,23,Bolivia,es,False,30 +1923,La Razón – Economía,"Coyuntura económica, empresas y finanzas bolivianas",https://www.la-razon.com/rss/economia/,4,Economía,23,Bolivia,es,False,30 +1957,ANF – Internacional,Sección internacional de la Agencia de Noticias Fides,https://anf.bo/feed/,7,Internacional,23,Bolivia,es,False,30 +1944,BolPress – Internacional,Análisis político internacional y geopolítico desde Bolivia,https://www.bolpress.com/feed/,7,Internacional,23,Bolivia,es,False,30 +1956,Brújula Digital – Internacional,Reportajes internacionales relevantes para la política boliviana,https://brujuladigital.net/feed,7,Internacional,23,Bolivia,es,False,30 +1959,Cabildeo Digital – Internacional,Análisis internacional y relaciones diplomáticas,https://www.cabildeodigital.com/feeds/posts/default?alt=rss,7,Internacional,23,Bolivia,es,True,0 +2065,Correo del Sur – Mundo,Cobertura internacional actualizada desde Sucre,https://correodelsur.com/rss/7/mundo,7,Internacional,23,Bolivia,es,False,30 +2063,El Deber – Mundo,Cobertura de política internacional desde un medio boliviano,https://eldeber.com.bo/rss/mundo,7,Internacional,23,Bolivia,es,False,30 +2066,El Día – Mundo,Internacional desde la perspectiva informativa de Santa Cruz,https://eldia.com.bo/rss/6,7,Internacional,23,Bolivia,es,False,30 +2068,Erbol – Internacional,Noticias globales y geopolítica con impacto en Bolivia,https://erbol.com.bo/rss/internacional.xml,7,Internacional,23,Bolivia,es,False,30 +1952,La Estrella del Oriente – Internacional,Noticias internacionales con vínculo a Bolivia y la región,https://www.leo.bo/feed/,7,Internacional,23,Bolivia,es,True,0 +1922,La Razón – Mundo,Cobertura internacional desde la perspectiva boliviana,https://larazon.bo/rss/mundo/,7,Internacional,23,Bolivia,es,False,30 +2061,La Razón – Mundo,Cobertura internacional y diplomacia desde un enfoque boliviano,https://www.la-razon.com/rss/mundo/,7,Internacional,23,Bolivia,es,False,30 +2064,Los Tiempos – Mundo,Noticias globales con enfoque en impacto latinoamericano y boliviano,https://www.lostiempos.com/rss/mundo,7,Internacional,23,Bolivia,es,False,5 +1920,Los Tiempos – Titulares,Cobertura de titulares y noticias principales de Bolivia y del mundo,http://www.lostiempos.com/rss/lostiempos-titulares.xml,7,Internacional,23,Bolivia,es,False,5 +1945,NoticiasBolivia.org – Internacional,Agregador de noticias internacionales relacionadas con Bolivia,https://noticiasbolivia.org/rss-noticiasbo.html,7,Internacional,23,Bolivia,es,False,30 +1926,Opinión Bolivia – Mundo,Noticias internacionales relevantes para Bolivia y Latinoamérica,https://www.opinion.com.bo/rss/mundo/,7,Internacional,23,Bolivia,es,True,0 +1955,RimayPampa – Internacional,Reportajes de pueblos indígenas y relaciones internacionales,https://rimaypampa.org/feed/,7,Internacional,23,Bolivia,es,False,30 +1958,Sol de Pando – Internacional,Artículos sobre geopolítica amazónica y relaciones internacionales,https://www.soldepando.com/feed/,7,Internacional,23,Bolivia,es,True,0 +1931,Agencia Boliviana de Información – Nacional,Cobertura nacional con fuerte contenido político,https://abi.bo/index.php/noticias/nacional?format=feed&type=rss,11,Política,23,Bolivia,es,False,30 +1930,Agencia Boliviana de Información – Política,Noticias políticas nacionales de Bolivia,https://abi.bo/index.php/noticias/politica?format=feed&type=rss,11,Política,23,Bolivia,es,True,0 +1948,Correo del Sur – Política,Noticias políticas de Sucre y Bolivia,https://correodelsur.com/rss/1/politica,11,Política,23,Bolivia,es,False,30 +1937,El Deber – Política,"Cobertura política boliviana: gobierno, oposición, elecciones y análisis",https://eldeber.com.bo/rss/politica,11,Política,23,Bolivia,es,False,30 +1951,El Día – Nacional,"Noticias nacionales de Bolivia, política incluida",https://eldia.com.bo/rss/1,11,Política,23,Bolivia,es,False,30 +1940,Erbol – Nacional,Noticias nacionales bolivianas,https://erbol.com.bo/rss/nacional.xml,11,Política,23,Bolivia,es,False,30 +1939,Erbol – Política,"Radio Erbol: noticias políticas, gobierno y sociedad",https://erbol.com.bo/rss/politica.xml,11,Política,23,Bolivia,es,False,30 +1946,La Patria – Política,Noticias políticas desde Oruro,https://lapatria.bo/feed/,11,Política,23,Bolivia,es,True,0 +1921,La Razón – Nacional,Noticias nacionales de política y actualidad boliviana,https://larazon.bo/rss/nacional/,11,Política,23,Bolivia,es,False,30 +1925,Opinión Bolivia – El País,Actualidad política y social de Bolivia,https://www.opinion.com.bo/rss/pais/,11,Política,23,Bolivia,es,True,0 +1954,Oxígeno.bo – Política,"Noticias políticas, sociedad y análisis",https://www.oxigeno.bo/rss.xml,11,Política,23,Bolivia,es,True,0 +1953,Periódico Bolivia – Política,"Cobertura política, social y económica del país",https://www.bolivia.com/rss/noticias-politica.xml,11,Política,23,Bolivia,es,False,30 +1934,Página Siete – Nacional,Actualidad nacional boliviana (incluye política),https://www.paginasiete.bo/rss/nacional/,11,Política,23,Bolivia,es,False,30 +1933,Página Siete – Política,Noticias políticas de Bolivia con enfoque crítico e independiente,https://www.paginasiete.bo/rss/politica/,11,Política,23,Bolivia,es,False,30 +1943,Radio Fides – Noticias Nacionales,Medio tradicional con fuerte contenido político,https://radiofides.com/es/rss,11,Política,23,Bolivia,es,True,0 +1942,Red UNO – Noticias,Cobertura nacional con contenido político,https://www.reduno.com.bo/feed,11,Política,23,Bolivia,es,False,30 +1941,Unitel – Nacional,"Unitel: actualidad nacional, política y sociedad",https://unitel.bo/rss/feed.xml,11,Política,23,Bolivia,es,False,30 +1963,Visión 360 – Noticias,Noticias generales incluyendo política de Bolivia,https://www.vision360.bo/rss,11,Política,23,Bolivia,es,False,30 +1929,Opinión Bolivia – Salud,Noticias de salud pública bienestar y sistema sanitario boliviano,https://www.opinion.com.bo/rss/salud/,12,Salud,23,Bolivia,es,True,0 +1949,Correo del Sur – Sociedad,Sucesos cultura comunidades y vida social de Sucre y Bolivia,https://correodelsur.com/rss/2/sociedad,13,Sociedad,23,Bolivia,es,False,30 +1950,El Alteño – Sociedad,Actualidad social de El Alto comunidades y vida urbana,https://diarioelalteno.com.bo/feed,13,Sociedad,23,Bolivia,es,False,30 +1938,El Deber – País,Actualidad nacional sucesos y sociedad en Bolivia,https://eldeber.com.bo/rss/pais,13,Sociedad,23,Bolivia,es,False,30 +2049,El Día – Sociedad,Noticias sociales y comunitarias de Santa Cruz y Bolivia,https://eldia.com.bo/rss/3,13,Sociedad,23,Bolivia,es,False,30 +1947,El País Tarija – Sociedad,Sucesos y vida social en Tarija y Bolivia,https://elpais.bo/feed/,13,Sociedad,23,Bolivia,es,False,30 +2052,Erbol – Comunidad y Sociedad,Reportajes sociales pueblos indígenas educación y vida comunitaria,https://erbol.com.bo/rss/comunidad.xml,13,Sociedad,23,Bolivia,es,False,30 +1924,La Razón – Sociales,Eventos sociales y vida cotidiana de la sociedad boliviana,https://larazon.bo/rss/sociales/,13,Sociedad,23,Bolivia,es,False,30 +2044,La Razón – Sociedad,Noticias de sociedad cultura educación y vida cotidiana en Bolivia,https://www.la-razon.com/rss/sociedad/,13,Sociedad,23,Bolivia,es,False,30 +2046,Los Tiempos – Sociedad,Noticias sociales de Cochabamba y Bolivia comunidad y educación,https://www.lostiempos.com/rss/sociedad,13,Sociedad,23,Bolivia,es,False,5 +2045,Opinión Bolivia – Sociedad,Actualidad social comunidades sucesos y vida urbana en Bolivia,https://www.opinion.com.bo/rss/sociedad/,13,Sociedad,23,Bolivia,es,False,30 +2035,Erbol – Ciencia y Tecnología,Sección de Erbol dedicada a ciencia tecnología e innovación,https://erbol.com.bo/rss/ciencia-tecnologia.xml,14,Tecnología,23,Bolivia,es,False,30 +2031,Los Tiempos – Doble Click,Sección cultural con contenido frecuente sobre tecnología y cultura digital,https://www.lostiempos.com/rss/doble-click,14,Tecnología,23,Bolivia,es,False,5 +2030,Los Tiempos – Tecnología,Actualidad tecnológica tendencias e innovación desde Bolivia,https://www.lostiempos.com/rss/tendencias/tecnologia,14,Tecnología,23,Bolivia,es,False,5 +1928,Opinión Bolivia – Ciencia,Noticias científicas y avances tecnológicos de interés nacional,https://www.opinion.com.bo/rss/ciencia/,14,Tecnología,23,Bolivia,es,True,0 +1927,Opinión Bolivia – Tecnología,Noticias de tecnología innovación y ciencia aplicadas en Bolivia,https://www.opinion.com.bo/rss/tecnologia/,14,Tecnología,23,Bolivia,es,True,0 +2032,Rincón Tecnológico,Portal boliviano especializado en noticias de móviles aplicaciones y gadgets,https://rincontecnologico.com.bo/feed/,14,Tecnología,23,Bolivia,es,True,0 +2033,TecnoBit Bolivia,Revista digital boliviana sobre tecnología móviles gaming e innovación,https://revistatecnobit.com/feed/,14,Tecnología,23,Bolivia,es,True,0 +2034,True Tech Bolivia,Blog tecnológico boliviano sobre smartphones IA y tendencias digitales,https://ttt.com.bo/feed/,14,Tecnología,23,Bolivia,es,True,0 +2109,Akta.ba – Biznis,Principal base de datos empresarial con noticias económicas,https://www.akta.ba/rss,4,Economía,24,Bosnia y Herzegovina,bs,True,0 +2094,BHRT – Ekonomija,Economía nacional finanzas y mercado laboral,https://www.bhrt.ba/tag/ekonomija/feed/,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2104,BiznisInfo – Najnovije,Portal especializado en economía y empresas de Bosnia,https://www.biznisinfo.ba/feed/,4,Economía,24,Bosnia y Herzegovina,bs,True,0 +2107,Bljesak.info – Gospodarstvo,Sección de economía y negocios del portal de Mostar,https://bljesak.info/rss/gospodarstvo,4,Economía,24,Bosnia y Herzegovina,hr,False,30 +2102,Dnevni Avaz – Biznis,Sección económica del diario Avaz,https://avaz.ba/feed/category/biznis,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2111,Hayat.ba – Biznis,Noticias económicas y de emprendimiento del canal Hayat TV,https://hayat.ba/kategorija/biznis/feed/,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2108,Kapital.ba – Vijesti,Portal económico especializado en negocios y mercado,https://www.kapital.ba/feed/,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2077,Klix.ba – Biznis,Sección económica y empresarial del portal Klix,https://www.klix.ba/rss/biznis,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2110,Manager.ba – Ekonomija,Noticias de empresas management y finanzas,https://www.manager.ba/rss,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2088,Nezavisne Novine – Ekonomija,Noticias económicas empresas y finanzas,https://www.nezavisne.com/rss/Ekonomija,4,Economía,24,Bosnia y Herzegovina,bs,False,30 +2105,RTRS – Ekonomija,Sección económica del medio RTRS,https://lat.rtrs.tv/rss/ekonomija.php,4,Economía,24,Bosnia y Herzegovina,sr,False,30 +2129,BHRT – Svijet,Noticias internacionales de la televisión pública BHRT,https://www.bhrt.ba/tag/svijet/feed/,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2131,Bljesak.info – Svijet,Noticias globales desde el portal de Mostar,https://bljesak.info/rss/svijet,7,Internacional,24,Bosnia y Herzegovina,hr,False,30 +2081,Dnevni Avaz – Svijet,Noticias internacionales desde el mayor diario de Bosnia,https://avaz.ba/feed/category/svijet,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2098,Hayat.ba – Svijet,Sección internacional del canal Hayat TV,https://hayat.ba/kategorija/vijesti/svijet/feed/,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2130,Klix.ba – Svijet,Internacional del portal de noticias más leído del país,https://www.klix.ba/rss/svijet,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2087,Nezavisne Novine – Svijet,Sección internacional con análisis global,https://www.nezavisne.com/rss/Svijet,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2084,RTRS – Svijet,Internacional desde la cadena RTRS,https://lat.rtrs.tv/rss/svijet.php,7,Internacional,24,Bosnia y Herzegovina,sr,False,30 +2091,Radio Sarajevo – Svijet,Internacional desde el medio capitalino Radio Sarajevo,https://radiosarajevo.ba/rss/svijet,7,Internacional,24,Bosnia y Herzegovina,bs,True,0 +2099,Slobodna Bosna – Svijet,Análisis y noticias internacionales,https://www.slobodna-bosna.ba/feed,7,Internacional,24,Bosnia y Herzegovina,bs,False,30 +2093,BHRT – Politika,Política nacional desde la cadena pública BHRT,https://www.bhrt.ba/tag/politika/feed/,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2124,Bljesak.info – Politika,Política nacional desde el portal de Mostar,https://bljesak.info/rss/politika,11,Política,24,Bosnia y Herzegovina,hr,False,5 +2080,Dnevni Avaz – Politika,Sección política del diario Avaz con cobertura nacional,https://avaz.ba/feed/category/politika,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2123,Hayat.ba – Politika,Noticias políticas del canal Hayat TV,https://hayat.ba/kategorija/vijesti/politika/feed/,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2120,Klix.ba – Politika,Sección política del portal informativo Klix,https://www.klix.ba/rss/politika,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2086,Nezavisne Novine – Politika,Noticias políticas nacionales y regionales,https://www.nezavisne.com/rss/Politika,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2089,Oslobođenje – Politika,Política y actualidad del histórico diario Oslobođenje,https://www.oslobodjenje.ba/feed,11,Política,24,Bosnia y Herzegovina,bs,False,30 +2083,RTRS – Politika,Sección política del medio nacional RTRS,https://lat.rtrs.tv/rss/politika.php,11,Política,24,Bosnia y Herzegovina,sr,False,30 +2125,Radio Sarajevo – Politika,Sección política del medio capitalino,https://radiosarajevo.ba/rss/politika,11,Política,24,Bosnia y Herzegovina,bs,True,0 +2092,BHRT – Vijesti,Canal nacional de Bosnia y Herzegovina,https://www.bhrt.ba/feed/,13,Sociedad,24,Bosnia y Herzegovina,bs,False,30 +2095,Bljesak.info – Vijesti,Portal de Mostar con cobertura nacional e internacional,https://bljesak.info/feed,13,Sociedad,24,Bosnia y Herzegovina,hr,False,30 +2079,Dnevni Avaz – Vijesti,Diario Avaz con noticias nacionales e internacionales,https://avaz.ba/feed,13,Sociedad,24,Bosnia y Herzegovina,bs,False,30 +2097,Hayat.ba – Vijesti,Noticias generales y sociales de Bosnia,https://hayat.ba/feed/,13,Sociedad,24,Bosnia y Herzegovina,bs,False,30 +2076,Klix.ba – Najnovije,Portal líder en Bosnia con noticias de actualidad,https://www.klix.ba/rss,13,Sociedad,24,Bosnia y Herzegovina,bs,False,30 +2085,Nezavisne Novine – Najnovije,Periódico con noticias nacionales actualizadas,https://www.nezavisne.com/rss/1,13,Sociedad,24,Bosnia y Herzegovina,bs,False,30 +2082,RTRS – Najnovije,Vijesti del canal de la República Srpska,https://lat.rtrs.tv/rss/najnovije.php,13,Sociedad,24,Bosnia y Herzegovina,sr,False,30 +2090,Radio Sarajevo – Vijesti,Noticias de Sarajevo y del país,https://radiosarajevo.ba/rss/vijesti,13,Sociedad,24,Bosnia y Herzegovina,bs,True,0 +2096,Bljesak.info – Tehnologija,Tecnología y avances digitales en el país,https://bljesak.info/rss/tehnologija,14,Tecnología,24,Bosnia y Herzegovina,hr,False,30 +2078,Klix.ba – Tech,Sección de tecnología del portal Klix,https://www.klix.ba/rss/scitech,14,Tecnología,24,Bosnia y Herzegovina,bs,False,30 +2153,AllAfrica – Botswana Business,Actualidad económica empresas y mercados,https://allafrica.com/tools/headlines/rdf/botswana/business.rdf,4,Economía,25,Botsuana,en,False,30 +2144,Bank of Botswana – Press Releases,Comunicados oficiales sobre finanzas política monetaria y economía,https://www.bankofbotswana.bw/press-releases/feed,4,Economía,25,Botsuana,en,False,30 +2115,Botswana Guardian,Semanal independiente con cobertura de noticias políticas y económicas.,https://www.botswanaguardian.co.bw/rss,4,Economía,25,Botsuana,en,False,30 +2150,Africanews – Botswana,Noticias regionales con sección específica de Botsuana,https://www.africanews.com/tag/botswana/rss,7,Internacional,25,Botsuana,en,False,30 +2112,Botswana Daily News,Medio de comunicación estatal con noticias nacionales e internacionales de Botsuana.,http://www.dailynews.gov.bw/rss.php,7,Internacional,25,Botsuana,en,False,30 +2152,AllAfrica – Botswana Politics,Compilación de noticias políticas de Botsuana,https://allafrica.com/tools/headlines/rdf/botswana/politics.rdf,11,Política,25,Botsuana,en,False,30 +2141,BW Parliament – News,Noticias del Parlamento de Botsuana incluidos debates y proyectos de ley,https://www.parliament.gov.bw/news/feed,11,Política,25,Botsuana,en,False,30 +2140,Botswana Government – Announcements,Comunicados oficiales del Gobierno de Botsuana,https://www.gov.bw/announcements/feed,11,Política,25,Botsuana,en,False,30 +2138,Botswana Guardian – Politics,Sección política del Botswana Guardian,https://www.botswanaguardian.co.bw/news.feed?type=rss,11,Política,25,Botsuana,en,False,30 +2135,Daily News Botswana – Government,Actualidad gubernamental y política oficial,https://dailynews.gov.bw/rss,11,Política,25,Botsuana,en,False,30 +2113,Mmegi Online,Periódico independiente líder de Botsuana con noticias nacionales y análisis.,https://www.mmegi.bw/index.php?show=news&task=rssfeed,11,Política,25,Botsuana,en,False,30 +2136,Mmegi Online – Politics,Noticias políticas del principal diario independiente del país,https://www.mmegi.bw/rssFeed,11,Política,25,Botsuana,en,False,30 +2114,The Patriot on Sunday,Semanario con noticias políticas y de actualidad de Botsuana.,https://www.thepatriot.co.bw/rss,11,Política,25,Botsuana,en,False,30 +2137,The Patriot on Sunday – Politika,Noticias políticas del semanario Patriot,https://www.thepatriot.co.bw/feed/,11,Política,25,Botsuana,en,False,30 +2143,AllAfrica – Botswana News,Compilación diaria de noticias de Botsuana,https://allafrica.com/tools/headlines/rdf/botswana/headlines.rdf,13,Sociedad,25,Botsuana,en,True,0 +2139,The Voice BW – News,Noticias nacionales sociedad cultura y política,https://thevoicebw.com/feed/,13,Sociedad,25,Botsuana,en,True,0 +2154,AllAfrica – Botswana Technology,Tecnología innovación y digitalización en Botsuana,https://allafrica.com/tools/headlines/rdf/botswana/technology.rdf,14,Tecnología,25,Botsuana,en,False,30 +2142,BizTech Africa – Botswana,Tecnología y negocios en África con categoría específica para Botsuana,https://www.biztechafrica.com/tag/botswana/feed/,14,Tecnología,25,Botsuana,en,False,5 +2157,Botswana Tourism – Updates,Noticias culturales y turísticas oficiales,https://www.botswanatourism.co.bw/rss.xml,15,Viajes,25,Botsuana,en,False,30 +2199,BBC Brasil – Ciência e Saúde,Ciencia internacional traducida al portugués,https://www.bbc.com/portuguese/topics/cyx5krnw38vt/rss.xml,1,Ciencia,26,Brasil,pt,False,30 +2291,Brasil Escola – Ciência,Divulgación científica educación y biología,https://brasilescola.uol.com.br/rss.xml,1,Ciencia,26,Brasil,pt,False,30 +2385,Brasil de Fato – Ciência e Saúde,Ciencia social epidemiología salud y ambiente,https://www.brasildefato.com.br/rss/ciencia-e-saude,1,Ciencia,26,Brasil,pt,False,30 +2280,Canaltech – Ciência,Tecnología científica experimentos y hallazgos,https://canaltech.com.br/rss/,1,Ciencia,26,Brasil,pt,True,0 +2372,Estadão – Ciência,Reportajes de investigación y ciencia aplicada,https://www.estadao.com.br/rss/ciencia/,1,Ciencia,26,Brasil,pt,False,30 +2370,Folha – Ciência,Avances científicos investigación universitaria y salud,https://feeds.folha.uol.com.br/ciencia/rss091.xml,1,Ciencia,26,Brasil,pt,True,0 +2382,Instituto Butantan – Notícias,Investigación biomedica vacunas y ciencia,https://butantan.gov.br/rss,1,Ciencia,26,Brasil,pt,False,30 +2381,Instituto Fiocruz – Boletim,Investigación en salud pública y ciencia biomédica,https://portal.fiocruz.br/rss.xml,1,Ciencia,26,Brasil,pt,False,30 +2384,Nexo Jornal – Ciência,Periodismo explicativo de ciencia y medio ambiente,https://www.nexojornal.com.br/rss.xml?tema=ciencia,1,Ciencia,26,Brasil,pt,True,0 +2371,O Globo – Ciência,Divulgación científica clima biología astronomía,https://oglobo.globo.com/sociedade/ciencia/rss.xml,1,Ciencia,26,Brasil,pt,False,30 +2282,Olhar Digital – Ciência e Espaço,Noticias de astronomía física biología y espacio,https://olhardigital.com.br/feed/,1,Ciencia,26,Brasil,pt,True,0 +2379,Revista FAPESP,Ciencia universitaria y proyectos de investigación,https://revistapesquisa.fapesp.br/feed/,1,Ciencia,26,Brasil,pt,True,0 +2283,TecMundo – Ciência,Investigación científica física química y espacio,https://www.tecmundo.com.br/rss,1,Ciencia,26,Brasil,pt,False,30 +2331,Brasil de Fato – Cultura,Cultura popular comunidades arte y música,https://www.brasildefato.com.br/rss/cultura,2,Cultura,26,Brasil,pt,False,30 +2330,CartaCapital – Cultura,Crónicas análisis culturales y arte,https://www.cartacapital.com.br/categoria/cultura/feed/,2,Cultura,26,Brasil,pt,True,0 +2335,Correio Braziliense – Diversão & Arte,Agenda cultural cine música artes escénicas,https://www.correiobraziliense.com.br/rss/cdiversaoearte.xml,2,Cultura,26,Brasil,pt,False,30 +2334,Diário do Nordeste – Entretenimento,Cultura música cine y TV,https://diariodonordeste.verdesmares.com.br/entretenimento/rss,2,Cultura,26,Brasil,pt,False,30 +2325,Estadão – Cultura,Reportajes culturales eventos y espectáculos,https://www.estadao.com.br/rss/cultura/,2,Cultura,26,Brasil,pt,False,30 +2326,Estadão – Paladar,Gastronomía y cultura culinaria brasileña,https://paladar.estadao.com.br/rss/,2,Cultura,26,Brasil,pt,False,30 +2322,Folha – Guia da Folha,Agenda cultural de Sao Paulo y Brasil,https://feeds.folha.uol.com.br/guiadafolha/rss091.xml,2,Cultura,26,Brasil,pt,False,30 +2321,Folha – Ilustrada,Cultura arte celebridades y eventos,https://feeds.folha.uol.com.br/ilustrada/rss091.xml,2,Cultura,26,Brasil,pt,True,0 +2320,G1 – Cinema,Noticias de cine estrenos y festivales,https://g1.globo.com/rss/g1/cinema/,2,Cultura,26,Brasil,pt,True,0 +2318,G1 – Cultura,Noticias de arte música cine TV y celebridades,https://g1.globo.com/rss/g1/pop-arte/,2,Cultura,26,Brasil,pt,True,0 +2319,G1 – Música,Actualidad musical conciertos y lanzamientos,https://g1.globo.com/rss/g1/musica/,2,Cultura,26,Brasil,pt,True,0 +2332,GaúchaZH – Cultura,Espectáculos eventos y agenda cultural del sur de Brasil,https://gauchazh.clicrbs.com.br/cultura/rss.html,2,Cultura,26,Brasil,pt,False,30 +2336,Nexo Jornal – Cultura,Análisis cultural literatura y arte,https://www.nexojornal.com.br/rss.xml?tema=cultura,2,Cultura,26,Brasil,pt,True,0 +2323,O Globo – Cultura,Arte literatura cine TV y música,https://oglobo.globo.com/cultura/rss.xml,2,Cultura,26,Brasil,pt,True,0 +2324,O Globo – Revista Ela,Cultura pop moda estilo y tendencias,https://ela.oglobo.globo.com/rss.xml,2,Cultura,26,Brasil,pt,False,30 +2333,O Povo – Cultura,Agenda cultural del noreste brasileño,https://www.opovo.com.br/rss/cultura/,2,Cultura,26,Brasil,pt,True,0 +2338,Revista Bula – Cultura,Críticas literarias cine arte y reportajes culturales,https://www.revistabula.com/feed/,2,Cultura,26,Brasil,pt,True,0 +2342,Revista Continente – Cultura,Cultura brasileña y latinoamericana,https://revistacontinente.com.br/feed/,2,Cultura,26,Brasil,pt,False,30 +2339,Revista Quem – Celebridades,Entretenimiento cultura pop y eventos,https://revistaquem.globo.com/rss/ultimas/feed.xml,2,Cultura,26,Brasil,pt,False,30 +2340,Rolling Stone Brasil,Música cultura pop cine y entretenimiento,https://rollingstone.uol.com.br/rss/,2,Cultura,26,Brasil,pt,True,0 +2341,Teatro em Cena – Brasil,Noticias de teatro artes escénicas y espectáculos,https://teatroemcena.com.br/feed/,2,Cultura,26,Brasil,pt,False,30 +2328,UOL – Nossa,Lifestyle cultura y comportamiento,https://rss.uol.com.br/feed/nossa.xml,2,Cultura,26,Brasil,pt,False,30 +2327,UOL – Splash,Cultura pop celebridades TV cine y música,https://rss.uol.com.br/feed/splash.xml,2,Cultura,26,Brasil,pt,False,30 +2329,Veja – Cultura,Arte cine literatura y entretenimiento,https://veja.abril.com.br/feed/cultura/,2,Cultura,26,Brasil,pt,False,30 +2367,Brasil de Fato – Esportes,Fútbol popular deporte comunitario,https://www.brasildefato.com.br/rss/esportes,3,Deportes,26,Brasil,pt,False,30 +2365,CartaCapital – Esporte,Deporte y análisis social del deporte,https://www.cartacapital.com.br/categoria/esporte/feed/,3,Deportes,26,Brasil,pt,True,0 +2364,Correio Braziliense – Esportes,Noticias deportivas de Brasilia,https://www.correiobraziliense.com.br/rss/cesportes.xml,3,Deportes,26,Brasil,pt,False,30 +2363,Diário do Nordeste – Esportes,Deportes y fútbol del noreste,https://diariodonordeste.verdesmares.com.br/esportes/rss,3,Deportes,26,Brasil,pt,False,30 +2358,ESPN Brasil – Notícias,Noticias de ESPN Brasil,https://www.espn.com.br/feeds/rss,3,Deportes,26,Brasil,pt,False,30 +2355,Estadão – Esportes,Deportes nacionales e internacionales,https://www.estadao.com.br/rss/esportes/,3,Deportes,26,Brasil,pt,False,30 +2353,Folha – Esporte,Noticias deportivas de Folha de S.Paulo,https://feeds.folha.uol.com.br/esporte/rss091.xml,3,Deportes,26,Brasil,pt,True,0 +2343,G1 – Esportes,Sección deportiva principal de Globo,https://g1.globo.com/rss/g1/esportes/,3,Deportes,26,Brasil,pt,True,0 +2344,G1 – Futebol,Fútbol brasileño y mundial,https://g1.globo.com/rss/g1/futebol/,3,Deportes,26,Brasil,pt,True,0 +2345,G1 – Globo Esporte (GE),Portal líder en deportes en Brasil,https://ge.globo.com/rss/feeds/ge.xml,3,Deportes,26,Brasil,pt,False,30 +2347,GE – Corinthians,Noticias del Corinthians,https://ge.globo.com/rss/tudo-sobre/corinthians/,3,Deportes,26,Brasil,pt,False,30 +2346,GE – Flamengo,Noticias del Flamengo,https://ge.globo.com/rss/tudo-sobre/flamengo/,3,Deportes,26,Brasil,pt,False,30 +2350,GE – Grêmio,Noticias del Grêmio,https://ge.globo.com/rss/tudo-sobre/gremio/,3,Deportes,26,Brasil,pt,False,30 +2351,GE – Internacional,Noticias del Internacional,https://ge.globo.com/rss/tudo-sobre/internacional/,3,Deportes,26,Brasil,pt,False,30 +2348,GE – Palmeiras,Noticias del Palmeiras,https://ge.globo.com/rss/tudo-sobre/palmeiras/,3,Deportes,26,Brasil,pt,False,30 +2352,GE – Seleção Brasileira,Selección brasileña de fútbol,https://ge.globo.com/rss/tudo-sobre/selecao-brasileira/,3,Deportes,26,Brasil,pt,False,30 +2349,GE – São Paulo FC,Noticias del São Paulo,https://ge.globo.com/rss/tudo-sobre/sao-paulo/,3,Deportes,26,Brasil,pt,False,30 +2361,GaúchaZH – Esportes,Deportes del sur de Brasil,https://gauchazh.clicrbs.com.br/esportes/rss.html,3,Deportes,26,Brasil,pt,False,30 +2357,Lance! – Últimas Notícias,Uno de los mayores portales deportivos de Brasil,https://www.lance.com.br/rss/ultimas,3,Deportes,26,Brasil,pt,False,30 +2366,Nexo Jornal – Esporte,Periodismo explicativo deportivo,https://www.nexojornal.com.br/rss.xml?tema=esporte,3,Deportes,26,Brasil,pt,True,0 +2354,O Globo – Esportes,Sección deportiva del diario O Globo,https://oglobo.globo.com/esportes/rss.xml,3,Deportes,26,Brasil,pt,True,0 +2362,O Povo – Esportes,Portal del noreste brasileño,https://www.opovo.com.br/rss/esportes/,3,Deportes,26,Brasil,pt,False,30 +2359,Superesportes – Brasil,Deportes nacionales desde Minas Gerais,https://www.mg.superesportes.com.br/rss/,3,Deportes,26,Brasil,pt,False,30 +2360,Superesportes – FMF,Federación Mineira de Fútbol,https://www.mg.superesportes.com.br/rss/futebol-mineiro/,3,Deportes,26,Brasil,pt,False,30 +2356,UOL – Esporte,Portal UOL con noticias y ligas deportivas,https://rss.uol.com.br/feed/esporte.xml,3,Deportes,26,Brasil,pt,True,0 +2232,Agência Brasil – Economia,Agencia estatal de noticias económicas,https://agenciabrasil.ebc.com.br/rss/economia.xml,4,Economía,26,Brasil,pt,False,30 +2228,Bolsa de Valores B3 – Notícias,Actualizaciones de la bolsa de São Paulo,https://www.b3.com.br/pt_br/noticias/rss/,4,Economía,26,Brasil,pt,False,30 +2230,Brasil de Fato – Economia,Noticias económicas sociales con enfoque humano,https://www.brasildefato.com.br/rss/economia,4,Economía,26,Brasil,pt,False,30 +2234,Canal Rural – Agronegócio,Agronegocio agrícola ganadería y mercado rural,https://www.canalrural.com.br/feed/,4,Economía,26,Brasil,pt,True,0 +2192,Correio Braziliense – Economia,Evolución económica y política fiscal,https://www.correiobraziliense.com.br/rss/ceconomia.xml,4,Economía,26,Brasil,pt,False,30 +2239,Economia IG – Últimas,Noticias económicas del portal iG,https://economia.ig.com.br/rss.xml,4,Economía,26,Brasil,pt,True,0 +2182,Estadão – Economia,Noticias económicas del diario Estadão,https://www.estadao.com.br/rss/economia/,4,Economía,26,Brasil,pt,False,30 +2223,Exame – Economia,Estrategia empresarial macroeconomía y negocios,https://exame.com/feed/economia/,4,Economía,26,Brasil,pt,True,0 +2224,Exame – Negócios,Startups empresas innovación y mercados,https://exame.com/feed/negocios/,4,Economía,26,Brasil,pt,False,30 +2172,Folha de S.Paulo – Mercado,Economía finanzas macroeconomía y negocios,https://feeds.folha.uol.com.br/mercado/rss091.xml,4,Economía,26,Brasil,pt,True,0 +2167,G1 – Economia,Economía brasileña empresas impuestos y mercados,https://g1.globo.com/rss/g1/economia/,4,Economía,26,Brasil,pt,True,0 +2235,Globo Rural – Agronegócio,Economía agrícola y producción agropecuaria,https://g1.globo.com/rss/g1/globo-rural/,4,Economía,26,Brasil,pt,True,0 +2226,Investing.com – Brasil,Estrategias financieras y mercados globales,https://br.investing.com/rss/news_301.rss,4,Economía,26,Brasil,pt,True,0 +2227,MoneyTimes – Economia,Noticias del mercado financiero y Cripto,https://www.moneytimes.com.br/feed/,4,Economía,26,Brasil,pt,True,0 +2177,O Globo – Economia,Sección económica del diario Globo,https://oglobo.globo.com/economia/rss.xml,4,Economía,26,Brasil,pt,True,0 +2231,Poder360 – Economia,Análisis político-económico profesional,https://www.poder360.com.br/tag/economia/feed/,4,Economía,26,Brasil,pt,False,30 +2233,Revista IstoÉ Dinheiro,Estrategias negocios inversiones economía,https://istoedinheiro.com.br/feed/,4,Economía,26,Brasil,pt,True,0 +2238,Sunset – Economia e Mercado,Análisis económico financiero y mercados,https://sunset.com.br/feed/,4,Economía,26,Brasil,pt,False,30 +2186,UOL Notícias – Economia,Noticias macro y microeconómicas de Brasil,https://rss.uol.com.br/feed/noticias/economia.xml,4,Economía,26,Brasil,pt,False,30 +2222,Valor Econômico – Empresas,Noticias sobre empresas y negocios,https://valor.globo.com/rss/empresas/,4,Economía,26,Brasil,pt,False,30 +2221,Valor Econômico – Mercado,Economía y mercados financieros,https://valor.globo.com/rss/financas/,4,Economía,26,Brasil,pt,False,30 +2220,Valor Econômico – Últimas,Principal periódico económico de Brasil,https://valor.globo.com/rss/,4,Economía,26,Brasil,pt,False,30 +2311,Agência Brasil – Internacional,Agencia estatal con noticias del mundo,https://agenciabrasil.ebc.com.br/rss/internacional.xml,7,Internacional,26,Brasil,pt,False,30 +2304,BBC Brasil – Internacional,Cobertura internacional en portugués de la BBC,https://www.bbc.com/portuguese/topics/c32p4kj2z3nt/rss.xml,7,Internacional,26,Brasil,pt,False,30 +2312,Brasil de Fato – Internacional,Noticias internacionales con enfoque latinoamericano,https://www.brasildefato.com.br/rss/internacional,7,Internacional,26,Brasil,pt,False,30 +2307,CartaCapital – Internacional,Reportajes internacionales y geopolítica,https://www.cartacapital.com.br/categoria/internacional/feed/,7,Internacional,26,Brasil,pt,False,30 +2190,Correio Braziliense – Mundo,Cobertura diplomática y geopolítica,https://www.correiobraziliense.com.br/rss/cmundo.xml,7,Internacional,26,Brasil,pt,False,30 +2315,Diário do Nordeste – Mundo,Cobertura internacional y diplomacia,https://diariodonordeste.verdesmares.com.br/mundo/rss,7,Internacional,26,Brasil,pt,False,30 +2309,Estadão – Blogs Internacional,Opinión geopolítica de columnistas del Estadão,https://internacional.estadao.com.br/blogs/feed/,7,Internacional,26,Brasil,pt,False,30 +2183,Estadão – Internacional,Noticias del mundo desde Estadão,https://www.estadao.com.br/rss/internacional/,7,Internacional,26,Brasil,pt,False,30 +2173,Folha de S.Paulo – Mundo,Internacional y geopolítica desde Folha,https://feeds.folha.uol.com.br/mundo/rss091.xml,7,Internacional,26,Brasil,pt,True,0 +2168,G1 – Mundo,Noticias internacionales del mayor portal de Brasil,https://g1.globo.com/rss/g1/mundo/,7,Internacional,26,Brasil,pt,True,0 +2314,GaúchaZH – Mundo,Noticias globales desde el sur de Brasil,https://gauchazh.clicrbs.com.br/mundo/rss.html,7,Internacional,26,Brasil,pt,False,30 +2308,Nexo Jornal – Internacional,Periodismo explicativo internacional,https://www.nexojornal.com.br/rss.xml?tema=mundo,7,Internacional,26,Brasil,pt,True,0 +2178,O Globo – Mundo,Internacional del diario O Globo,https://oglobo.globo.com/mundo/rss.xml,7,Internacional,26,Brasil,pt,True,0 +2316,O Povo – Mundo,Noticias internacionales del noreste brasileño,https://www.opovo.com.br/rss/mundo/,7,Internacional,26,Brasil,pt,False,30 +2313,O Tempo – Mundo,Internacional desde Minas Gerais,https://www.otempo.com.br/rss/mundo,7,Internacional,26,Brasil,pt,False,30 +2317,Paraná Portal – Mundo,Internacional desde Paraná,https://paranaportal.uol.com.br/feed/mundo/,7,Internacional,26,Brasil,pt,False,30 +2305,R7 – Internacional,Noticias del mundo desde R7,https://noticias.r7.com/internacional/feed.xml,7,Internacional,26,Brasil,pt,False,30 +2187,UOL Notícias – Internacional,Noticias globales desde UOL,https://rss.uol.com.br/feed/noticias/internacional.xml,7,Internacional,26,Brasil,pt,False,30 +2310,Valor Econômico – Internacional,Economía internacional y mercados globales,https://valor.globo.com/rss/internacional/,7,Internacional,26,Brasil,pt,False,30 +2306,Veja – Mundo,Internacional análisis y geopolítica,https://veja.abril.com.br/feed/mundo/,7,Internacional,26,Brasil,pt,False,30 +2266,Agência Brasil – Meio Ambiente,Noticias ambientales del gobierno federal,https://agenciabrasil.ebc.com.br/rss/geral.xml,8,Medio Ambiente,26,Brasil,pt,False,30 +2392,BBC Brasil – Meio Ambiente,Clima y medio ambiente global en portugués,https://www.bbc.com/portuguese/topics/c340q0qx2p3t/rss.xml,8,Medio Ambiente,26,Brasil,pt,False,30 +2394,Brasil de Fato – Meio Ambiente,Reportajes sobre clima pueblos originarios y ecología,https://www.brasildefato.com.br/rss/meio-ambiente,8,Medio Ambiente,26,Brasil,pt,False,30 +2390,Estadão – Sustentabilidade,Sustentabilidade energía ambiente y clima,https://www.estadao.com.br/rss/sustentabilidade/,8,Medio Ambiente,26,Brasil,pt,False,30 +2388,Folha – Ambiente e Clima,Medio ambiente clima sustentabilidade y ecología,https://feeds.folha.uol.com.br/ambiente/rss091.xml,8,Medio Ambiente,26,Brasil,pt,True,0 +2368,G1 – Meio Ambiente,Reportagens ambientais ecologia e clima,https://g1.globo.com/rss/g1/ciencia-e-saude/,8,Medio Ambiente,26,Brasil,pt,True,0 +2244,G1 – Natureza,Clima biodiversidade amazônia queimadas e meio ambiente,https://g1.globo.com/rss/g1/natureza/,8,Medio Ambiente,26,Brasil,pt,True,0 +2380,Instituto Nacional de Pesquisas Espaciais (INPE),Clima meteorología incendios y monitoreo ambiental,https://www.inpe.br/noticias/rss.php,8,Medio Ambiente,26,Brasil,pt,False,30 +2397,Instituto Socioambiental (ISA),Conservación Amazonía pueblos indígenas y clima,https://www.socioambiental.org/pt-br/rss,8,Medio Ambiente,26,Brasil,pt,False,30 +2399,Mongabay Brasil – Meio Ambiente,Ambientalismo global y Amazonía,https://brasil.mongabay.com/feed/,8,Medio Ambiente,26,Brasil,pt,True,0 +2400,O Eco – Jornalismo Ambiental,Portal especializado en medio ambiente,https://oeco.org.br/feed/,8,Medio Ambiente,26,Brasil,pt,True,0 +2389,O Globo – Meio Ambiente,Clima conservación fauna y desastres naturales,https://oglobo.globo.com/sociedade/meio-ambiente/rss.xml,8,Medio Ambiente,26,Brasil,pt,False,30 +2398,Observatório do Clima,Noticias sobre cambio climático y políticas ambientales,https://www.oc.eco.br/feed/,8,Medio Ambiente,26,Brasil,pt,True,0 +2395,Projeto Colabora – Meio Ambiente,Periodismo ambiental investigación y sostenibilidad,https://projetocolabora.com.br/feed/,8,Medio Ambiente,26,Brasil,pt,True,0 +2254,UOL – VivaBem Ambiente,Contenido ambiental y clima en UOL,https://rss.uol.com.br/feed/vivabem.xml,8,Medio Ambiente,26,Brasil,pt,False,30 +2212,Agência Brasil – Política,Agencia estatal con cobertura institucional,https://agenciabrasil.ebc.com.br/rss/politica.xml,11,Política,26,Brasil,pt,False,30 +2204,Brasil de Fato – Política,Portal progresista con enfoque político,https://www.brasildefato.com.br/rss/politica,11,Política,26,Brasil,pt,False,30 +2210,Brasil247 – Política,Portal político con fuerte producción diaria,https://www.brasil247.com/c/poder/rss,11,Política,26,Brasil,pt,False,30 +2208,CartaCapital – Política,Opiniones y reportajes políticos,https://www.cartacapital.com.br/categoria/politica/feed/,11,Política,26,Brasil,pt,True,0 +2189,Correio Braziliense – Política,Política desde Brasilia y el Congreso,https://www.correiobraziliense.com.br/rss/cpolitica.xml,11,Política,26,Brasil,pt,False,30 +2214,Câmara dos Deputados – Notícias,Noticias del Congreso Diputados,https://www.camara.leg.br/noticias/rss/,11,Política,26,Brasil,pt,False,30 +2209,Diário do Poder – Política,Noticias políticas del diario especializado,https://diariodopoder.com.br/feed,11,Política,26,Brasil,pt,True,0 +2181,Estadão – Política,Noticias políticas del diario Estadão,https://www.estadao.com.br/rss/politica/,11,Política,26,Brasil,pt,False,30 +2202,Exame – Política,Análisis político del medio económico Exame,https://exame.com/feed/politica/,11,Política,26,Brasil,pt,False,30 +2171,Folha de S.Paulo – Poder,Sección política “Poder” de Folha,https://feeds.folha.uol.com.br/poder/rss091.xml,11,Política,26,Brasil,pt,True,0 +2166,G1 – Política,Sección política del mayor portal de Brasil,https://g1.globo.com/rss/g1/politica/,11,Política,26,Brasil,pt,True,0 +2207,Jovem Pan – Política,Radio y portal con cobertura política continua,https://jovempan.com.br/noticias/politica/feed,11,Política,26,Brasil,pt,True,0 +2206,Nexo Jornal – Política,Análisis profundo político,https://www.nexojornal.com.br/rss.xml?tema=politica,11,Política,26,Brasil,pt,True,0 +2176,O Globo – Política,Política nacional desde el diario O Globo,https://oglobo.globo.com/brasil/politica/rss.xml,11,Política,26,Brasil,pt,False,30 +2211,O Tempo – Política,Noticias políticas estatales y federales,https://www.otempo.com.br/rss/politica,11,Política,26,Brasil,pt,False,30 +2205,Poder360 – Política,Portal especializado exclusivamente en política,https://www.poder360.com.br/feed/,11,Política,26,Brasil,pt,True,0 +2200,R7 – Política,Portal R7 con enfoque en política institucional,https://noticias.r7.com/politica/feed.xml,11,Política,26,Brasil,pt,False,30 +2213,Senado Federal – Notícias,Noticias y comunicados del Senado brasileño,https://www12.senado.leg.br/noticias/ultimas/rss,11,Política,26,Brasil,pt,False,30 +2185,UOL Notícias – Política,Portal masivo con cobertura política nacional,https://rss.uol.com.br/feed/noticias/politica.xml,11,Política,26,Brasil,pt,False,30 +2203,Valor Econômico – Política,Política económica y gobierno,https://valor.globo.com/rss/politica/,11,Política,26,Brasil,pt,False,30 +2201,Veja – Política,Revista Veja sección política,https://veja.abril.com.br/feed/politica/,11,Política,26,Brasil,pt,False,30 +2267,Agência Brasil – Direitos Humanos,Derechos humanos sociedad y política social,https://agenciabrasil.ebc.com.br/rss/direitos-humanos.xml,13,Sociedad,26,Brasil,pt,False,30 +2258,BBC Brasil – Sociedade,Historias humanas social y cultural,https://www.bbc.com/portuguese/topics/cz74k717pw5t/rss.xml,13,Sociedad,26,Brasil,pt,False,30 +2268,Brasil de Fato – Sociedade,Periodismo social movimientos populares cultura y salud,https://www.brasildefato.com.br/rss/sociedade,13,Sociedad,26,Brasil,pt,False,30 +2262,CartaCapital – Sociedade,Análisis críticos sobre desigualdad salud y educación,https://www.cartacapital.com.br/categoria/sociedade/feed/,13,Sociedad,26,Brasil,pt,True,0 +2191,Correio Braziliense – Brasil,Noticias y reportajes sociales desde Brasilia,https://www.correiobraziliense.com.br/rss/cbrasil.xml,13,Sociedad,26,Brasil,pt,False,30 +2256,Correio Braziliense – Cidades,Noticias de ciudades tráfico educación y sociedad,https://www.correiobraziliense.com.br/rss/ccidades.xml,13,Sociedad,26,Brasil,pt,False,30 +2269,Diário do Nordeste – Cotidiano,Noticias sociales de Ceará,https://diariodonordeste.verdesmares.com.br/ultimas/rss,13,Sociedad,26,Brasil,pt,False,30 +2250,Estadão – Brasil,Noticias nacionales y temas sociales,https://www.estadao.com.br/rss/brasil/,13,Sociedad,26,Brasil,pt,False,30 +2252,Estadão – Comportamento,Conducta temas sociales y vida cotidiana,https://emais.estadao.com.br/rss/,13,Sociedad,26,Brasil,pt,False,30 +2251,Estadão – Saúde,Salud pública y ciencia aplicada,https://www.estadao.com.br/rss/saude/,13,Sociedad,26,Brasil,pt,False,30 +2257,Estadão – Sociedade,Artículos de sociedad comportamiento y reportajes,https://emais.estadao.com.br/rss/emais.xml,13,Sociedad,26,Brasil,pt,False,30 +2180,Estadão – Últimas,Diario histórico de São Paulo,https://www.estadao.com.br/rss/ultimas/,13,Sociedad,26,Brasil,pt,False,30 +2274,Extra – Geral,Sucesos sociedad y actualidad,https://extra.globo.com/rss/geral.xml,13,Sociedad,26,Brasil,pt,False,30 +2245,Folha de S.Paulo – Cotidiano,Sección de sociedad crimen transporte clima,https://feeds.folha.uol.com.br/cotidiano/rss091.xml,13,Sociedad,26,Brasil,pt,True,0 +2170,Folha de S.Paulo – Últimas,Uno de los diarios más importantes del país,https://feeds.folha.uol.com.br/emcimadahora/rss091.xml,13,Sociedad,26,Brasil,pt,True,0 +2246,Folha – Equilíbrio e Saúde,Salud pública bienestar y calidad de vida,https://feeds.folha.uol.com.br/equilibrioesaude/rss091.xml,13,Sociedad,26,Brasil,pt,True,0 +2241,G1 – Brasil,Actualidad nacional sucesos seguridad y sociedad,https://g1.globo.com/rss/g1/brasil/,13,Sociedad,26,Brasil,pt,True,0 +2242,G1 – Educação,Noticias del sistema educativo brasileño,https://g1.globo.com/rss/g1/educacao/,13,Sociedad,26,Brasil,pt,True,0 +2243,G1 – Saúde,Salud pública epidemias hospitales y bienestar,https://g1.globo.com/rss/g1/saude/,13,Sociedad,26,Brasil,pt,True,0 +2165,G1 – Últimas Notícias,Noticias generales de Brasil incluyendo sociedad salud educación y seguridad,https://g1.globo.com/rss/g1/,13,Sociedad,26,Brasil,pt,True,0 +2263,GaúchaZH – Notícias,Noticias sociales del sur de Brasil,https://gauchazh.clicrbs.com.br/geral/rss.html,13,Sociedad,26,Brasil,pt,False,30 +2273,JC Online – Cotidiano,Diario de Pernambuco con sección social,https://jc.ne10.uol.com.br/cotidiano/rss,13,Sociedad,26,Brasil,pt,False,30 +2270,NSC Total – Geral,Portal del estado de Santa Catarina,https://www.nsctotal.com.br/feed,13,Sociedad,26,Brasil,pt,False,30 +2265,Nexo Jornal – Sociedade,Periodismo explicativo social,https://www.nexojornal.com.br/rss.xml?tema=sociedade,13,Sociedad,26,Brasil,pt,True,0 +2247,O Globo – Brasil,Noticias sociales nacionales,https://oglobo.globo.com/brasil/rss.xml,13,Sociedad,26,Brasil,pt,True,0 +2249,O Globo – Rio,Noticias locales de Río de Janeiro,https://oglobo.globo.com/rio/rss.xml,13,Sociedad,26,Brasil,pt,True,0 +2248,O Globo – Sociedade,Comportamiento cultura seguridad y temas humanos,https://oglobo.globo.com/sociedade/rss.xml,13,Sociedad,26,Brasil,pt,False,30 +2175,O Globo – Últimas Notícias,Diario tradicional de Río de Janeiro,https://oglobo.globo.com/rss.xml,13,Sociedad,26,Brasil,pt,True,0 +2264,O Povo – Cotidiano,Portal del noreste brasileño con noticias locales,https://www.opovo.com.br/rss/ultimas/,13,Sociedad,26,Brasil,pt,False,30 +2271,O Tempo – Cidades,Noticias locales y sociedad en Minas Gerais,https://www.otempo.com.br/rss/cidades,13,Sociedad,26,Brasil,pt,False,30 +2272,Paraná Portal – Geral,Noticias sociales de Paraná,https://paranaportal.uol.com.br/feed/,13,Sociedad,26,Brasil,pt,False,30 +2259,R7 – Cidades,Sucesos locales tráfico clima y sociedad,https://noticias.r7.com/cidades/feed.xml,13,Sociedad,26,Brasil,pt,False,30 +2260,R7 – Saúde,Noticias de salud pública,https://noticias.r7.com/saude/feed.xml,13,Sociedad,26,Brasil,pt,False,30 +2253,UOL Notícias – Cotidiano,Sucesos crimen tránsito y sociedad,https://rss.uol.com.br/feed/noticias/cotidiano.xml,13,Sociedad,26,Brasil,pt,False,30 +2261,Veja – Sociedade,Temas sociales educación salud criminalidad,https://veja.abril.com.br/feed/sociedade/,13,Sociedad,26,Brasil,pt,False,30 +2285,Adrenaline – Hardware e Games,Tecnología hardware y gaming,https://adrenaline.com.br/feed/,14,Tecnología,26,Brasil,pt,True,0 +2292,Agência Brasil – Tecnologia,Ciencia tecnología e innovación del gobierno federal,https://agenciabrasil.ebc.com.br/rss/tecnologia.xml,14,Tecnología,26,Brasil,pt,False,30 +2296,Brasil de Fato – Tecnologia,Tecnología con enfoque social y de derechos digitales,https://www.brasildefato.com.br/rss/tecnologia,14,Tecnología,26,Brasil,pt,False,30 +2295,Dfndr Lab – Cibersegurança,Seguridad digital alertas virus y estafas,https://www.psafe.com/dfndr-lab/feed/,14,Tecnología,26,Brasil,pt,True,0 +2184,Estadão – Link,Sección oficial de tecnología startups e innovación,https://www.estadao.com.br/rss/link/,14,Tecnología,26,Brasil,pt,False,30 +2290,Exame – Tecnologia,Innovación IA ciencia y startups,https://exame.com/feed/tecnologia/,14,Tecnología,26,Brasil,pt,False,30 +2174,Folha – Tec,Sección tecnológica de Folha de S.Paulo,https://feeds.folha.uol.com.br/tec/rss091.xml,14,Tecnología,26,Brasil,pt,True,0 +2169,G1 – Tecnologia,Tecnología innovación internet seguridad digital,https://g1.globo.com/rss/g1/tecnologia/,14,Tecnología,26,Brasil,pt,True,0 +2225,InfoMoney – Tech e Cripto,Tecnología financiera blockchain y criptomonedas,https://www.infomoney.com.br/feed/,14,Tecnología,26,Brasil,pt,True,0 +2286,Manual do Usuário – Tecnologia,Análisis críticas y reportajes tecnológicos,https://manualdousuario.net/feed/,14,Tecnología,26,Brasil,pt,True,0 +2293,Mundo Conectado – Tecnologias,Tecnología de consumo y dispositivos,https://mundoconectado.com.br/rss/,14,Tecnología,26,Brasil,pt,True,0 +2237,Neofeed – Tech,Economía digital empresas tecnológicas e innovación,https://neofeed.com.br/feed/,14,Tecnología,26,Brasil,pt,True,0 +2179,O Globo – Tecnologia,Noticias tecnológicas ciencia e innovación,https://oglobo.globo.com/economia/tecnologia/rss.xml,14,Tecnología,26,Brasil,pt,True,0 +2294,SempreUpdate – Linux e Open Source,Portal brasileño de software libre,https://sempreupdate.com.br/feed/,14,Tecnología,26,Brasil,pt,False,30 +2236,Startups.com.br – Startups,Innovación startups inversiones y tecnología,https://startups.com.br/feed/,14,Tecnología,26,Brasil,pt,True,0 +2281,TechTudo – Últimas Notícias,Tecnología dispositivos streaming y apps,https://www.techtudo.com.br/feeds/rss.html,14,Tecnología,26,Brasil,pt,False,30 +2297,Techenet Brasil,Computación tecnología global y ciencia aplicada,https://www.techenet.com/feed/,14,Tecnología,26,Brasil,pt,True,0 +2284,TudoCelular – Notícias,Portal especializado en móviles y hardware,https://www.tudocelular.com/rss/,14,Tecnología,26,Brasil,pt,True,0 +2188,UOL Tilt – Tecnologia,Portal UOL dedicado a temas de tecnología y ciencia,https://rss.uol.com.br/feed/tecnologia/ultimas.xml,14,Tecnología,26,Brasil,pt,False,30 +2409,Menara – Brunei,Noticias culturales islámicas y sociedad,https://www.menara.my/tag/brunei/feed/,2,Cultura,27,Brunéi,en,True,0 +2404,ASEAN Briefing – Brunei,Economia política e inversiones en Brunéi,https://www.aseanbriefing.com/news/category/brunei/feed/,4,Economía,27,Brunéi,en,False,30 +2406,Energy & Industry Dept Brunei,Actualizaciones de energía industria y economía,https://www.energy.gov.bn/Lists/Announcements/Posts.aspx?rss=1,4,Economía,27,Brunéi,en,False,30 +2407,Asia News Network – Brunei,Noticias regionales centradas en Brunéi,https://asianews.network/topic/brunei/feed/,7,Internacional,27,Brunéi,en,False,30 +2410,The Independent SG – Brunei,Cobertura internacional con sección de Brunéi,https://theindependent.sg/tag/brunei/feed/,7,Internacional,27,Brunéi,en,True,0 +2412,EIN News – Brunei Government & Politics News,Agregador internacional de noticias políticas de Brunei,https://world.einnews.com/news/brunei-government-politics,11,Política,27,Brunéi,en,False,30 +2411,Prime Minister’s Office – News,Comunicados oficiales del Gobierno de Brunei,https://www.pmo.gov.bn/SitePages/All%20News.aspx,11,Política,27,Brunéi,en,False,30 +2403,AllAsia – Brunei News,Compilación de noticias recientes exclusivamente sobre Brunéi,https://www.theasianaffairs.com/category/brunei/feed/,13,Sociedad,27,Brunéi,en,True,0 +2401,Borneo Bulletin – News,Principal medio de Brunéi con noticias nacionales,https://borneobulletin.com.bn/feed/,13,Sociedad,27,Brunéi,en,False,30 +2408,MindaNews – Brunei,Noticias de Brunéi y Borneo,https://www.mindanews.com/tag/brunei/feed/,13,Sociedad,27,Brunéi,en,True,0 +2405,The Scoop – Brunei News,Portal informativo independiente sobre Brunéi,https://thescoop.co/feed/,13,Sociedad,27,Brunéi,en,True,0 +2402,Brunei Tourism – Updates,Noticias culturales y turísticas oficiales,https://www.bruneitourism.com/feed/,15,Viajes,27,Brunéi,en,True,0 +2452,Archaeology in Bulgaria,Cultura e historia arqueológica en Bulgaria,https://archaeologyinbulgaria.com/feed/,2,Cultura,28,Bulgaria,en,False,30 +2453,The Sofia Globe – Culture,Arte eventos y cultura búlgara,https://www.sofiaglobe.com/category/leisure/culture/feed/,2,Cultura,28,Bulgaria,en,True,0 +2458,Capital.bg – Economía,Análisis económico y financiero del diario Capital,https://www.capital.bg/rss/,4,Economía,28,Bulgaria,bg,True,0 +2421,SeeNews – Bulgaria Business & Economy,Economía empresas y mercados financieros en Bulgaria,https://seenews.com/feed,4,Economía,28,Bulgaria,en,False,30 +2422,Balkan Insight – Bulgaria Politics & Society,Portal internacional con análisis político y social de Bulgaria,https://www.balkaninsight.com/category/bi/bulgaria/bulgaria-politics-and-society/feed/,11,Política,28,Bulgaria,en,False,30 +2451,Euractiv – Bulgaria,Política europea análisis y actualidad búlgara,https://www.euractiv.com/section/politics/feed/,11,Política,28,Bulgaria,en,False,30 +2420,Government of Bulgaria – News,Anuncios oficiales del Gobierno búlgaro,https://www.me.government.bg/en/pages/rss-feed-65.html,11,Política,28,Bulgaria,en,False,30 +2419,Parliament of Bulgaria – News,Comunicados oficiales del Parlamento de Bulgaria,https://old.parliament.bg/en/rss,11,Política,28,Bulgaria,en,False,30 +2446,BGNES – News (Agencia privada),Agencia privada búlgara con noticias nacionales e internacionales,https://www.bgnes.bg/rss/,13,Sociedad,28,Bulgaria,bg,False,30 +2447,BNR – Bulgarian National Radio – News,Noticias nacionales y culturales desde la radio pública,https://bnr.bg/en/rss,13,Sociedad,28,Bulgaria,en,False,30 +2448,BNR – Horizont,Actualidad social y política en búlgaro,https://bnr.bg/rss/post/100,13,Sociedad,28,Bulgaria,bg,False,30 +2449,BNR – Regional News,Noticias regionales y locales de toda Bulgaria,https://bnr.bg/en/rss?c=4185,13,Sociedad,28,Bulgaria,en,False,30 +2414,BTA – Noticias en búlgaro,Agencia nacional (versión búlgara) con actualidad del país,https://bta.bg/bg/rss/free,13,Sociedad,28,Bulgaria,bg,True,0 +2413,Bulgarian News Agency (BTA) – English News,Agencia nacional búlgara en inglés con actualidad general,https://www.old.bta.bg/en/page/110,13,Sociedad,28,Bulgaria,en,False,30 +2417,Dnevnik.bg – RSS,Diario búlgaro con cobertura nacional y análisis,https://www.dnevnik.bg/rss/,13,Sociedad,28,Bulgaria,bg,True,0 +2415,Novinite.com – News,Portal independiente en inglés con noticias actualizadas,https://www.novinite.com/services/news_rss.php,13,Sociedad,28,Bulgaria,en,False,30 +2418,Standart News – RSS,Portal 24h de Bulgaria con noticias diversas,https://www.standartnews.com/rss?p=1,13,Sociedad,28,Bulgaria,en,True,0 +2450,The Mayor EU – Bulgaria Section,Noticias municipales de ciudades búlgaras,https://www.themayor.eu/en/a/rss-tag/bulgaria,13,Sociedad,28,Bulgaria,en,False,30 +2416,The Sofia Globe – Bulgaria News,Medio en inglés centrado en Bulgaria con política economía e internacional,https://www.sofiaglobe.com/category/news/feed/,13,Sociedad,28,Bulgaria,en,True,0 +2456,ComputerWorld Bulgaria,Tecnología ciberseguridad y negocios TI,https://computerworld.bg/rss/,14,Tecnología,28,Bulgaria,bg,False,30 +2457,Investor.bg – Tecnología,Noticias tecnológicas y de startups,https://www.investor.bg/rss/?section=technology,14,Tecnología,28,Bulgaria,bg,False,30 +2455,TechNews.bg,Tecnología TI en Bulgaria,https://technews.bg/feed/,14,Tecnología,28,Bulgaria,bg,True,0 +2454,Travel Bulgaria – Travel News,Turismo cultura y viajes internos,https://traveltobulgaria.com/feed/,15,Viajes,28,Bulgaria,en,True,0 +2464,AllAfrica – Burkina Faso Business,Noticias económicas y financieras,https://allafrica.com/tools/headlines/rdf/burkinafaso/business.rdf,4,Economía,29,Burkina Faso,fr,False,30 +2465,AllAfrica – Burkina Faso Agriculture,Agricultura clima desarrollo rural,https://allafrica.com/tools/headlines/rdf/burkinafaso/agriculture.rdf,8,Medio Ambiente,29,Burkina Faso,fr,False,30 +2471,Africanews – Burkina Faso Politics,Actualidad política regional relacionada con Burkina,https://www.africanews.com/tag/burkina-faso/rss,11,Política,29,Burkina Faso,en,False,30 +2463,AllAfrica – Burkina Faso Politics,Noticias políticas de Burkina Faso,https://allafrica.com/tools/headlines/rdf/burkinafaso/politics.rdf,11,Política,29,Burkina Faso,fr,False,30 +2466,Sahelien.com – Burkina Faso Politics,Cobertura política y seguridad en Burkina Faso,https://sahelien.com/feed/,11,Política,29,Burkina Faso,fr,True,0 +2468,UN News – Burkina Faso – Politics,Noticias políticas y diplomáticas de la ONU,https://news.un.org/feed/keyword/burkina-faso,11,Política,29,Burkina Faso,en,True,0 +2467,WADR – West Africa Democracy Radio – Burkina,Radio regional centrada en política,https://wadr.org/feed/,11,Política,29,Burkina Faso,en,True,0 +2462,AllAfrica – Burkina Faso News,Agregador pan-africano con noticias exclusivas de Burkina,https://allafrica.com/tools/headlines/rdf/burkinafaso/headlines.rdf,13,Sociedad,29,Burkina Faso,fr,True,0 +2460,Burkina24 – Infos,Portal informativo con noticias nacionales,https://burkina24.com/feed/,13,Sociedad,29,Burkina Faso,fr,True,0 +2459,LeFaso.net – Actualité,Principal portal de noticias de Burkina Faso,https://lefaso.net/spip.php?page=backend,13,Sociedad,29,Burkina Faso,fr,True,0 +2461,RTB – Radiodiffusion Télévision du Burkina,Noticias públicas del medio estatal,https://www.rtb.bf/feed/,13,Sociedad,29,Burkina Faso,fr,True,0 +2469,ReliefWeb – Burkina Faso,Informes humanitarios y de seguridad alimentaria,https://reliefweb.int/updates/rss?country=48,13,Sociedad,29,Burkina Faso,en,False,30 +2483,AllAfrica – Burundi Business,Información económica sobre empresas inversiones y mercados,https://allafrica.com/tools/headlines/rdf/burundi/business.rdf,4,Economía,30,Burundi,en,False,30 +2478,Burundi Eco – Économie,Portal económico centrado en finanzas empresas y desarrollo local,https://burundieco.com/feed/,4,Economía,30,Burundi,fr,False,30 +2485,Africanews – Burundi,Noticias africanas centradas en la situación de Burundi,https://www.africanews.com/tag/burundi/rss,7,Internacional,30,Burundi,en,False,30 +2482,AllAfrica – Burundi Politics,Noticias políticas e institucionales de Burundi,https://allafrica.com/tools/headlines/rdf/burundi/politics.rdf,11,Política,30,Burundi,en,False,30 +2505,Burundi Eco,Semanal socioeconomico con mucha informacion politica y de gobierno,https://burundi-eco.com/feed,11,Política,30,Burundi,fr,True,0 +2504,Burundi Forum,Portal de noticias de Burundi con fuerte enfoque politico y de actualidad,https://burundi-forum.org/feed,11,Política,30,Burundi,fr,True,0 +2508,IWACU English News,Version en ingles de Iwacu con amplia cobertura politica de Burundi,https://www.iwacu-burundi.org/englishnews/feed,11,Política,30,Burundi,en,True,0 +2507,Radio Isanganiro,Radio privada con programas de debate y noticias politicas,https://isanganiro.org/feed,11,Política,30,Burundi,fr,True,0 +2506,Yaga Burundi,Blog colectivo que cubre sociedad y politica de Burundi,https://yaga-burundi.com/feed,11,Política,30,Burundi,fr,False,30 +2481,AllAfrica – Burundi News,Agregador panafricano con selección exclusiva de noticias sobre Burundi,https://allafrica.com/tools/headlines/rdf/burundi/headlines.rdf,13,Sociedad,30,Burundi,en,True,0 +2477,Iwacu Burundi – Actualités,Principal medio independiente con noticias nacionales y reportajes de actualidad,https://www.iwacu-burundi.org/feed/,13,Sociedad,30,Burundi,fr,True,0 +2480,Net Press Burundi,Agencia informativa con actualidad política social y económica,https://netpress.bi/feed/,13,Sociedad,30,Burundi,fr,False,30 +2470,OCHA – Burundi,Boletines de la oficina humanitaria de la ONU con foco en emergencias en Burundi,https://www.unocha.org/rss.xml,13,Sociedad,30,Burundi,en,True,0 +2479,RTNB – Radio Télévision Nationale du Burundi,Medio público con boletines informativos y noticias generales,https://www.rtnb.bi/feed/,13,Sociedad,30,Burundi,fr,False,30 +2497,ReliefWeb – Burundi,Informes humanitarios sobre crisis seguridad y ayuda internacional en Burundi,https://reliefweb.int/updates/rss?country=46,13,Sociedad,30,Burundi,en,False,30 +2486,UN News – Burundi,Boletines de Naciones Unidas sobre la situación política y social en Burundi,https://news.un.org/feed/keyword/burundi,13,Sociedad,30,Burundi,en,True,0 +2511,Business Bhutan,Portal economico y empresarial de Butan,https://businessbhutan.bt/feed,4,Economía,31,Bután,en,False,30 +2518,Africanews Bhutan,Noticias internacionales relacionadas con Butan,https://www.africanews.com/tag/bhutan/rss,7,Internacional,31,Bután,en,False,30 +2510,BBS Bhutan Broadcasting Service Politica,Noticias politicas y comunicados oficiales en la television publica,https://www.bbs.bt/news/?feed=rss2,11,Política,31,Bután,en,False,30 +2515,Druk Journal Politica,Analisis politico y de gobernanza en Butan,https://drukjournal.bt/feed,11,Política,31,Bután,en,True,0 +2509,Kuensel Politica,Seccion politica del diario nacional Kuensel,https://kuenselonline.com/feed,11,Política,31,Bután,en,False,30 +2514,PMO Bhutan News,Comunicados y anuncios de la oficina del primer ministro de Butan,https://www.pmo.gov.bt/feed,11,Política,31,Bután,en,False,30 +2512,The Bhutanese Politica,Diario independiente con fuerte cobertura politica y de gobierno,https://thebhutanese.bt/feed,11,Política,31,Bután,en,True,0 +2513,Bhutan Times,Medio digital con noticias nacionales y regionales,https://www.bhutantimes.com/feed,13,Sociedad,31,Bután,en,False,30 +2517,ReliefWeb Bhutan,Informes humanitarios y de desarrollo sobre Butan,https://reliefweb.int/updates/rss?country=27,13,Sociedad,31,Bután,en,False,30 +2516,UN Bhutan News,Noticias de Naciones Unidas sobre Butan,https://bhutan.un.org/news/rss.xml,13,Sociedad,31,Bután,en,False,30 +2541,De Morgen Economie,Noticias economicas y empresariales con enfoque analitico en Belgica,https://www.demorgen.be/economie/rss.xml,4,Economía,18,Bélgica,nl,True,0 +2539,De Standaard Economie,Actualidad economica belga y europea desde un diario de referencia en neerlandes,https://www.standaard.be/rss/section/economie,4,Economía,18,Bélgica,nl,False,30 +2540,De Tijd Economie,Diario economico lider en Belgica con foco en finanzas empresas y mercados,https://www.tijd.be/rss,4,Economía,18,Bélgica,nl,False,30 +2542,Knack Economie,Reportajes economicos belgas con enfoque en mercados empresas y tendencias europeas,https://www.knack.be/economie/feed/,4,Economía,18,Bélgica,nl,False,30 +2537,La Libre Economie,Noticias economicas de Belgica incluyendo empresas mercados energia y empleo,https://www.lalibre.be/arc/outboundfeeds/rss/section/economie/?outputType=xml,4,Economía,18,Bélgica,fr,True,0 +2536,Le Soir Economie,Seccion economica del diario francofono centrada en empresas mercados y empleo en Belgica,https://www.lesoir.be/rss/23/economie,4,Economía,18,Bélgica,fr,True,0 +2538,RTBF Economie,Servicio publico belga con informacion economica nacional internacional finanzas y mercado laboral,https://rss.rtbf.be/article/rss/economie_rtbf_info.xml,4,Economía,18,Bélgica,fr,False,30 +2543,The Brussels Times Business,Noticias de economia y negocios en Belgica y Bruselas en ingles,https://www.brusselstimes.com/feed/10,4,Economía,18,Bélgica,en,False,30 +2534,Brussels Morning – EU Politics,Diario electronico en ingles con foco en asuntos europeos politica belga y noticias de Bruselas,https://brusselsmorning.com/feed/,7,Internacional,18,Bélgica,en,True,0 +2525,De Morgen – Noticias,Diario en neerlandes con enfoque en actualidad politica sociedad y cultura en Belgica,http://www.demorgen.be/rss.xml,7,Internacional,18,Bélgica,nl,True,0 +2526,De Standaard – Portada,Diario de referencia en neerlandes centrado en noticias nacionales internacionales economia y cultura,https://www.standaard.be/rss/section/1f2838d4-99ea-49f0-9102-138784c7ea7c,7,Internacional,18,Bélgica,nl,False,30 +2524,Het Laatste Nieuws – HLN,Diario popular en neerlandes con noticias generales de Belgica y del mundo,http://www.hln.be/rss.xml,7,Internacional,18,Bélgica,nl,True,0 +2527,Het Nieuwsblad – Noticias,Diario generalista en neerlandes con fuerte peso de la informacion regional deporte y sucesos,https://www.nieuwsblad.be/rss/section/55178e67-15a8-4ddd-a3d8-bfe5708f8932,7,Internacional,18,Bélgica,nl,True,0 +2532,Knack – Noticias,Revista y portal de actualidad en neerlandes centrado en analisis politico y social de Belgica y Europa,https://www.knack.be/feed/,7,Internacional,18,Bélgica,nl,True,0 +2530,La Dernière Heure – Actu,Boulevard y diario deportivo francofono con noticias de actualidad belga y deportiva,https://www.dhnet.be/arc/outboundfeeds/rss/section/actu/?outputType=xml,7,Internacional,18,Bélgica,fr,True,0 +2528,La Libre Belgique – Belgique,Diario francofono historico con noticias politicas y de sociedad centradas en Belgica,https://www.lalibre.be/arc/outboundfeeds/rss/section/belgique/?outputType=xml,7,Internacional,18,Bélgica,fr,True,0 +2529,Le Soir – Actualité principale,Diario francofono de gran tirada con cobertura profunda de politica economia sociedad y cultura en Belgica,https://www.lesoir.be/rss2/9/cible_principale,7,Internacional,18,Bélgica,fr,True,0 +2531,RTBF Info – Destacados,Servicio publico de radio y television con resumen de las principales noticias informativas de Belgica,https://rss.rtbf.be/article/rss/highlight_rtbf_info.xml?source=internal,7,Internacional,18,Bélgica,fr,True,0 +2533,The Brussels Times – Belgium and EU,Diario digital en ingles con noticias y analisis sobre Belgica Bruselas y asuntos de la Union Europea,https://www.brusselstimes.com/rss-feed/6,7,Internacional,18,Bélgica,en,False,30 +2549,De Morgen Politiek,Cobertura politica y analisis critico de la actualidad belga,https://www.demorgen.be/politiek/rss.xml,11,Política,18,Bélgica,nl,True,0 +2548,De Standaard Politiek,Noticias y reportajes de politica belga y europea en neerlandes,https://www.standaard.be/rss/section/belgie/politiek,11,Política,18,Bélgica,nl,False,30 +2550,Knack Politiek,Revista con seccion politica de Belgica y Europa,https://www.knack.be/politiek/feed/,11,Política,18,Bélgica,nl,False,30 +2546,La Libre Politique,Noticias y analisis politicos de Belgica con enfoque en instituciones y partidos,https://www.lalibre.be/arc/outboundfeeds/rss/section/politique/?outputType=xml,11,Política,18,Bélgica,fr,True,0 +2535,Le Soir Politique,Cobertura politica profunda sobre Belgica y la Union Europea desde un diario francofono influyente,https://www.lesoir.be/rss/15/politique,11,Política,18,Bélgica,fr,True,0 +2547,RTBF Politique,Servicio publico belga con informacion politica actual de Belgica y Bruselas,https://rss.rtbf.be/article/rss/politique_rtbf_info.xml,11,Política,18,Bélgica,fr,False,30 +2551,The Brussels Times Politics,Noticias politicas belgas y europeas en ingles,https://www.brusselstimes.com/feed/16,11,Política,18,Bélgica,en,False,30 +2553,Brava News Portal,Cobertura diaria general de noticias del archipielago de Cabo Verde incluyendo economia y sociedad,https://www.brava.news/rss-feeds,7,Internacional,32,Cabo Verde,pt,False,30 +2552,Reforma do Estado Noticias,Cobertura de noticias económicas y de gobernanza desde Cabo Verde,https://www.reformadoestado.gov.cv/index.php/rss-noticias,7,Internacional,32,Cabo Verde,pt,False,30 +2554,Terra Cabo Verde Blog Noticias,Blog de noticias e inversiones inmobiliarias y turísticas en Cabo Verde,https://www.terracaboverde.com/blog/feed/,7,Internacional,32,Cabo Verde,en,True,0 +2569,Cambodia Investment Review,Medio especializado en negocios inversiones banca y empresas en Camboya,https://www.cambodiainvestmentreview.com/feed/,4,Economía,33,Camboya,en,True,0 +2560,National Bank of Cambodia News,Banco central de Camboya con comunicados economicos y financieros,https://www.nbc.org.kh/rss/rss_feed.php?feed=news,4,Economía,33,Camboya,en,False,30 +2593,ASEAN News Today Cambodia,Cobertura de noticias de Camboya en ingles dentro de portal ASEAN noticias incluyendo economia y finanzas,https://aseannewstoday.com/feeds/cambodia-news-headlines/,7,Internacional,33,Camboya,en,False,30 +2589,Agence Kampuchea Presse News,Agencia nacional de noticias de Camboya con cobertura de negocios deportes y politica,https://www.akp.gov.kh/?feed=rss2,7,Internacional,33,Camboya,km,False,30 +2586,Cambodia Express News,Portal de noticias nacionales de Camboya en ingles con secciones economia tecnologia y sociedad,https://cen.com.kh/feed,7,Internacional,33,Camboya,en,True,0 +2561,Cambodia News Feed Aggregator,Agregado de noticias generales de Camboya incluyendo economia turismo y sociedad,https://rss.feedspot.com/cambodia_news_rss_feeds/,7,Internacional,33,Camboya,en,False,30 +2573,Cambodia News Feed Aggregator,Agregado de noticias generales de Camboya,https://www.cambodianewswatch.org/feed/,7,Internacional,33,Camboya,en,True,0 +2591,Cambodian Times RSS,Diario online de Camboya en ingles con cobertura negocios economia sociedad,https://feeds.cambodiantimes.com/rss,7,Internacional,33,Camboya,en,False,30 +2566,Fresh News Asia,Cobertura de noticias rapidas en Camboya en version inglesa via RSS,https://en.freshnewsasia.com/index.php?format=feed&type=rss,7,Internacional,33,Camboya,en,False,30 +2585,Kampuchea Thmey Daily,Diario en jemer de Camboya con noticias actuales generales y sociales,https://www.kampucheathmey.com/feed,7,Internacional,33,Camboya,km,False,30 +2587,Khmer Breaking News,Sitio de noticias en jemer con cobertura de economia salud tecnologia y actualidad,https://kbn.news/feed,7,Internacional,33,Camboya,km,True,0 +2563,Khmer Times News,Periodico en ingles con cobertura de politica economia negocios y sociedad en Camboya,https://www.khmertimeskh.com/feed/,7,Internacional,33,Camboya,en,True,0 +2559,Koh Santepheap Daily,Diario en jemer con noticias generales y actualizacion constante,https://kohsantepheapdaily.com.kh/feed,7,Internacional,33,Camboya,km,True,0 +2576,Koh Santepheap Daily,Diario en jemer con noticias generales y actualizacion constant,https://www.kohsantepheapdaily.com.kh/feed,7,Internacional,33,Camboya,km,True,0 +2588,Nokor Wat News,Noticias camboyanas en ingles con secciones de economia negocios y actualidad,https://www.nokorwatnews.com/feed,7,Internacional,33,Camboya,en,False,30 +2562,Open Development Cambodia News,Plataforma de datos y noticias de desarrollo economico y social de Camboya,https://opendevelopmentcambodia.net/news/,7,Internacional,33,Camboya,en,False,30 +2568,Open Development Cambodia News,Plataforma de datos y noticias de desarrollo economico y social en Camboya,https://opendevelopmentcambodia.net/feed/,7,Internacional,33,Camboya,en,True,0 +2564,Phnom Penh Post News,Diario en ingles de Camboya con noticias generales politica economia y sociedad,https://www.phnompenhpost.com/rss,7,Internacional,33,Camboya,en,False,30 +2572,RFA Khmer Service,Servicio en jemer de Radio Free Asia con noticias politicas sociales de Camboya,https://www.rfa.org/khmer/rss2.xml,7,Internacional,33,Camboya,km,True,0 +2592,Sabay News RSS,Portal popular en jemer con secciones de tecnologia entretenimiento economia y noticias,https://news.sabay.com.kh/rss,7,Internacional,33,Camboya,km,False,30 +2590,The Cambodia Daily News (EN),Medio historico en ingles sobre noticias y negocios en Camboya,https://english.cambodiadaily.com/rss,7,Internacional,33,Camboya,en,True,0 +2571,VOA Cambodia Khmer,Servicio internacional con noticias en jemer sobre Camboya,https://khmer.voanews.com/api/zgqyqme_oq,7,Internacional,33,Camboya,km,False,30 +2570,VOD Cambodia English,Medio independiente en ingles con noticias politicas sociales y economicas de Camboya,https://vodenglish.news/feed/,7,Internacional,33,Camboya,en,True,0 +2611,Journal du Cameroun Sports,Seccion deportiva del medio francofono principal,https://www.journalducameroun.com/category/sport/feed/,3,Deportes,34,Camerún,fr,True,0 +2612,Business in Cameroon,Economia negocios y mercado financiero en Camerun,https://www.businessincameroon.com/feed,4,Economía,34,Camerún,en,False,30 +2601,Cameroon Business Today,Economia empresas inversiones y mercados en Camerun,https://www.cameroonbusinesstoday.cm/rss,4,Economía,34,Camerún,en,False,30 +2607,EcoMatin Cameroun,Economia mercados empresas y finanzas en Camerun,https://ecomatin.net/feed/,4,Economía,34,Camerún,fr,True,0 +2609,SBBC Business Cameroun,Negocios empresas mercado y economia en Camerun,https://www.sbbc.com/feed/,4,Economía,34,Camerún,fr,True,0 +2606,237 Online News,Portal de noticias de Camerun en frances con economia deportes politica y cultura,https://www.237online.com/feed/,7,Internacional,34,Camerún,fr,True,0 +2599,Actu Cameroun Actualite,Portal de noticias de Camerun con politica deportes economia y sociedad,https://actucameroun.com/feed/,7,Internacional,34,Camerún,fr,True,0 +2600,AllAfrica Cameroun News,Agregador con noticias politicas economicas y sociales de Camerun,https://allafrica.com/tools/headlines/rdf/cameroon/headlines.rdf,7,Internacional,34,Camerún,en,True,0 +2604,Cameroon Magazine,Noticias generales sobre politica deportes y sociedad del pais,https://www.cameroonmagazine.com/feed/,7,Internacional,34,Camerún,en,False,30 +2594,Cameroon Online News,Portal con noticias generales de politica economia seguridad y sociedad en Camerun,https://cameroononlinenews.com/feed/,7,Internacional,34,Camerún,en,True,0 +2608,Cameroon Radio Television News,Medio publico con noticias de politica sociedad economia y cultura,https://www.crtv.cm/feed/,7,Internacional,34,Camerún,fr,False,30 +2597,Cameroon Tribune News,Diario estatal con noticias de gobierno economia y sociedad,https://www.cameroon-tribune.cm/rss,7,Internacional,34,Camerún,fr,True,0 +2598,Journal du Cameroun Actualite,Medio francofono con noticias de economia politica y cultura,https://www.journalducameroun.com/feed/,7,Internacional,34,Camerún,fr,True,0 +2622,237 Online Politique,Noticias politicas de Camerun desde el medio francofono 237 Online,https://www.237online.com/category/politique/feed/,11,Política,34,Camerún,fr,False,30 +2620,Actu Cameroun Politique,Seccion politica del portal de noticias Actu Cameroun,https://actucameroun.com/category/politique/feed/,11,Política,34,Camerún,fr,True,0 +2596,Cameroon Concord News,Medio con fuerte cobertura politica incluyendo crisis anglofona y gobierno,https://www.cameroonconcordnews.com/feed/,11,Política,34,Camerún,en,True,0 +2605,Cameroon Info Net Politique,Noticias politicas del portal camerunes en frances,https://www.cameroon-info.net/rss,11,Política,34,Camerún,fr,False,30 +2595,Cameroon Intelligence Report,Portal especializado en politica conflictos y analisis del gobierno de Camerun,https://www.cameroonintelligencereport.com/feed/,11,Política,34,Camerún,en,True,0 +2602,Cameroon News Agency Politics,Agencia independiente con cobertura politica nacional y regional de Camerun,https://cameroonnewsagency.com/feed/,11,Política,34,Camerún,en,True,0 +2610,Cameroon Voice Politics,Medio independiente con noticias politicas y sociales de Camerun,https://www.cameroonvoice.com/rss,11,Política,34,Camerún,fr,False,30 +2619,Journal du Cameroun Politique,Seccion politica de uno de los diarios francofonos mas importantes del pais,https://www.journalducameroun.com/category/politique/feed/,11,Política,34,Camerún,fr,True,0 +2603,Mimimefo Infos Politics,Medio anglofono con noticias de politica derechos humanos y sociedad en Camerun,https://mimimefoinfos.com/feed/,11,Política,34,Camerún,en,True,0 +2613,StopBlaBlaCam Politique,Seccion politica del medio StopBlaBlaCam con enfoque en gobierno y economia publica,https://www.stopblablacam.com/feed,11,Política,34,Camerún,fr,False,30 +2696,Art Canada Institute,Investigacion y publicaciones de arte canadiense,https://www.aci-iac.ca/feed/,2,Cultura,35,Canadá,en,True,0 +2684,CBC Arts,Noticias de arte musica cine literatura y cultura canadiense,https://www.cbc.ca/arts/rss,2,Cultura,35,Canadá,en,False,5 +2685,CBC Books,Literatura autores canadienses premios y novedades editoriales,https://www.cbc.ca/books/feeds/rss,2,Cultura,35,Canadá,en,False,5 +2686,CTV Entertainment,Noticias de entretenimiento cine musica celebridades y television,https://www.ctvnews.ca/rss/ctvnews-ca-entertainment-public-rss-1.822291,2,Cultura,35,Canadá,en,False,30 +2699,Canada Council for the Arts,Noticias culturales federales y apoyo a artistas,https://canadacouncil.ca/rss,2,Cultura,35,Canadá,en,False,30 +2695,Canadian Art Magazine,Cobertura del arte contemporaneo en Canada,https://canadianart.ca/feed/,2,Cultura,35,Canadá,en,False,30 +2702,Canadian Music Centre,Musica contemporanea e iniciativas de compositores canadienses,https://cmccanada.org/feed/,2,Cultura,35,Canadá,en,True,0 +2687,Global News Entertainment,Cultura entretenimiento television y cine en Canada,https://globalnews.ca/entertainment/feed/,2,Cultura,35,Canadá,en,True,0 +2703,Heritage Canada News,Noticias de patrimonio cultural museos historia y cultura nacional,https://www.canada.ca/en/canadian-heritage/news.atom.xml,2,Cultura,35,Canadá,en,False,30 +2700,Hot Docs Festival Canada,Noticias de cine documental y actividades del festival,https://hotdocs.ca/rss,2,Cultura,35,Canadá,en,False,30 +2689,La Presse Arts et Spectacles,Cobertura de espectaculos arte y cultura en Quebec y Canada,https://www.lapresse.ca/arc/outboundfeeds/rss/section/arts/,2,Cultura,35,Canadá,fr,False,30 +2690,Le Devoir Culture,Actualidad cultural y critica artistica del periodico Le Devoir,https://www.ledevoir.com/rss/culture.xml,2,Cultura,35,Canadá,fr,False,30 +2691,Montreal Gazette Arts,Cobertura de cultura arte musica y eventos de Montreal,https://montrealgazette.com/category/entertainment/feed,2,Cultura,35,Canadá,en,True,0 +2697,Museum of Contemporary Art Toronto,Noticias y exposiciones del museo de arte contemporaneo,https://moca.ca/feed/,2,Cultura,35,Canadá,en,True,0 +2693,National Post Arts and Culture,Cine arte musica y literatura desde el National Post,https://nationalpost.com/category/entertainment/feed,2,Cultura,35,Canadá,en,True,0 +2688,Radio Canada Arts et Culture,Seccion cultural francofona con arte musica cine y espectaculos,https://ici.radio-canada.ca/rss/1000018,2,Cultura,35,Canadá,fr,False,30 +2694,The Globe and Mail Arts,Resenas de cine musica libros y arte,https://www.theglobeandmail.com/arts/?service=rss,2,Cultura,35,Canadá,en,False,30 +2701,Toronto International Film Festival,Cine canadiense e internacional y cultura audiovisual,https://tiff.net/feed,2,Cultura,35,Canadá,en,False,30 +2692,Toronto Star Entertainment,Espectaculos arte cine y cultura pop en Toronto y Canada,https://www.thestar.com/content/thestar/feed.rss/entertainment.html,2,Cultura,35,Canadá,en,False,30 +2698,Vancouver Art Gallery News,Exposiciones y actividades culturales en Vancouver,https://www.vanartgallery.bc.ca/feed,2,Cultura,35,Canadá,en,False,30 +2626,CBC News Canada,Corporacion publica canadiense con noticias de politica sociedad economia y provincias,https://www.cbc.ca/cmlink/rss-topstories,7,Internacional,35,Canadá,en,False,5 +2624,CTV News Canada,Portal nacional con noticias generales politica economia y sociedad de Canada,https://www.ctvnews.ca/rss/ctvnews-ca-top-stories-public-rss-1.822009,7,Internacional,35,Canadá,en,False,30 +2641,Calgary Herald News,Noticias de Alberta politica negocios y sociedad,https://calgaryherald.com/feed,7,Internacional,35,Canadá,en,True,0 +2638,Edmonton Journal News,Noticias de Alberta con politica economica regional y sociedad,https://edmontonjournal.com/feed,7,Internacional,35,Canadá,en,True,0 +2625,Global News Canada,Noticias nacionales de Canada con secciones politica economia y actualidad,https://globalnews.ca/feed/,7,Internacional,35,Canadá,en,True,0 +2635,La Presse Actualites,Diario francofono con politica nacional provincial y sociedad,https://www.lapresse.ca/arc/outboundfeeds/rss/section/actualites/,7,Internacional,35,Canadá,fr,False,30 +2634,Le Devoir Actualites,Periodico francofono de Quebec con noticias de politica sociedad y economia,https://www.ledevoir.com/rss/actualites.xml,7,Internacional,35,Canadá,fr,False,30 +2636,Le Journal de Montreal Actualites,Noticias generales y politicas de Quebec y Canada,https://www.journaldemontreal.com/actualites/rss.xml,7,Internacional,35,Canadá,fr,False,30 +2633,Montreal Gazette News,Diario de Montreal con noticias generales y actualidad canadiense,https://montrealgazette.com/feed,7,Internacional,35,Canadá,en,True,0 +2628,Radio Canada Actualites,Servicio publico francofono con noticias nacionales y politica de Canada,https://ici.radio-canada.ca/rss/4159,7,Internacional,35,Canadá,fr,True,0 +2637,Vancouver Sun News,Diario de Vancouver con noticias locales nacionales y politica,https://vancouversun.com/feed,7,Internacional,35,Canadá,en,True,0 +2639,Winnipeg Free Press News,Enfoque en politica local nacional y crimen en Manitoba,https://www.winnipegfreepress.com/rss/,7,Internacional,35,Canadá,en,True,0 +2627,CBC Politics,Seccion de politica nacional canadiense con analisis actualidad y gobierno,https://www.cbc.ca/cmlink/rss-politics,11,Política,35,Canadá,en,False,5 +2642,Hill Times Politics,Medio especializado en politica parlamentaria y gobierno federal,https://www.hilltimes.com/feed/,11,Política,35,Canadá,en,True,0 +2643,IPolitics Canada,Portal especializado en politicas publicas gobierno y legislacion canadiense,https://ipolitics.ca/feed/,11,Política,35,Canadá,en,True,0 +2631,National Post Politics,Noticias politicas canadienses con enfoque conservador y nacional,https://nationalpost.com/category/politics/feed,11,Política,35,Canadá,en,True,0 +2640,Ottawa Citizen Politics,Politica federal desde la capital de Canada con cobertura parlamentaria,https://ottawacitizen.com/category/news/politics/feed,11,Política,35,Canadá,en,True,0 +2629,Radio Canada Politique,Seccion politica francofona con cobertura parlamentaria y gobierno,https://ici.radio-canada.ca/rss/1000684,11,Política,35,Canadá,fr,False,30 +2630,The Globe and Mail Politics,Diario nacional con analisis de politica canadiense y mundial,https://www.theglobeandmail.com/politics/?service=rss,11,Política,35,Canadá,en,False,30 +2632,Toronto Star Politics,Diario de Toronto con seccion politica federal y provincial,https://www.thestar.com/content/thestar/feed.rss/news/politics.html,11,Política,35,Canadá,en,False,30 +2672,Alberta Health Services,Noticias de salud hospitales y emergencias en Alberta,https://www.albertahealthservices.ca/rss/news.rss,12,Salud,35,Canadá,en,False,30 +2671,BC Centre for Disease Control,Alertas sanitarias y comunicados del centro de control de enfermedades,https://bccdc.ca/about/news-stories/news.rss,12,Salud,35,Canadá,en,False,30 +2678,BC Childrens Hospital Research,Investigacion medica pediatrica en Canada,https://www.bcchr.ca/news/rss,12,Salud,35,Canadá,en,False,30 +2664,CBC News Salud,Seccion de salud publica medicina y ciencia de CBC News,https://www.cbc.ca/cmlink/rss-health,12,Salud,35,Canadá,en,False,5 +2665,CTV News Salud,Noticias de salud medicina y bienestar en Canada,https://www.ctvnews.ca/rss/ctvnews-ca-health-public-rss-1.822284,12,Salud,35,Canadá,en,False,30 +2683,CTV Vancouver Island Health,Noticias sanitarias y hospitales en Vancouver Island,https://vancouverisland.ctvnews.ca/rss/ctv-news-vancouver-island-health-1.4189897,12,Salud,35,Canadá,en,False,30 +2680,Canadian Institute for Health Information,Datos sanitarios estatisticas y reportes,https://www.cihi.ca/en/rss.xml,12,Salud,35,Canadá,en,False,30 +2679,Canadian Medical Association News,Noticias medicas y politicas de salud en Canada,https://www.cma.ca/news.rss,12,Salud,35,Canadá,en,False,30 +2666,Global News Salud,Noticias de salud publica y sistemas sanitarios en Canada,https://globalnews.ca/health/feed/,12,Salud,35,Canadá,en,True,0 +2667,Health Canada News,Publicaciones oficiales de salud publica regulacion farmaceutica y alertas,https://www.canada.ca/en/health-canada/news.atom.xml,12,Salud,35,Canadá,en,False,30 +2681,Healthing Canada,Portal especializado en salud enfermedades y bienestar,https://healthing.ca/feed/,12,Salud,35,Canadá,en,True,0 +2677,McGill University Health Centre,Noticias de salud investigacion y avances clinicos,https://muhc.ca/rss.xml,12,Salud,35,Canadá,en,False,30 +2674,Montreal Childrens Hospital,Noticias y avances clinicos del hospital infantil de Montreal,https://montrealchildrenshospital.ca/news/feed,12,Salud,35,Canadá,fr,False,30 +2669,Ontario Health News,Actualidad sanitaria hospitales y politicas de salud en Ontario,https://www.ontario.ca/news/health-news.rss,12,Salud,35,Canadá,en,False,30 +2668,Public Health Agency of Canada,Comunicados de salud publica enfermedades y emergencias sanitarias,https://www.canada.ca/en/public-health/news.atom.xml,12,Salud,35,Canadá,en,False,30 +2670,Quebec Sante Actualites,Noticias de salud medicina y hospitales en Quebec,https://www.quebec.ca/sante/actualites/feed,12,Salud,35,Canadá,fr,False,30 +2673,SickKids Hospital Toronto,Investigacion pediatrica y noticias clinicas del hospital infantil SickKids,https://www.sickkids.ca/about/rss-feeds/news/,12,Salud,35,Canadá,en,False,30 +2675,Sunnybrook Health Sciences Centre,Investigacion medica y noticias clinicas en Toronto,https://sunnybrook.ca/rss/news.xml,12,Salud,35,Canadá,en,False,30 +2682,The Globe and Mail Salud,Noticias medicas y de bienestar desde el diario nacional,https://www.theglobeandmail.com/life/health-and-fitness/?service=rss,12,Salud,35,Canadá,en,False,30 +2676,University Health Network,UHN Toronto con noticias de salud investigacion y hospitales,https://www.uhn.ca/corp/rss/news.xml,12,Salud,35,Canadá,en,False,30 +2645,BetaKit Canada Tecnologia,Portal canadiense especializado en startups innovacion IA y empresas tech,https://betakit.com/feed/,14,Tecnología,35,Canadá,en,True,0 +2648,CBC News Tecnologia,Noticias de ciencia tecnologia y digitalizacion en Canada,https://www.cbc.ca/cmlink/rss-technology,14,Tecnología,35,Canadá,en,False,5 +2656,CIRA Cybersecurity,Ciberseguridad canadiense DNS internet y privacidad digital,https://www.cira.ca/newsroom/feed,14,Tecnología,35,Canadá,en,False,30 +2647,CTV News Tecnologia,Seccion de tecnologia del medio canadiense CTV News,https://www.ctvnews.ca/rss/ctvnews-ca-sci-tech-public-rss-1.822289,14,Tecnología,35,Canadá,en,False,30 +2663,Carleton University Technology News,Actualidad tecnologica e investigacion aplicada en Canada,https://newsroom.carleton.ca/feed/,14,Tecnología,35,Canadá,en,True,0 +2661,Creative Destruction Lab Tecnologia,Centro de innovacion emprendimiento tech y deeptech,https://creativedestructionlab.com/feed/,14,Tecnología,35,Canadá,en,True,0 +2662,CyberSecure Canada,Noticias y alertas sobre ciberseguridad y proteccion digital,https://www.cyber.gc.ca/en/news/rss.xml,14,Tecnología,35,Canadá,en,False,30 +2652,Financial Post FP Tech Desk,Seccion de negocios tecnologicos fintech y startups,https://financialpost.com/category/technology/feed,14,Tecnología,35,Canadá,en,True,0 +2649,Global News Tecnologia,Noticias tech de Canada incluyendo redes sociales IA y ciencia,https://globalnews.ca/tech/feed/,14,Tecnología,35,Canadá,en,True,0 +2653,IT World Canada,Portal especializado en tecnologia empresarial IA ciberseguridad y nube,https://www.itworldcanada.com/feed,14,Tecnología,35,Canadá,en,False,30 +2646,MobileSyrup Canada,Portal numero uno en Canada sobre moviles telecom y tecnologia de consumo,https://mobilesyrup.com/feed/,14,Tecnología,35,Canadá,en,True,0 +2659,Montreal AI Research Blog,Blog canadiense sobre IA aprendizaje automatico y ciencia de datos,https://montreal.ai/feed,14,Tecnología,35,Canadá,en,False,30 +2650,National Post Tecnologia,Seccion tech del diario canadiense National Post,https://nationalpost.com/category/technology/feed,14,Tecnología,35,Canadá,en,True,0 +2655,TechBomb Canada,Noticias tech ciencia y digitalizacion en Canada,https://www.techbomb.ca/feed/,14,Tecnología,35,Canadá,en,True,0 +2644,TechCrunch Canada,Noticias de tecnologia startups y empresas tech en Canada,https://techcrunch.com/tag/canada/feed/,14,Tecnología,35,Canadá,en,True,0 +2654,Techvibes Canada,Medio sobre startups innovacion y emprendimiento tecnologico,https://techvibes.com/feed,14,Tecnología,35,Canadá,en,False,30 +2651,Toronto Star Tecnologia,Noticias de tecnologia de Toronto Star enfocadas en innovacion y digitalizacion,https://www.thestar.com/content/thestar/feed.rss/technology.html,14,Tecnología,35,Canadá,en,False,30 +2657,University of Toronto Engineering News,Innovacion investigacion y avances tecnologicos universitarios,https://news.engineering.utoronto.ca/feed/,14,Tecnología,35,Canadá,en,True,0 +2658,University of Waterloo Tech News,Noticias de investigacion tecnologica IA y computacion cuantica,https://uwaterloo.ca/news/rss.xml,14,Tecnología,35,Canadá,en,True,0 +2660,Vector Institute AI,Noticias de investigacion canadiense en IA redes neuronales y ciencia computacional,https://vectorinstitute.ai/feed/,14,Tecnología,35,Canadá,en,False,30 +2782,Qatar University Science,Publicaciones de ciencia e investigacion en Qatar University,https://www.qu.edu.qa/static_file/qu/newsroom/rss.xml,1,Ciencia,36,Catar,en,False,30 +2760,Doha Film Institute,Noticias de cine festivales y cultura audiovisual en Qatar,https://www.dohafilminstitute.com/rss,2,Cultura,36,Catar,en,True,0 +2762,Katara Cultural Village News,Centro cultural con eventos arte musica exposiciones y patrimonio,https://www.katara.net/en/rss,2,Cultura,36,Catar,en,False,30 +2722,Qatar Museums News,Arte museos patrimonio y exposiciones de la institucion cultural nacional,https://qm.org.qa/en/media/rss,2,Cultura,36,Catar,en,False,30 +2788,Gulf Times Sports,Seccion de deportes futbol atletismo y competiciones locales,https://www.gulf-times.com/rss/sport,3,Deportes,36,Catar,en,False,30 +2789,Qatar Tribune Sports,Deportes locales internacionales y eventos en Qatar,https://www.qatar-tribune.com/rss/sports,3,Deportes,36,Catar,en,False,30 +2787,The Peninsula Sports,Noticias deportivas de Qatar y region,https://thepeninsulaqatar.com/rss/sports,3,Deportes,36,Catar,en,True,0 +2711,Gulf Times Business,Economia energia gas y negocios en Qatar,https://www.gulf-times.com/rss/business,4,Economía,36,Catar,en,False,30 +2720,Qatar Financial Centre,Noticias de finanzas y reformas economicas,https://www.qfc.qa/en/Media/News/rss,4,Economía,36,Catar,en,False,30 +2713,Qatar Tribune Business,Economia local inversiones y mercado laboral,https://www.qatar-tribune.com/rss/business,4,Economía,36,Catar,en,False,30 +2712,The Peninsula Business,Negocios empresas y finanzas en Qatar,https://thepeninsulaqatar.com/rss/business,4,Economía,36,Catar,en,True,0 +2717,Doha College News,Noticias escolares actividades y educacion en Doha,https://www.dohacollege.com/rss,5,Educación,36,Catar,en,False,30 +2718,Hamad Bin Khalifa University News,Noticias academicas e investigacion,https://www.hbku.edu.qa/en/rss.xml,5,Educación,36,Catar,en,True,0 +2710,Al Jazeera Arabic International,Noticias globales en arabe desde Qatar,https://www.aljazeera.net/aljazeerarss/ar-all-news,7,Internacional,36,Catar,ar,True,0 +2719,Qatar Environment and Energy,Noticias medioambientales energia e iniciativas verdes,https://www.qatarenergy.qa/en/MediaCenter/News/_layouts/15/feed.aspx,8,Medio Ambiente,36,Catar,en,False,30 +2723,Qatar Foundation Sustainability,Proyectos de sostenibilidad educacion ambiental e iniciativas verdes,https://www.qf.org.qa/rss,8,Medio Ambiente,36,Catar,en,False,30 +2704,The Peninsula Opinion,Editoriales analisis y columnas de opinion,https://thepeninsulaqatar.com/rss,10,Opinión,36,Catar,en,False,30 +2744,Al Jazeera Investigations,Investigaciones politicas sobre MENA y geopolitica,https://www.aljazeera.com/xml/rss/investigations.xml,11,Política,36,Catar,en,False,30 +2751,Arab Center for Research Qatar,Think tank politico y geopolitico con sede en Doha,https://arabcenterdc.org/feed/,11,Política,36,Catar,en,True,0 +2750,Doha Forum Politics,Foro politico internacional con sede en Doha,https://dohaforum.org/feed,11,Política,36,Catar,en,False,30 +2749,Doha International Family Institute Policy,Politicas sociales y derechos familiares en Qatar,https://www.difi.org.qa/feed/,11,Política,36,Catar,en,False,30 +2753,Gulf Organization for Industrial Consulting,Decisiones politicas industriales en el Golfo,https://goic.org.qa/en/rss,11,Política,36,Catar,en,False,30 +2755,IOM Qatar Migration Policy,Politicas migratorias y reportes de la IOM en Qatar,https://www.iom.int/rss,11,Política,36,Catar,en,False,30 +2752,Middle East Forum Qatar Politics,Analisis politicos regionales y relaciones internacionales,https://www.meforum.org/rss,11,Política,36,Catar,en,False,30 +2737,Ministry of Foreign Affairs Arabic,Version arabe de politicas exteriores y comunicados oficiales,https://www.mofa.gov.qa/ar/feed,11,Política,36,Catar,ar,False,30 +2733,Ministry of Foreign Affairs Qatar,Comunicados oficiales de diplomacia y relaciones internacionales,https://www.mofa.gov.qa/en/feed,11,Política,36,Catar,en,False,30 +2725,QNA Arabic Politics,Version arabe de la agencia oficial con contenido politico,https://www.qna.org.qa/ar/Rss,11,Política,36,Catar,ar,False,30 +2707,QNA Qatar News Agency Politics,Agencia oficial con comunicados politicos del gobierno de Qatar,https://www.qna.org.qa/en/Rss,11,Política,36,Catar,en,False,30 +2754,UN Qatar Office,Noticias politicas ONU desde la oficina regional en Doha,https://www.un.org.qa/en/rss.xml,11,Política,36,Catar,en,False,30 +2708,Doha News Health,Noticias de salud seguridad alimentaria y bienestar,https://www.dohanews.co/feed/,12,Salud,36,Catar,en,True,0 +2721,Ministry of Public Health Qatar,Comunicados de salud publica alertas y programas sanitarios,https://www.moph.gov.qa/english/mediacenter/news/rssfeed/pages/rss.aspx,12,Salud,36,Catar,en,False,30 +2765,Gulf Times Community,Sociedad expatriados ocio y actividades locales,https://www.gulf-times.com/rss/community,13,Sociedad,36,Catar,en,False,30 +2715,ILoveQatar Community,Eventos sociales cultura sociedad y actividades,https://www.iloveqatar.net/rss.xml,13,Sociedad,36,Catar,en,False,30 +2714,Qatar Living Community,Comunidad expatriada vida diaria sociedad y eventos,https://www.qatarliving.com/rss,13,Sociedad,36,Catar,en,False,30 +1554,Gulf Times Technology,Tecnologia telecom digitalizacion y ciencia en Qatar,https://www.gulf-times.com/rss,14,Tecnología,36,Catar,en,False,30 +2779,Ministry of Communications and IT Qatar,Politicas y proyectos digitales del ministerio,https://www.mcit.gov.qa/en/media/rss.xml,14,Tecnología,36,Catar,en,False,30 +2778,Ooredoo Qatar News,Telecomunicaciones 5G fibra y tecnologia de red en Qatar,https://www.ooredoo.qa/web/en/press-rss,14,Tecnología,36,Catar,en,False,30 +2775,Qatar Computing Research Institute,QCRI investigacion en IA datos seguridad y ciencias computacionales,https://www.hbku.edu.qa/en/qcri/rss,14,Tecnología,36,Catar,en,False,30 +2780,Qatar Digital Government,Avances en gobierno digital ciberseguridad y servicios electronicos,https://www.motc.gov.qa/en/rss,14,Tecnología,36,Catar,en,False,30 +2777,Qatar Science and Technology Park,Innovacion startups y proyectos de investigacion en Qatar,https://qstp.org.qa/feed/,14,Tecnología,36,Catar,en,False,30 +2706,Qatar Tribune Technology,Seccion tech del Qatar Tribune con noticias de innovacion ciencia y empresas,https://www.qatar-tribune.com/rss,14,Tecnología,36,Catar,en,False,30 +2716,Marhaba Qatar Travel,Guia de viajes hoteles destinos y actividades,https://www.marhaba.qa/feed/,15,Viajes,36,Catar,en,True,0 +2813,Visit Qatar Travel,Turismo viajes cultura y destinos nacionales,https://visitqatar.com/feed,15,Viajes,36,Catar,en,False,30 +2815,Alwihda Info Culture,Seccion de cultura arte tradiciones y eventos en Chad,https://www.alwihdainfo.com/xml/syndication.rss,2,Cultura,37,Chad,fr,True,0 +2821,BBC Afrique Culture,Cobertura cultural africana y reportajes sobre tradiciones y artes del Sahel,https://www.bbc.com/afrique/index.xml,2,Cultura,37,Chad,fr,True,0 +2817,Journal du Tchad Culture,Seccion cultural del diario con arte musica libros y patrimonio,https://journaldutchad.com/feed/,2,Cultura,37,Chad,fr,False,30 +2829,OCHA Sahel Cultural Heritage,Informes sobre patrimonio cultural y comunidades del Sahel,https://reliefweb.int/updates/rss.xml,2,Cultura,37,Chad,en,True,0 +2834,Sahel Tribune Culture,Portal regional con arte musica costumbres y cultura del Sahel,https://saheltribune.com/feed/,2,Cultura,37,Chad,en,True,0 +2816,Tchad Infos Culture,Cultura musica artes tradicionales eventos y sociedad,https://tchadinfos.com/feed/,2,Cultura,37,Chad,fr,False,30 +2861,UNESCO Culture Africa,Patrimonio cultural proyectos artisticos y educacion en Chad,https://www.unesco.org/en/rss,2,Cultura,37,Chad,en,False,30 +2819,VOA Afrique Culture,Noticias culturales africanas incluyendo Chad arte cine y tradiciones,https://www.voaafrique.com/api/zpypmeir_r,2,Cultura,37,Chad,fr,False,30 +2832,African Development Bank Chad Economie,Proyectos de desarrollo inversiones y mercado regional en Chad,https://www.afdb.org/en/rss,4,Economía,37,Chad,en,False,30 +2823,AllAfrica Tchad Business,Noticias economicas y empresariales del Chad via AllAfrica,https://allafrica.com/tools/headlines/rdf/chad/headlines.rdf,4,Economía,37,Chad,en,True,0 +2854,FAO Chad Agriculture,Economia agricola seguridad alimentaria y produccion en Chad,https://www.fao.org/news/rss/en/,4,Economía,37,Chad,en,False,30 +2831,IMF Chad,Comunicados del Fondo Monetario sobre economia politica fiscal y reformas del Chad,https://www.imf.org/en/News/SiteIndex/FeedByRegion?region=MEA,4,Economía,37,Chad,en,False,30 +2833,Le Sahel Tchad,Medio regional que cubre Chad y paises vecinos,https://lesahel.org/feed/,7,Internacional,37,Chad,fr,True,0 +2825,UN Chad News,Noticias institucionales de Naciones Unidas en Chad,https://news.un.org/feed/subscribe/en/news/topic/africa/feed/,7,Internacional,37,Chad,en,True,0 +2824,Xinhua Afrique Tchad,Agencia china con noticias de Chad economia y politica,https://www.xinhuanet.com/english/rss/world.xml,7,Internacional,37,Chad,en,False,30 +2757,Amnesty International Chad,Reportes politicos y sociales sobre el Chad,https://www.amnesty.org/en/latest/feed/,11,Política,37,Chad,en,True,0 +2758,Human Rights Watch Chad,Informes y noticias sobre derechos humanos en Chad,https://www.hrw.org/rss/news,11,Política,37,Chad,en,True,0 +2826,UNICEF Chad,Comunicados oficiales sobre ninos salud y educacion en Chad,https://www.unicef.org/feeds/press-release.xml,12,Salud,37,Chad,en,False,30 +2841,ITU Africa Digital,Union Internacional de Telecomunicaciones con informes sobre Chad,https://www.itu.int/net4/itu-d/rss.aspx,14,Tecnología,37,Chad,en,False,30 +3019,ALMA Observatory Chile,Radioastronomia e investigacion espacial,https://www.almaobservatory.org/en/feed/,1,Ciencia,38,Chile,en,True,0 +3008,BioBioChile Ciencia,Ciencia espacio salud fisica y tecnologia cientifica,https://www.biobiochile.cl/noticias/ciencia-y-tecnologia/ciencia/feed,1,Ciencia,38,Chile,es,False,30 +3017,Cerro Tololo Inter American Observatory,Astronomia investigaciones y noticias tecnicas,https://www.ctio.noirlab.edu/noirlab/rss.xml,1,Ciencia,38,Chile,en,False,30 +3007,Emol Ciencia,Ciencia astronomia salud investigacion y universidades,https://www.emol.com/rss/ciencia.xml,1,Ciencia,38,Chile,es,False,5 +3018,European Southern Observatory ESO Chile,Astronomia internacional con observatorios en Chile,https://www.eso.org/public/rss.xml,1,Ciencia,38,Chile,en,False,30 +3005,La Tercera Ciencia,Seccion de ciencia astronomia investigacion y salud,https://www.latercera.com/etiqueta/ciencia/feed/,1,Ciencia,38,Chile,es,False,30 +3006,La Tercera Futuro,Noticias de ciencia innovacion astronomia y estudios,https://www.latercera.com/etiqueta/futuro/feed/,1,Ciencia,38,Chile,es,False,30 +3012,Meganoticias Ciencia,Noticias de ciencia descubrimientos y estudios,https://www.meganoticias.cl/ciencia/rss/,1,Ciencia,38,Chile,es,False,30 +2924,Ministerio de Ciencia Chile,Comunicados proyectos cientificos e innovacion,https://www.minciencia.gob.cl/rss,1,Ciencia,38,Chile,es,False,30 +3016,SOCHIAS Sociedad Chilena de Astronomia,Noticias de astronomia chilena e investigacion,https://sochias.cl/feed/,1,Ciencia,38,Chile,es,True,0 +3023,Universidad Austral Ciencia,Investigacion ecoambiental biologica y cientifica,https://www.uach.cl/rss,1,Ciencia,38,Chile,es,False,30 +2923,Universidad Catolica Ciencia,Noticias de ciencia y tecnologia de la UC,https://www.uc.cl/rss/,1,Ciencia,38,Chile,es,True,0 +2922,Universidad de Chile Ciencia,Investigacion cientifica y estudios academicos,https://www.uchile.cl/rss,1,Ciencia,38,Chile,es,False,30 +3022,Universidad de Concepcion Ciencia,Investigacion cientifica en fisica salud y astronomia,https://www.udec.cl/rss,1,Ciencia,38,Chile,es,False,30 +2935,24Horas Cultura,Arte patrimonio libros y espectaculos,https://www.24horas.cl/tendencias/rss,2,Cultura,38,Chile,es,False,30 +2942,Biblioteca Nacional de Chile,Libros literatura archivos y patrimonio,https://www.bibliotecanacional.gob.cl/rss,2,Cultura,38,Chile,es,False,30 +2927,BioBioChile Cultura,Cultura chilena musica libros teatro y tradiciones,https://www.biobiochile.cl/noticias/artes-y-espectaculos/cultura/feed,2,Cultura,38,Chile,es,False,30 +2940,Centro Cultural Gabriela Mistral GAM,Eventos culturales teatro musica exposiciones,https://www.gam.cl/feed/,2,Cultura,38,Chile,es,False,30 +2938,Diario UChile Cultura,Radio Universidad de Chile con fuerte enfoque cultural,https://radio.uchile.cl/category/cultura/feed/,2,Cultura,38,Chile,es,True,0 +2929,El Mostrador Cultura,Reportajes y actualidad cultural en Chile,https://www.elmostrador.cl/cultura/feed/,2,Cultura,38,Chile,es,False,30 +2926,Emol Cultura,Cultura espectaculos libros cine y patrimonio en Chile,https://www.emol.com/rss/cultura.xml,2,Cultura,38,Chile,es,False,5 +2939,Fundacion Cultural de Providencia,Actividades arte exposiciones y eventos,https://www.culturaprovidencia.cl/feed/,2,Cultura,38,Chile,es,True,0 +2925,La Tercera Cultura,Seccion de cultura arte musica literatura y espectaculos,https://www.latercera.com/cultura/feed/,2,Cultura,38,Chile,es,False,30 +2934,Meganoticias Cultura,Cultura espectaculos entretenimiento y musica,https://www.meganoticias.cl/cultura/rss/,2,Cultura,38,Chile,es,False,30 +2943,Ministerio de las Culturas Chile,Patrimonio arte literatura y politicas culturales,https://www.cultura.gob.cl/feed/,2,Cultura,38,Chile,es,True,0 +2941,Museo Nacional de Bellas Artes,Noticias de exposiciones y patrimonio,https://www.mnba.gob.cl/rss,2,Cultura,38,Chile,es,False,30 +2944,Santiago Cultura,Agenda cultural oficial de Santiago de Chile,https://www.santiagocultura.cl/feed/,2,Cultura,38,Chile,es,True,0 +2991,24Horas Deportes,Noticias deportivas del canal TVN,https://www.24horas.cl/deportes/rss,3,Deportes,38,Chile,es,False,30 +2989,ADN Deportes,Radio ADN con noticias deportivas y transmisiones,https://www.adnradio.cl/rss/category/deportes/,3,Deportes,38,Chile,es,False,30 +3001,AS Chile Deportes,Seccion chilena de AS con futbol internacional,https://chile.as.com/rss/futbol/chile.xml,3,Deportes,38,Chile,es,False,30 +2987,BioBioChile Deportes,Noticias deportivas chilenas y globales en tiempo real,https://www.biobiochile.cl/noticias/deportes/feed,3,Deportes,38,Chile,es,False,30 +2988,Cooperativa Deportes,Cobertura de futbol local seleccion chilena y otros deportes,https://www.cooperativa.cl/deportes/site/tax/port/rss_3___1.xml,3,Deportes,38,Chile,es,False,30 +2986,Emol Deportes,Seccion de deportes futbol tenis seleccion chilena y ligas,https://www.emol.com/rss/deportes.xml,3,Deportes,38,Chile,es,False,5 +3003,Futbol Chile News,Portal enfocado en futbol chileno selecciones y ligas,https://www.futbolchileno.com/feed/,3,Deportes,38,Chile,es,True,0 +3002,La Cuarta Deportes,Futbol nacional espectaculos deportivos y seleccion,https://www.lacuarta.com/feed/,3,Deportes,38,Chile,es,False,30 +2878,La Nacion Deportes,Seccion deportiva de La Nacion Chile,https://www.lanacion.cl/feed/,3,Deportes,38,Chile,es,True,0 +2985,La Tercera Deportes,Noticias de futbol deportes nacionales e internacionales,https://www.latercera.com/deportes/feed/,3,Deportes,38,Chile,es,False,30 +2990,Meganoticias Deportes,Ligas locales futbol internacional y deportes varios,https://www.meganoticias.cl/deportes/rss/,3,Deportes,38,Chile,es,False,30 +3004,Prensa Futbol,Noticias futboleras de Chile y mundo,https://www.prensafutbol.cl/feed/,3,Deportes,38,Chile,es,True,0 +2880,Publimetro Deportes,Noticias deportivas futbol tenis y deportes extremos,https://www.publimetro.cl/feed,3,Deportes,38,Chile,es,False,30 +2999,RedGol Chile,Portal deportivo especializado en futbol,https://redgol.cl/rss,3,Deportes,38,Chile,es,False,30 +2884,SoyChile Deportes,Red de diarios regionales con deportes locales y nacionales,https://www.soychile.cl/RSS.aspx,3,Deportes,38,Chile,es,False,30 +3000,TNT Sports Chile,Noticias deportivas de TNT Sports,https://www.tntsports.cl/rss,3,Deportes,38,Chile,es,False,30 +2893,24Horas Economia,Seccion de economia macro mercados y empresas,https://www.24horas.cl/economia/rss,4,Economía,38,Chile,es,False,30 +2896,AmCham Chile,Comercio inversiones y negocios Chile Estados Unidos,https://amchamchile.cl/feed/,4,Economía,38,Chile,es,True,0 +2904,Banco Central de Chile Comunicados,Informes monetarios financieros y comunicados economicos,https://www.bcentral.cl/rss,4,Economía,38,Chile,es,False,30 +2888,BioBioChile Economia,Noticias economicas nacionales finanzas y empresas,https://www.biobiochile.cl/noticias/economia/feed,4,Economía,38,Chile,es,False,30 +2898,Camchal Chile Alemania,Noticias economicas y comerciales Chile Alemania,https://www.camchal.cl/feed/,4,Economía,38,Chile,es,False,30 +2895,Chile Economico News,Agregado economico de empresas energia e inversion,https://www.chileeconomico.cl/feed/,4,Economía,38,Chile,es,False,30 +2902,Colegio Ingenieros Chile,Ingenieria industria productividad y economia,https://www.ingenieros.cl/feed/,4,Economía,38,Chile,es,True,0 +2903,Direcon ProChile Exportaciones,Comercio exterior exportaciones y tratados comerciales,https://www.prochile.gob.cl/feed/,4,Economía,38,Chile,es,False,30 +2887,El Mercurio Economia,Noticias economicas del diario El Mercurio,https://www.emol.com/rss/economia.xml,4,Economía,38,Chile,es,False,5 +2890,El Mostrador Mercados,Analisis economico empresas y politicas publicas,https://www.elmostrador.cl/mercados/feed/,4,Economía,38,Chile,es,False,30 +2886,La Tercera Pulso,Seccion economica Pulso con negocios macro mercados y empresas,https://www.latercera.com/pulso/feed/,4,Economía,38,Chile,es,False,30 +2894,Meganoticias Economia,Noticias de empresas mineria energia finanzas y mercado,https://www.meganoticias.cl/economia/rss/,4,Economía,38,Chile,es,False,30 +2901,Revista Edifica Construccion,Construccion arquitectura industria y proyectos nacionales,https://www.revistaenconcreto.cl/feed/,4,Economía,38,Chile,es,True,0 +2900,Revista Electro Industria,Energia electricidad redes y automatizacion industrial,https://www.electroindustria.cl/rss/,4,Economía,38,Chile,es,False,30 +2899,Revista Mineria Chilena,Noticias de mineria industria del cobre y energia,https://www.mch.cl/feed/,4,Economía,38,Chile,es,False,30 +2897,Sofofa News,Industria manufactura comercio exterior e innovacion,https://sofofa.cl/feed/,4,Economía,38,Chile,es,True,0 +2868,24Horas TVN,Servicio informativo del canal TVN con noticias nacionales e internacionales,https://www.24horas.cl/rss,7,Internacional,38,Chile,es,False,30 +2869,ADN Radio Chile,Noticias nacionales deportes politica y actualidad,https://www.adnradio.cl/rss/,7,Internacional,38,Chile,es,False,30 +2867,BioBioChile Noticias,Radio Bio Bio con noticias nacionales en tiempo real,https://www.biobiochile.cl/feed,7,Internacional,38,Chile,es,False,30 +2872,Ciper Chile,Periodismo de investigacion politica y sociedad,https://www.ciperchile.cl/feed/,7,Internacional,38,Chile,es,True,0 +2883,El Observador Chile,Medio regional con noticias generales locales y nacionales,https://www.elobservador.cl/feed/,7,Internacional,38,Chile,es,False,30 +2866,Emol El Mercurio,Portal de noticias generales de Chile con politica economia y actualidad,https://www.emol.com/rss,7,Internacional,38,Chile,es,False,5 +2865,La Tercera Noticias,Diario nacional con noticias de politica economia y sociedad en Chile,https://www.latercera.com/feed/,7,Internacional,38,Chile,es,False,30 +2873,Meganoticias Chile,Portal informativo de Mega con noticias generales,https://www.meganoticias.cl/rss/,7,Internacional,38,Chile,es,False,30 +2912,24Horas Medio Ambiente,Noticias ambientales estudios clima e investigacion,https://www.24horas.cl/ciencia-tecnologia/rss,8,Medio Ambiente,38,Chile,es,False,30 +3028,BioBioChile Medio Ambiente,Noticias ambientales investigacion ecologia y fauna,https://www.biobiochile.cl/noticias/ciencia-y-tecnologia/medio-ambiente/feed,8,Medio Ambiente,38,Chile,es,False,30 +3037,Chile Sustentable,Noticias ambientales politicas publicas y energia,https://www.chilesustentable.net/feed/,8,Medio Ambiente,38,Chile,es,False,30 +2870,Cooperativa Medio Ambiente,Informacion ambiental ecologia y sostenibilidad,https://www.cooperativa.cl/noticias/site/tax/port/rss_3___1.xml,8,Medio Ambiente,38,Chile,es,False,30 +2875,El Ciudadano Medio Ambiente,Ecologia sustentabilidad pueblos originarios y naturaleza,https://www.elciudadano.com/feed/,8,Medio Ambiente,38,Chile,es,False,30 +2879,El Desconcierto Medio Ambiente,Noticias ambientales crisis hidrica y ecosistemas,https://www.eldesconcierto.cl/feed/,8,Medio Ambiente,38,Chile,es,True,0 +2871,El Mostrador Medio Ambiente,Analisis ambiental politica ecologica y sostenibilidad,https://www.elmostrador.cl/feed/,8,Medio Ambiente,38,Chile,es,False,30 +3027,Emol Medio Ambiente,Ecosistemas medio ambiente energia y sostenibilidad,https://www.emol.com/rss/medioambiente.xml,8,Medio Ambiente,38,Chile,es,False,5 +3036,Fundacion Terram,Politica ambiental investigacion y conservacion,https://www.terram.cl/feed/,8,Medio Ambiente,38,Chile,es,True,0 +3043,Futuro 360 CNN Chile,Divulgacion cientifica y ambiental del canal CNN,https://www.cnnchile.com/rss/,8,Medio Ambiente,38,Chile,es,False,30 +3038,Greenpeace Chile,Acciones ambientales oceano clima y biodiversidad,https://www.greenpeace.org/chile/feed/,8,Medio Ambiente,38,Chile,es,True,0 +3026,La Tercera Medio Ambiente,Noticias ambientales cambio climatico y ecosistemas,https://www.latercera.com/etiqueta/medioambiente/feed/,8,Medio Ambiente,38,Chile,es,False,30 +3032,Meganoticias Medio Ambiente,Ecologia clima agua fauna y naturaleza,https://www.meganoticias.cl/medio-ambiente/rss/,8,Medio Ambiente,38,Chile,es,False,30 +3025,Ministerio del Medio Ambiente Chile,Comunicados politicas ambientales cambio climatico y biodiversidad,https://mma.gob.cl/feed/,8,Medio Ambiente,38,Chile,es,True,0 +3041,Oceana Chile,Oceano pesca sustentable ecosistemas marinos,https://chile.oceana.org/feed/,8,Medio Ambiente,38,Chile,es,True,0 +2844,PNUD Chile Medio Ambiente,Desarrollo sostenible politicas ambientales y estudios,https://www.undp.org/rss,8,Medio Ambiente,38,Chile,es,False,30 +3040,Parques Nacionales Chile CONAF,Noticias de parques bosques incendios y naturaleza,https://www.conaf.cl/feed/,8,Medio Ambiente,38,Chile,es,True,0 +3044,RedPE Red de Politica Energetica,Transicion energetica y medio ambiente,https://redpe.cl/feed/,8,Medio Ambiente,38,Chile,es,False,30 +2874,T13 Medio Ambiente,Noticias sobre contaminación clima biodiversidad y crisis hidrica,https://www.t13.cl/rss,8,Medio Ambiente,38,Chile,es,False,30 +3039,WCS Chile Wildlife Conservation Society,Conservacion de fauna ecosistemas y biodiversidad,https://chile.wcs.org/DesktopModules/DnnForge%20-%20NewsArticles/Rss.aspx,8,Medio Ambiente,38,Chile,es,True,0 +2951,24Horas Salud,Salud publica ciencia y bienestar en Chile,https://www.24horas.cl/salud/rss,12,Salud,38,Chile,es,False,30 +2950,BioBioChile Salud,Noticias de salud ciencia medicina y bienestar,https://www.biobiochile.cl/noticias/salud-y-bienestar/feed,12,Salud,38,Chile,es,False,30 +2955,Clinica Alemana Noticias,Noticias de salud investigacion clinica y medicina,https://www.alemana.cl/rss,12,Salud,38,Chile,es,False,30 +2956,Clinica Las Condes Salud,Informacion medica de especialistas y salud publica,https://www.clinicalascondes.cl/rss,12,Salud,38,Chile,es,False,30 +2957,Clinica Santa Maria Salud,Publicaciones medicas y contenidos de especialistas,https://www.clinicasantamaria.cl/rss,12,Salud,38,Chile,es,False,30 +2947,Colmed Colegio Medico Chile,Noticias medicas comunicados y salud publica,https://www.colmed.cl/feed/,12,Salud,38,Chile,es,False,30 +2949,Emol Salud,Noticias de salud medicina enfermedades y estudios,https://www.emol.com/rss/salud.xml,12,Salud,38,Chile,es,False,5 +2959,Fundacion Arturo Lopez Perez,Oncologia investigacion y noticias de salud,https://www.falp.cl/feed/,12,Salud,38,Chile,es,False,30 +2958,Hospital Clinico Universidad de Chile,Investigacion medicina y salud publica,https://www.redclinica.cl/rss,12,Salud,38,Chile,es,False,30 +2946,ISP Instituto de Salud Publica,Alertas sanitarias farmacos investigacion y salud publica,https://www.ispch.cl/feed/,12,Salud,38,Chile,es,True,0 +2948,La Tercera Salud,Seccion salud bienestar y ciencia medica,https://www.latercera.com/etiqueta/salud/feed/,12,Salud,38,Chile,es,False,30 +2953,Meganoticias Salud,Noticias sobre salud publica y bienestar,https://www.meganoticias.cl/salud/rss/,12,Salud,38,Chile,es,False,30 +2945,Ministerio de Salud Chile,Comunicados oficiales salud publica normativa y alertas,https://www.minsal.cl/feed/,12,Salud,38,Chile,es,False,30 +2971,24Horas Sociedad,Actualidad social reportajes y problemas comunitarios,https://www.24horas.cl/nacional/rss,13,Sociedad,38,Chile,es,False,30 +2968,BioBioChile Sociedad,Pueblos originarios comunidades y temas sociales,https://www.biobiochile.cl/noticias/nacional/chile/feed,13,Sociedad,38,Chile,es,False,30 +2882,Diario UChile Sociedad,Reportajes humanos sociedad politica y educacion,https://radio.uchile.cl/feed/,13,Sociedad,38,Chile,es,True,0 +2967,Emol Nacional,Noticias sociales del diario El Mercurio,https://www.emol.com/rss/nacional.xml,13,Sociedad,38,Chile,es,False,5 +2981,Fundacion Sol,Estudios sociales trabajo desigualdad y economia social,https://www.fundacionsol.cl/feed/,13,Sociedad,38,Chile,es,False,30 +2983,Instituto Nacional de Derechos Humanos INDH,Reportes sociales y derechos humanos,https://www.indh.cl/feed/,13,Sociedad,38,Chile,es,False,30 +2966,La Tercera Nacional,Actualidad nacional con fuerte enfoque social,https://www.latercera.com/nacional/feed/,13,Sociedad,38,Chile,es,False,30 +2965,La Tercera Sociedad,Noticias sociales reportajes y temas pais,https://www.latercera.com/etiqueta/sociedad/feed/,13,Sociedad,38,Chile,es,False,30 +2881,La Voz de los que Sobran Sociedad,Reportajes sociales enfoque en desigualdad y comunidades,https://lavozdelosquesobran.cl/feed/,13,Sociedad,38,Chile,es,True,0 +2970,Meganoticias Sociedad,Comunidad reportajes y temas de convivencia nacional,https://www.meganoticias.cl/nacional/rss/,13,Sociedad,38,Chile,es,False,30 +2982,Observatorio Ciudadano Chile,Derechos humanos pueblos originarios y sociedad civil,https://www.observatorio.cl/feed/,13,Sociedad,38,Chile,es,True,0 +2984,Servicio Jesuita a Migrantes Chile,Migracion refugiados y sociedad civil,https://sjmchile.org/feed/,13,Sociedad,38,Chile,es,True,0 +2876,The Clinic Sociedad,Reportajes ciudadanos cultura social y vida cotidiana,https://www.theclinic.cl/feed/,13,Sociedad,38,Chile,es,True,0 +2910,ADN Radio Tecnologia,Tecnologia videojuegos ciencia y cultura digital,https://www.adnradio.cl/rss/category/tecnologia/,14,Tecnología,38,Chile,es,False,30 +2906,BioBioChile Tecnologia,Seccion de tecnologia innovacion y ciencia,https://www.biobiochile.cl/noticias/ciencia-y-tecnologia/feed,14,Tecnología,38,Chile,es,False,30 +2877,Diario Financiero Innovacion,Innovacion tecnologia startups fintech y negocios tech,https://www.df.cl/rss,14,Tecnología,38,Chile,es,False,30 +2914,El Mostrador Tecnologia,Tecnologia digital startups innovacion y ciencia,https://www.elmostrador.cl/agenda-digital/feed/,14,Tecnología,38,Chile,es,False,30 +2908,Emol Tecnologia,Noticias de tecnologia ciencia empresas y digitalizacion,https://www.emol.com/rss/tecnologia.xml,14,Tecnología,38,Chile,es,False,5 +2905,FayerWayer Chile,Medio tecnologico con noticias de ciencia apps moviles y cultura digital,https://www.fayerwayer.com/feed/,14,Tecnología,38,Chile,es,True,0 +2920,IBM Chile News,Noticias de tecnologia empresarial e inteligencia artificial,https://newsroom.ibm.com/rss-feeds,14,Tecnología,38,Chile,en,False,30 +2907,La Tercera Laboratorio Digital,Ciencia tecnologia redes y transformacion digital,https://www.latercera.com/etiqueta/tecnologia/feed/,14,Tecnología,38,Chile,es,False,30 +2913,Meganoticias Tecnologia,Tecnologia consumo redes y ciencia,https://www.meganoticias.cl/tecnologia/rss/,14,Tecnología,38,Chile,es,False,30 +2921,Microsoft Chile News,Innovacion IA computacion nube ecosistema digital,https://news.microsoft.com/feed/,14,Tecnología,38,Chile,en,True,0 +2918,Startups Latam Chile,Emprendimiento innovacion y startups tecnológicas en Chile,https://startupslatam.com/feed/,14,Tecnología,38,Chile,es,True,0 +3176,Academy of Military Sciences Science,Investigacion militar energetica y tecnologica,https://www.amss.cn/rss,1,Ciencia,39,China,zh,False,30 +3173,Beihang University Aerospace,Aeroespacial robotica y sistemas autonomos,https://ev.buaa.edu.cn/rss.jsp,1,Ciencia,39,China,en,False,30 +3159,CGTN Science,Ciencia china fisica biologia y exploracion espacial,https://www.cgtn.com/rss/science,1,Ciencia,39,China,en,False,30 +3166,CNSA Updates,Agencia espacial china ciencia de misiones,https://www.cnsa.gov.cn/rss,1,Ciencia,39,China,zh,False,30 +3051,Caixin Science,Analisis cientifico biotecnologia y energia,https://www.caixinglobal.com/rss,1,Ciencia,39,China,en,False,30 +3156,China Daily Science,Seccion de ciencia tecnologia y avances cientificos,https://www.chinadaily.com.cn/rss/science_rss.xml,1,Ciencia,39,China,en,False,30 +3179,China Environmental Science Review,Ciencia ambiental energia aire y agua,https://www.cern.ac.cn/rss,1,Ciencia,39,China,zh,False,30 +3152,China Space News,Ciencia espacial cohetes satelites y misiones,https://english.spacechina.com/rss.xml,1,Ciencia,39,China,en,False,30 +3160,China.org.cn Science,Ciencia nacional energia ambiente y salud,https://www.china.org.cn/rss/science.xml,1,Ciencia,39,China,en,False,30 +3168,Chinese Academy of Engineering,Ingenieria investigacion energetica y ciencia aplicada,https://en.cae.cn/rss/,1,Ciencia,39,China,en,False,30 +3167,Chinese Academy of Sciences,Ciencia oficial fisica quimica robotica y biologia,https://english.cas.cn/rss,1,Ciencia,39,China,en,False,30 +3161,ECNS Science,Agencia china con ciencia biomedicina y tecnologia,https://www.ecns.cn/rss/sci-tech.xml,1,Ciencia,39,China,en,False,30 +3157,Global Times Science,Ciencia china medicina fisica energia y espacio,https://www.globaltimes.cn/rss/science.xml,1,Ciencia,39,China,en,False,30 +3175,HKUST Science,Investigacion en energia materiales fisica e ingenieria,https://www.ust.hk/rss,1,Ciencia,39,China,en,False,30 +3174,Hong Kong University Science,Ciencia investigacion medica y fisica,https://www.hku.hk/rss,1,Ciencia,39,China,en,True,0 +3178,Institute of High Energy Physics,Fisica de particulas aceleradores y cosmologia,https://english.ihep.cas.cn/rss,1,Ciencia,39,China,en,False,30 +3177,National Center for Nanoscience,Nanotecnologia materiales y fisica,https://www.nanoctr.cn/rss,1,Ciencia,39,China,zh,False,30 +3158,People s Daily Science,Ciencia estatal biotecnologia energia y espacio,https://english.peopledaily.com.cn/rss/science.xml,1,Ciencia,39,China,en,False,30 +3172,ShanghaiTech University Science,Investigacion avanzada en energia biotecnologia y IA,https://www.shanghaitech.edu.cn/rss,1,Ciencia,39,China,en,False,30 +3169,Tsinghua University Science,Investigacion cientifica de primera linea en China,https://www.tsinghua.edu.cn/rss.jsp,1,Ciencia,39,China,en,False,30 +3155,Xinhua Science,Ciencia investigacion y descubrimientos del sistema estatal chino,https://www.news.cn/english/rss/science.xml,1,Ciencia,39,China,en,False,30 +3191,China Culture Ministry,Ministerio de cultura eventos politicas y patrimonio,https://www.mcprc.gov.cn/rss,2,Cultura,39,China,zh,False,30 +3180,China Daily Culture,Arte cine literatura patrimonio y tradiciones,https://www.chinadaily.com.cn/rss/culture_rss.xml,2,Cultura,39,China,en,True,0 +3198,China Pictorial Culture,Fotografia historia arte y tradiciones,https://www.chinapictorial.com.cn/rss,2,Cultura,39,China,en,False,30 +3185,China.org.cn Culture,Patrimonio tradiciones festivales y exposiciones,https://www.china.org.cn/rss/culture.xml,2,Cultura,39,China,en,False,30 +3186,ECNS Culture,Artes conciertos literatura y eventos culturales chinos,https://www.ecns.cn/rss/culture.xml,2,Cultura,39,China,en,False,30 +3182,Global Times Culture,Cultura china contemporanea cine musica y artes escenicas,https://www.globaltimes.cn/rss/culture.xml,2,Cultura,39,China,en,False,30 +3204,Hong Kong Heritage Museum,Exposiciones de patrimonio chino y asiatico,https://www.heritagemuseum.gov.hk/en_US/web/hm/rss.html,2,Cultura,39,China,en,False,30 +3195,Nanjing Museum Cultura,Arte historia arqueologia y patrimonio,https://www.njmuseum.com/rss,2,Cultura,39,China,zh,False,30 +3192,National Art Museum of China,Exposiciones arte chino moderno y clasico,https://www.namoc.org/rss,2,Cultura,39,China,zh,False,30 +3196,National Library of China,Colecciones literatura eventos culturales y archivos,https://www.nlc.cn/rss,2,Cultura,39,China,zh,False,30 +3193,Palace Museum Beijing,Noticias y exposiciones del Museo de la Ciudad Prohibida,https://en.dpm.org.cn/rss,2,Cultura,39,China,en,False,30 +3183,People s Daily Culture,Patrimonio chino museos festivales y artes,https://english.peopledaily.com.cn/rss/culture.xml,2,Cultura,39,China,en,False,30 +3194,Shanghai Museum Cultura,Arte chino antiguedades e historia,https://www.shanghaimuseum.net/rss,2,Cultura,39,China,en,False,30 +3181,Xinhua Culture,Seccion cultural oficial con arte literatura y museos,https://www.news.cn/english/rss/culture.xml,2,Cultura,39,China,en,False,30 +3084,Asian Development Bank China Data,Proyectos de desarrollo e inversion,https://www.adb.org/rss,4,Economía,39,China,en,False,30 +3077,Bloomberg China,Economia china mercados yuan energia e industria,https://www.bloomberg.com/feed/podcast.xml,4,Economía,39,China,en,False,30 +3103,CEEW Asia China Energy,Investigacion economica de energia en Asia,https://www.ceew.in/rss.xml,4,Economía,39,China,en,True,0 +3069,CGTN Business,Economia china mercados internacionales comercio y crecimiento,https://www.cgtn.com/rss/business,4,Economía,39,China,en,False,30 +3098,China Agribusiness News,Agricultura mercados de granos y economia rural,https://www.agriculture.com/rss,4,Economía,39,China,en,False,30 +3085,China Banking News,Noticias de bancos regulacion crediticia y yuan digital,https://www.chinabankingnews.com/feed/,4,Economía,39,China,en,True,0 +3089,China Briefing Economy,Impuestos regulacion comercio e inversion extranjera,https://www.china-briefing.com/news/feed/,4,Economía,39,China,en,False,30 +3065,China Daily Business,Economia mercados comercio exterior y empresas,https://www.chinadaily.com.cn/rss/business_rss.xml,4,Economía,39,China,en,False,30 +3088,China Economic Review,Analisis de mercados chinos industria y empresas,https://chinaeconomicreview.com/feed/,4,Economía,39,China,en,True,0 +3100,China Logistics News,Logistica puertos rutas y comercio internacional,https://www.logisticsmanager.com/feed/,4,Economía,39,China,en,True,0 +3071,China News Service Business,Economia empresas y tendencias de consumo,https://www.ecns.cn/rss/business.xml,4,Economía,39,China,en,False,30 +3102,China Power Project CSIS,Analisis economico politico y financiero,https://www.csis.org/rss,4,Economía,39,China,en,False,30 +3104,China Transport News,Economia del transporte infraestructuras y comercio,https://www.transportweekly.com/news/rss,4,Economía,39,China,en,False,30 +3070,China.org.cn Business,Comercio exterior exportaciones importaciones e industria,https://www.china.org.cn/rss/business.xml,4,Economía,39,China,en,False,30 +3096,Energy China News,Energia renovable carbon petroleo y politica energetica china,https://www.energyportal.eu/feed/,4,Economía,39,China,en,True,0 +3067,Global Times Business,Economia politica comercial empresas y mercados,https://www.globaltimes.cn/rss/business.xml,4,Economía,39,China,en,False,30 +3082,IMF China,Comunicados y reportes del Fondo Monetario sobre China,https://www.imf.org/en/News/SiteIndex/FeedByRegion?region=APD,4,Economía,39,China,en,False,30 +3101,McKinsey China Insights,Analisis economico empresarial sobre China,https://www.mckinsey.com/featured-insights/rss,4,Economía,39,China,en,False,30 +3081,OECD China Reports,Informes economicos sobre China de la OCDE,https://www.oecd.org/newsroom/feeds/rss.xml,4,Economía,39,China,en,False,30 +3097,Oil Price China Market,Mercados energeticos y petroleo en China,https://oilprice.com/rss/main,4,Economía,39,China,en,True,0 +3068,People s Daily Business,Seccion economica oficial del Partido Comunista chino,https://english.peopledaily.com.cn/rss/business.xml,4,Economía,39,China,en,False,30 +3057,Reuters China Business,Cobertura empresarial global enfocada en China,https://www.reuters.com/rssFeed/chinaNews,4,Economía,39,China,en,False,30 +3091,Shanghai Stock Exchange News,Noticias del mercado bursatil chino,https://english.sse.com.cn/rss/pressrelease.xml,4,Economía,39,China,en,False,30 +3092,Shenzhen Stock Exchange News,Actualidad de empresas tecnológicas y financieras,https://www.szse.cn/api/report/ShowReport/data?SHOWTYPE=rss,4,Economía,39,China,en,False,30 +3078,The Economist China,Analisis economico y financiero especializado,https://www.economist.com/regions/china/rss.xml,4,Economía,39,China,en,False,30 +3066,Xinhua Finance,Agencia estatal con noticias financieras y de mercados,https://www.news.cn/english/rss/business.xml,4,Economía,39,China,en,False,30 +3086,Yicai Global,Economia china tecnologia fintech y mercados,https://www.yicaiglobal.com/news/rss,4,Economía,39,China,en,False,30 +3271,AsiaOne Entertainment,Entretenimiento asiatico cine y cultura pop China,https://www.asiaone.com/entertainment/rss.xml,6,Entretenimiento,39,China,en,False,30 +3275,Bilibili Entertainment,Streaming chino anime juegos y entretenimiento digital,https://www.bilibili.com/rss,6,Entretenimiento,39,China,zh,False,30 +3184,CGTN Entertainment,Cine musica premiaciones dramas y documentales,https://www.cgtn.com/rss/culture,6,Entretenimiento,39,China,en,False,30 +3200,CRI China Entertainment,Cine musica celebridades y entretenimiento cultural,https://chinaplus.cri.cn/rss,6,Entretenimiento,39,China,en,False,30 +3256,China Daily Entertainment,Entretenimiento cine musica arte y TV china,https://www.chinadaily.com.cn/rss/entertainment_rss.xml,6,Entretenimiento,39,China,en,True,0 +3278,China Film Insider,Cine chino industria filmica y mercado,https://chinafilminsider.com/feed/,6,Entretenimiento,39,China,en,True,0 +3260,China.org.cn Entertainment,TV china cine musica y espectaculos,https://www.china.org.cn/rss/entertainment.xml,6,Entretenimiento,39,China,en,False,30 +3276,Douban Movies,Cine chino reseñas y estrenos,https://www.douban.com/feed,6,Entretenimiento,39,China,zh,False,30 +3272,DramaPanda China,Series chinas dramas y entretenimiento digital,https://www.dramapanda.com/feeds/posts/default,6,Entretenimiento,39,China,en,True,0 +3211,ECNS Entertainment,Noticias de entretenimiento festivales y actores,https://www.ecns.cn/rss/life.xml,6,Entretenimiento,39,China,en,False,30 +3207,Global Times Life,Entretenimiento cultura pop eventos y celebridades,https://www.globaltimes.cn/rss/life.xml,6,Entretenimiento,39,China,en,False,30 +3273,Jomyut Entertainment Asia,Entretenimiento cine musica y dramas chinos,https://www.jomyut.com/rss,6,Entretenimiento,39,China,en,False,30 +3277,Mtime China,Cine estrenos box office y actores,https://news.mtime.com/rss,6,Entretenimiento,39,China,zh,False,30 +3258,People s Daily Entertainment,Entretenimiento estatal cine TV festivales y musica,https://english.peopledaily.com.cn/rss/entertainment.xml,6,Entretenimiento,39,China,en,False,30 +3274,QQ Music News,Musica china artistas y novedades digitales,https://y.qq.com/feed.xml,6,Entretenimiento,39,China,zh,False,30 +3052,Shanghai Daily Entertainment,Cine local TV eventos y entretenimiento urbano,https://www.shine.cn/rss/,6,Entretenimiento,39,China,en,False,30 +3203,TimeOut Beijing Entertainment,Entretenimiento cine musica exposiciones y ocio,https://www.timeoutbeijing.com/feeds/all,6,Entretenimiento,39,China,en,False,30 +3202,TimeOut Shanghai Entertainment,Eventos conciertos teatro cine y ocio urbano,https://www.timeoutshanghai.com/feeds/all,6,Entretenimiento,39,China,en,False,30 +3279,Variety Asia,Entretenimiento cine TV musica con foco en China,https://variety.com/feed/,6,Entretenimiento,39,China,en,True,0 +3255,Xinhua Entertainment,Cine musica TV celebridades y cultura pop en China,https://www.news.cn/english/rss/ent.xml,6,Entretenimiento,39,China,en,False,30 +3270,Yahoo HK Entertainment,Cobertura hongkonesa de cine musica y celebridades chinas,https://hk.news.yahoo.com/rss/entertainment,6,Entretenimiento,39,China,en,True,0 +3284,CGTN Environment,Energia verde clima fauna y sostenibilidad,https://www.cgtn.com/rss/environment,8,Medio Ambiente,39,China,en,False,30 +3301,Carbon Brief China,Analisis de emisiones energia y clima en China,https://www.carbonbrief.org/rss,8,Medio Ambiente,39,China,en,True,0 +3281,China Daily Environment,Medio ambiente energia clima y sostenibilidad,https://www.chinadaily.com.cn/rss/environment_rss.xml,8,Medio Ambiente,39,China,en,False,30 +3297,China Ecological Society,Investigacion ecologica biodiversidad y ambiente,https://www.ces.org.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3294,China Energy News,Energia industria electrica carbon y renovables,https://www.cnenergynews.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3300,China Energy Portal,Datos energia renovables carbon y emisiones,https://chinaenergyportal.org/feed/,8,Medio Ambiente,39,China,en,True,0 +3291,China Environmental Protection Ministry,Politicas ambientales y comunicados oficiales,https://www.mee.gov.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3293,China Meteorological Administration,Clima meteorologia contaminacion y alertas,https://www.cma.gov.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3296,China Water Affairs,Politicas de agua rios presas y recursos hidricos,https://www.water.gov.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3285,China.org.cn Environment,Cambio climatico energia y ecologia en China,https://www.china.org.cn/rss/environment.xml,8,Medio Ambiente,39,China,en,False,30 +3286,ECNS Environment,Contaminacion biodiversidad energia y clima,https://www.ecns.cn/rss/environment.xml,8,Medio Ambiente,39,China,en,False,30 +3282,Global Times Environment,Medio ambiente clima biodiversidad y energia,https://www.globaltimes.cn/rss/environment.xml,8,Medio Ambiente,39,China,en,False,30 +3302,ICLEI East Asia,Desarrollo sostenible y ciudades verdes en China,https://iclei-eas.org/feed/,8,Medio Ambiente,39,China,en,False,30 +3292,National Forestry and Grassland Administration,Bosques fauna areas protegidas y biodiversidad,https://www.forestry.gov.cn/rss,8,Medio Ambiente,39,China,zh,False,5 +3283,People s Daily Environment,Politicas ambientales aire agua y energia,https://english.peopledaily.com.cn/rss/environment.xml,8,Medio Ambiente,39,China,en,False,30 +3141,Sixth Tone Environment,Reportajes ambientales contaminacion y biodiversidad,https://www.sixthtone.com/rss,8,Medio Ambiente,39,China,en,True,0 +3295,State Grid Energy News,Energia electrica renovables y politica energetica,https://www.sgcc.com.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3094,SupChina Environment,Energia ecologia y clima en China moderna,https://supchina.com/feed/,8,Medio Ambiente,39,China,en,False,30 +3140,The China Project Environment,Politicas verdes energia renovable y clima,https://thechinaproject.com/feed/,8,Medio Ambiente,39,China,en,True,0 +3299,WWF China,Fauna biodiversidad y conservacion,https://www.wwf.org.cn/rss,8,Medio Ambiente,39,China,zh,False,30 +3280,Xinhua Environment,Ecologia clima energia y politicas ambientales,https://www.news.cn/english/rss/environment.xml,8,Medio Ambiente,39,China,en,False,30 +3058,BBC News China Politics,Cobertura politica independiente sobre China,https://feeds.bbci.co.uk/news/world/asia/china/rss.xml,11,Política,39,China,en,True,0 +3049,CCTV Politics,Television estatal CCTV con noticias politicas nacionales,https://english.cctv.com/rss/politics/index.xml,11,Política,39,China,en,False,30 +3053,CGTN Politics,Canal internacional del gobierno chino con cobertura politica,https://www.cgtn.com/rss/column/politics,11,Política,39,China,en,False,30 +3046,China Daily Politics,Diario estatal chino en ingles con noticias politicas y oficiales,https://www.chinadaily.com.cn/rss/politics_rss.xml,11,Política,39,China,en,False,30 +3055,China News Service Politics,Segunda agencia estatal china con comunicados politicos,https://www.ecns.cn/rss/politics.xml,11,Política,39,China,en,False,30 +3054,China.org.cn Politics,Portal oficial en ingles con noticias del Partido y el estado,https://www.china.org.cn/rss/politics.xml,11,Política,39,China,en,False,30 +3047,Global Times Politics,Medio pro gubernamental con noticias politicas y geoestrategicas,https://www.globaltimes.cn/rss/politics.xml,11,Política,39,China,en,False,30 +3063,Jamestown Foundation China Brief,Analisis politico militar y estrategico sobre China,https://jamestown.org/feed/,11,Política,39,China,en,False,30 +3048,People s Daily Politics,Diario oficial del Partido Comunista Chino con noticias politicas,https://english.peopledaily.com.cn/rss/politics.xml,11,Política,39,China,en,False,30 +3061,RFA Radio Free Asia China,Medio enfocado en derechos humanos y politica interna,https://www.rfa.org/english/rss,11,Política,39,China,en,True,0 +3059,The Guardian China Politics,Analisis occidental de la politica china,https://www.theguardian.com/world/china/rss,11,Política,39,China,en,True,0 +3060,VOA China,Noticias politicas criticas del gobierno chino,https://www.voachinese.com/api/zpipveiqkm,11,Política,39,China,zh,False,30 +3045,Xinhua Politics,Agencia oficial china con comunicados politicos del gobierno central,https://www.news.cn/english/rss/politics.xml,11,Política,39,China,en,False,30 +3209,CGTN Health,Salud bienestar medicina y ciencia medica,https://www.cgtn.com/rss/health,12,Salud,39,China,en,False,30 +3214,China CDC Health,Centro chino de control de enfermedades,https://www.chinacdc.cn/en/rss,12,Salud,39,China,en,False,30 +3206,China Daily Health,Salud medicina nutricion y bienestar,https://www.chinadaily.com.cn/rss/health_rss.xml,12,Salud,39,China,en,False,30 +3225,China Hospital News,Noticias de hospitales medicina y salud,https://www.chinahospital.com/rss,12,Salud,39,China,zh,False,30 +3221,China Medical Tribune,Medicina clinica especialistas y salud profesional,https://www.cmt.com.cn/rss,12,Salud,39,China,zh,False,30 +3224,China Nutrition Society,Nutricion bienestar y salud,https://www.cnsoc.org/rss,12,Salud,39,China,zh,False,30 +3222,China Pharmaceutical News,Industria farmaceutica regulacion y medicamentos,https://www.cphmedia.com/rss,12,Salud,39,China,zh,True,0 +3210,China.org.cn Health,Noticias de salud medicina vacunas y prevencion,https://www.china.org.cn/rss/health.xml,12,Salud,39,China,en,False,30 +3216,Chinese Academy of Medical Sciences,Investigacion medica ciencia y salud,https://www.cams.cn/rss,12,Salud,39,China,zh,False,30 +3223,DrugNews China,Noticias de farmacos normativa y aprobaciones,https://drugnews.cn/rss,12,Salud,39,China,en,False,30 +3171,Fudan University Health,Medicina investigacion salud publica y ciencia,https://www.fudan.edu.cn/rss,12,Salud,39,China,zh,False,30 +3215,National Health Commission China,Comunicados oficiales de salud publica,https://www.nhc.gov.cn/rss,12,Salud,39,China,zh,False,30 +3227,Nature China Health,Investigacion global con foco en ciencia china,https://www.nature.com/subjects/china.rss,12,Salud,39,China,en,False,30 +3217,Peking Union Medical College,Salud investigacion medica y estudios clinicos,https://www.pumc.edu.cn/rss,12,Salud,39,China,zh,False,30 +3170,Peking University Health Science,Investigacion medica y avances clinicos,https://english.pku.edu.cn/rss.aspx,12,Salud,39,China,en,False,30 +3208,People s Daily Health,Salud medica bienestar y prevencion,https://english.peopledaily.com.cn/rss/health.xml,12,Salud,39,China,en,False,30 +3218,Shanghai Medical College,Investigacion clinica y salud publica,https://www.shmc.edu.cn/rss,12,Salud,39,China,zh,False,30 +3228,The Lancet Asia Health,Salud publica y medicina clinica en Asia y China,https://www.thelancet.com/rssfeed/asia,12,Salud,39,China,en,False,30 +3226,Traditional Chinese Medicine News,Medicina tradicional china y tratamientos,https://www.tcmnews.cn/rss,12,Salud,39,China,zh,False,30 +3205,Xinhua Health,Salud publica medicina y comunicados oficiales,https://www.news.cn/english/rss/health.xml,12,Salud,39,China,en,False,30 +3251,Amnesty International China,Politicas sociales derechos y poblacion,https://www.amnesty.org/en/location/asia-and-the-pacific/east-asia/china/feed/,13,Sociedad,39,China,en,True,0 +3197,Beijing Review Society,Sociedad gobierno local cultura y poblacion,https://www.bjreview.com/rss,13,Sociedad,39,China,en,True,0 +3234,CGTN Society,Reportajes sobre vida social educacion y poblacion,https://www.cgtn.com/rss/china,13,Sociedad,39,China,en,False,30 +3231,China Daily Society,Reportajes sociales demografia educacion y vida cotidiana,https://www.chinadaily.com.cn/rss/china_rss.xml,13,Sociedad,39,China,en,True,0 +3248,China Family Planning News,Demografia natalidad poblacion y politicas familiares,https://www.chinapop.gov.cn/rss,13,Sociedad,39,China,zh,False,30 +3093,China Labour Bulletin,Trabajo derechos laborales y condiciones sociales,https://clb.org.hk/en/rss.xml,13,Sociedad,39,China,en,False,30 +3199,China Today Society,Temas sociales desigualdad vivienda y convivencia,https://www.chinatoday.com.cn/rss,13,Sociedad,39,China,en,False,30 +3247,China Women News,Mujeres igualdad genero familia y sociedad,https://www.cnwomen.com.cn/rss,13,Sociedad,39,China,zh,False,30 +3246,China Youth Daily Society,Juventud educacion empleo y sociedad,https://www.cyol.com/rss,13,Sociedad,39,China,zh,False,30 +3235,China.org.cn Society,Vida social politicas demograficas y migracion,https://www.china.org.cn/rss/china.xml,13,Sociedad,39,China,en,False,30 +3236,ECNS Society,Noticias sociales urbanizacion familia y migracion interna,https://www.ecns.cn/rss/china.xml,13,Sociedad,39,China,en,False,30 +3232,Global Times Society,Sociedad china migracion trabajo juventud y problemas sociales,https://www.globaltimes.cn/rss/china.xml,13,Sociedad,39,China,en,False,30 +3250,Human Rights Watch China,Derechos humanos sociedad civil y vigilancia social,https://www.hrw.org/asia/china/rss.xml,13,Sociedad,39,China,en,False,30 +2756,ILO China Labour,Trabajo empleo migracion y condiciones sociales,https://www.ilo.org/global/lang--en/index.htm?rss=true,13,Sociedad,39,China,en,False,30 +3233,People s Daily Society,Sociedad comunitaria ciudadania y poblacion,https://english.peopledaily.com.cn/rss/china.xml,13,Sociedad,39,China,en,False,30 +3230,Xinhua Society,Temas sociales poblacion trabajo vivienda y comunidad,https://www.news.cn/english/rss/society.xml,13,Sociedad,39,China,en,False,30 +3245,Xinmin Evening News Society,Noticias sociales urbanas de Shanghai,https://www.xinmin.cn/rss,13,Sociedad,39,China,zh,False,30 +3116,36Kr Global,Startups chinas tecnologia IA y finanzas tech,https://36kr.com/feed,14,Tecnología,39,China,zh,True,0 +3122,Alibaba Cloud Blog,Innovacion IA nube big data y ciberseguridad,https://www.alibabacloud.com/blog/rss,14,Tecnología,39,China,en,False,30 +3153,Alibaba DAMO Academy,IA robotica computacion en nube y seguridad,https://damo.alibaba.com/feed,14,Tecnología,39,China,en,False,30 +3130,Ant Financial Tech,Fintech blockchain IA pagos digitales,https://www.antgroup.com/en/rss,14,Tecnología,39,China,en,False,30 +3137,AsiaTechDaily China,Startups inversion IA y tecnologia asiatica con foco en China,https://www.asiatechdaily.com/feed/,14,Tecnología,39,China,en,False,30 +3133,BYD Tech News,Movilidad electrica energia y vehiculos,https://www.byd.com/en/rss,14,Tecnología,39,China,en,False,30 +3154,Baidu Research,IA modelos de lenguaje vision y aprendizaje profundo,https://research.baidu.com/rss,14,Tecnología,39,China,en,False,30 +3121,Baidu Tech News,Ciencia computacional IA y desarrollo digital,https://feed.baidu.com/feed/api/landingpage,14,Tecnología,39,China,zh,False,30 +3128,Bytedance Engineering,IA algoritmos ciencia de datos y desarrollo,https://blog.bytedance.com/en/feed/,14,Tecnología,39,China,en,False,30 +3109,CGTN Tech,Tecnologia china IA robótica e innovacion,https://www.cgtn.com/rss/tech,14,Tecnología,39,China,en,False,30 +3148,Canalys China,Datos de mercado dispositivos y analisis del sector tecnologico,https://www.canalys.com/feed,14,Tecnología,39,China,en,False,30 +3105,China Daily Tech,Tecnologia innovacion IA telecom y empresas tech,https://www.chinadaily.com.cn/rss/tech_rss.xml,14,Tecnología,39,China,en,False,30 +3135,China Internet Watch,Analisis de tendencias digitales y tecnologia en China,https://www.chinainternetwatch.com/feed/,14,Tecnología,39,China,en,False,30 +3111,China News Service Tech,Tecnologia digital empresas y ciencia,https://www.ecns.cn/rss/tech.xml,14,Tecnología,39,China,en,False,30 +3136,China TechNews,Portal especializado en noticias de tecnologia china,https://technews.tw/feed/,14,Tecnología,39,China,zh,True,0 +3110,China.org.cn Tech,Cobertura de ciencia y tecnologia desde China,https://www.china.org.cn/rss/tech.xml,14,Tecnología,39,China,en,False,30 +3147,Counterpoint Research China,Analisis del mercado de moviles y tecnologia en China,https://www.counterpointresearch.com/feed/,14,Tecnología,39,China,en,True,0 +3131,DJI Tech News,Robots drones fotografia y hardware profesional,https://www.dji.com/newsroom/news/rss,14,Tecnología,39,China,en,False,30 +3142,Digitimes Asia Chips,Noticias sobre hardware chips y cadena de suministro asiatica,https://www.digitimes.com/rss.asp,14,Tecnología,39,China,en,False,30 +3145,GSMArena Moviles China,Noticias y fichas de moviles relevantes para marcas chinas,https://www.gsmarena.com/rss-news-reviews.php3,14,Tecnología,39,China,en,True,0 +3150,Gartner Tech Asia,Analisis de tecnologia empresarial IA nube y seguridad,https://www.gartner.com/en/newsroom/rss,14,Tecnología,39,China,en,False,30 +3134,Geely Tech News,Movilidad tecnologia vehiculos y futuro industrial,https://global.geely.com/feed/,14,Tecnología,39,China,en,False,30 +3144,GizmoChina,Moviles tecnologia de consumo y ecosistema de marcas chinas,https://www.gizmochina.com/feed/,14,Tecnología,39,China,en,True,0 +3106,Global Times Tech,Noticias tecnicas IA seguridad y ciencia china,https://www.globaltimes.cn/rss/tech.xml,14,Tecnología,39,China,en,False,30 +3123,Huawei Newsroom Tech,Tecnologia 5G redes IA y hardware empresarial,https://www.huawei.com/en/rss,14,Tecnología,39,China,en,False,30 +3149,IDC China,Informes sobre mercado tecnologico empresarial y consumo,https://www.idc.com/rss,14,Tecnología,39,China,en,False,30 +3138,KrASIA China Tech,Startups innovacion y ecosistema tecnologico chino,https://kr-asia.com/feed,14,Tecnología,39,China,en,False,30 +3124,Lenovo Tech News,Innovacion IA hardware PC y cloud,https://news.lenovo.com/feed/,14,Tecnología,39,China,en,True,0 +3132,NIO Tech News,Tecnologia vehiculos electricos y baterias,https://www.nio.com/news/rss,14,Tecnología,39,China,en,False,30 +3120,NetEase Tech,Espacio tecnologico de NetEase lider en digitalizacion,https://news.163.com/special/00019K7N/rss_newtech.xml,14,Tecnología,39,China,zh,False,30 +3056,Nikkei Asia China Tech,Analisis de empresas tecnologicas chinas,https://asia.nikkei.com/rss/feed/nar,14,Tecnología,39,China,en,True,0 +3126,Oppo News,Moviles IA fotografia y ecosistema digital,https://press.oppo.com/feed/,14,Tecnología,39,China,en,False,30 +3108,People s Daily Tech,Tecnologia digital IA telecom y politica cientifica,https://english.peopledaily.com.cn/rss/tech.xml,14,Tecnología,39,China,en,False,30 +3143,Semiconductor Digest Asia,Informacion sobre semiconductores litografia y hardware,https://www.semiconductor-digest.com/feed/,14,Tecnología,39,China,en,True,0 +3117,Sina Tech China,Portal tecnologico chino con noticias diarias,https://feed.sina.com.cn/tech.xml,14,Tecnología,39,China,zh,False,30 +3119,Sohu IT,Tecnologia china IA dispositivos y ciberseguridad,https://www.sohu.com/rss,14,Tecnología,39,China,zh,False,30 +3118,Tencent News Tech,Seccion tecnologia del portal de Tencent,https://xw.qq.com/feed,14,Tecnología,39,China,zh,False,30 +3129,TikTok Engineering,IA algoritmos sistemas distribuidos y seguridad,https://newsroom.tiktok.com/en-us/rss,14,Tecnología,39,China,en,False,30 +3127,Vivo Tech News,Innovacion en moviles fotografia y hardware,https://www.vivo.com/en/rss,14,Tecnología,39,China,en,False,30 +3125,Xiaomi Global News,Hardware innovacion y tecnologia de consumo,https://blog.mi.com/en/feed/,14,Tecnología,39,China,en,False,30 +3107,Xinhua Tech,Agencia estatal con noticias tecnicas y de ciencia aplicada,https://www.news.cn/english/rss/tech.xml,14,Tecnología,39,China,en,False,30 +3348,Centre for Energy Research Cyprus,Ciencia energetica renovables y sostenibilidad,https://energy.gov.cy/rss,1,Ciencia,40,Chipre,en,False,30 +3336,Cyprus Department of Meteorology,Ciencia del clima meteorologia y datos ambientales,https://www.moa.gov.cy/moa/ms/ms.nsf/rss_en/rss_en?opendocument,1,Ciencia,40,Chipre,en,False,30 +3338,Cyprus Environment Ministry Science,Biodiversidad ciencia ambiental y clima,https://www.moa.gov.cy/moa/environment/environment.nsf/rss_en/rss_en?opendocument,1,Ciencia,40,Chipre,en,False,30 +3337,Cyprus Geological Survey,Geologia vulcanismo minerales y estudios cientificos,https://www.moa.gov.cy/moa/gsd/gsd.nsf/rss_en/rss_en?opendocument,1,Ciencia,40,Chipre,en,False,30 +3342,EMSC Seismic Science Chipre,Sismologia terremotos y datos geofisicos de Chipre,https://www.emsc-csem.org/rss.php,1,Ciencia,40,Chipre,en,False,30 +3339,EuroNews Cyprus Science,Ciencia europea con articulos dedicados a Chipre,https://www.euronews.com/rss?level=theme&name=science,1,Ciencia,40,Chipre,en,True,0 +3347,European Environment Agency Cyprus,Datos cientificos ambientales para Chipre,https://www.eea.europa.eu/en/rss,1,Ciencia,40,Chipre,en,False,30 +3343,European Marine Board Cyprus,Ciencia marina estudios costeros y clima,https://www.marineboard.eu/rss,1,Ciencia,40,Chipre,en,False,30 +3340,European Space Agency Cyprus,Colaboracion espacial con Chipre y ciencia espacial,https://www.esa.int/rssfeed/Our_Activities/Space_Science,1,Ciencia,40,Chipre,en,True,0 +3346,Health Sciences Cyprus,Investigacion biomedica y publicaciones cientificas,https://www.healthsciencescyprus.com/feed/,1,Ciencia,40,Chipre,en,False,30 +3349,Marine Science Cyprus,Ciencia marina biodiversidad y ecosistemas,https://www.marine-science.org/feed/,1,Ciencia,40,Chipre,en,False,30 +3341,MedPan Mediterranean Science,Ciencia ambiental marina en Chipre y Mediterraneo,https://medpan.org/feed/,1,Ciencia,40,Chipre,en,False,30 +3344,Nature Asia Cyprus,Estudios cientificos globales con entradas sobre Chipre,https://www.nature.com/subjects/rss,1,Ciencia,40,Chipre,en,False,30 +3333,Open University of Cyprus Research,Ciencia computacional educacion e investigacion,https://www.ouc.ac.cy/web/guest/rss,1,Ciencia,40,Chipre,en,False,30 +3345,Science Business EU Cyprus,Ciencia innovacion y proyectos en Chipre,https://sciencebusiness.net/rss.xml,1,Ciencia,40,Chipre,en,False,30 +3359,CVAR Centre of Visual Arts,Arte chipriota y europeo exposiciones y archivo,https://cvar.severis.org/en/feed/,2,Cultura,40,Chipre,en,False,30 +3369,Cyprus Cultural Foundation,Historia arte literatura y patrimonio,https://www.culturalfoundation.org.cy/rss,2,Cultura,40,Chipre,en,False,30 +3357,Cyprus Department of Antiquities,Tradicion arqueologia patrimonio y hallazgos,https://www.mcw.gov.cy/mcw/DA/DA.nsf/rss_en/rss_en?opendocument,2,Cultura,40,Chipre,en,False,30 +3364,Cyprus Film Days,Eventos de cine independiente y cine europeo en Chipre,https://filmfestival.com.cy/feed/,2,Cultura,40,Chipre,en,False,30 +3356,Cyprus Museum News,Patrimonio arqueologico e historia antigua de Chipre,https://www.mcw.gov.cy/mcw/da/da.nsf/rss_en/rss_en?opendocument,2,Cultura,40,Chipre,en,False,30 +3350,Visit Cyprus Culture,Cultura patrimonio eventos museos y tradiciones,https://www.visitcyprus.com/rss.xml,2,Cultura,40,Chipre,en,False,30 +3468,AEL Limassol FC News,Noticias del club ligas y competiciones,https://aelfc.com/rss,3,Deportes,40,Chipre,en,True,0 +3466,APOEL FC News,Club de futbol noticias y competiciones,https://apoelfc.com.cy/rss,3,Deportes,40,Chipre,en,False,30 +3469,Anorthosis Famagusta News,Club de futbol noticias y resultados,https://anorthosisfc.com.cy/rss,3,Deportes,40,Chipre,en,True,0 +3473,Cyprus Athletic Federation,Atletismo carreras maraton y competiciones,https://www.koeas.org.cy/rss,3,Deportes,40,Chipre,en,True,0 +3471,Cyprus Basketball Federation,Baloncesto federacion ligas y competiciones,https://cbf.org.cy/rss,3,Deportes,40,Chipre,en,False,30 +3465,Cyprus Football Association,Futbol ligas copas y seleccion nacional,https://www.cfa.com.cy/rss,3,Deportes,40,Chipre,en,False,30 +3472,Cyprus Volleyball Federation,Voleibol federacion y competiciones,https://www.volleyball.org.cy/rss,3,Deportes,40,Chipre,en,False,30 +3467,Omonia FC News,Club de futbol noticias partidos y competiciones,https://omonia.com.cy/feed/,3,Deportes,40,Chipre,en,False,30 +3330,UCy Sports University,Deporte universitario competiciones y actividad fisica,https://www.ucy.ac.cy/en/feed/,3,Deportes,40,Chipre,en,False,30 +3474,World Football Cyprus,Informacion internacional de futbol con enfoque Chipre,https://www.worldfootball.net/rss/news/,3,Deportes,40,Chipre,en,False,30 +3383,Cyprus Central Bank,Economia financiera banca politica monetaria,https://www.centralbank.cy/en/rss,4,Economía,40,Chipre,en,False,30 +3382,Cyprus Chamber of Commerce,Negocios empresas exportaciones y economia,https://www.ccci.org.cy/rss,4,Economía,40,Chipre,en,True,0 +3381,Cyprus Investment Promotion Agency,Inversiones comercio y desarrollo economico,https://www.investcyprus.org.cy/rss,4,Economía,40,Chipre,en,True,0 +3384,Cyprus Ministry of Finance,Economia nacional presupuestos y fiscalidad,https://www.mof.gov.cy/rss,4,Economía,40,Chipre,en,False,30 +3385,Cyprus Property News,Economia inmobiliaria y bienes raices,https://www.cypruspropertynews.com/feed/,4,Economía,40,Chipre,en,False,30 +3386,Cyprus Shipping News,Economia maritima barcos puertos y comercio,https://cyprusshippingnews.com/feed/,4,Economía,40,Chipre,en,True,0 +3380,EuroNews Cyprus Economy,Economia europea y su impacto en Chipre,https://www.euronews.com/rss?level=theme&name=business,4,Economía,40,Chipre,en,True,0 +3390,Eurostat Cyprus,Datos estadisticos economia empleo e industria,https://ec.europa.eu/eurostat/en/web/rss,4,Economía,40,Chipre,en,False,30 +3393,Moodys Cyprus,Calificaciones financieras y riesgo economico,https://www.moodys.com/rss,4,Economía,40,Chipre,en,False,30 +3392,OECD Cyprus Economy,Analisis economico politicas y comparativas,https://www.oecd.org/rss,4,Economía,40,Chipre,en,False,30 +3377,Reporter Cyprus Business,Economia turismo negocios y empleo,https://reporter.com.cy/rss,4,Economía,40,Chipre,el,False,30 +3394,SandP Cyprus Ratings,Analisis economico ratings financieros y riesgo,https://www.spglobal.com/ratings/en/rss,4,Economía,40,Chipre,en,False,30 +3252,UN Cyprus Development,Desarrollo economico comercio y sostenibilidad,https://china.un.org/rss.xml,4,Economía,40,Chipre,en,True,0 +3423,AlphaNews Entertainment,Entretenimiento TV musica vida nocturna y espectaculos,https://www.alphanews.live/rss,6,Entretenimiento,40,Chipre,el,True,0 +3365,Ayia Napa Events,Entretenimiento playa musica electronica y ocio,https://www.ayianapa.org.cy/rss,6,Entretenimiento,40,Chipre,en,False,30 +3485,Cyprus Events Guide,Eventos culturales conciertos festivales y ocio,https://www.cyprusevents.net/feed/,6,Entretenimiento,40,Chipre,en,True,0 +3351,Cyprus Mail Entertainment,Cine musica eventos y ocio en Chipre,https://cyprus-mail.com/feed/,6,Entretenimiento,40,Chipre,en,True,0 +3352,Cyprus News Agency Entertainment,Eventos culturales TV festivales y ocio,https://www.cna.org.cy/webnews-en.aspx?rss=1,6,Entretenimiento,40,Chipre,en,False,30 +3362,Cyprus Symphony Orchestra Events,Musica clasica conciertos y espectaculos,https://www.cyso.org.cy/rss,6,Entretenimiento,40,Chipre,en,False,30 +3363,Cyprus Theatre Organisation THOC,Teatro obras estrenos y espectaculos,https://thoc.org.cy/rss,6,Entretenimiento,40,Chipre,en,False,30 +3378,Cyprus Times Entertainment,Música TV espectáculos y ocio urbano,https://cyprustimes.com/feed/,6,Entretenimiento,40,Chipre,en,True,0 +3360,European University Cyprus Arts,Actividades culturales teatro musica y exposiciones,https://www.euc.ac.cy/rss,6,Entretenimiento,40,Chipre,en,True,0 +3372,InCyprus Entertainment,Entretenimiento local cine TV y vida nocturna,https://in-cyprus.philenews.com/feed/,6,Entretenimiento,40,Chipre,en,True,0 +3376,Kathimerini Cyprus Entertainment,Cine musica espectaculos y eventos,https://kathimerini.com.cy/rss,6,Entretenimiento,40,Chipre,el,False,30 +3366,Larnaka Region Events,Eventos musica ferias cine y espectaculos,https://larnakaregion.com/rss,6,Entretenimiento,40,Chipre,en,False,30 +3358,Leventis Municipal Museum Events,Eventos culturales exposiciones y actividades,https://www.leventismuseum.org.cy/rss,6,Entretenimiento,40,Chipre,en,True,0 +3368,Limassol Tourism Events,Ocio cultural conciertos exposiciones y vida nocturna,https://www.limassoltourism.com/en/rss,6,Entretenimiento,40,Chipre,en,False,30 +3379,Offsite Entertainment,Noticias de entretenimiento conciertos y eventos,https://www.offsite.com.cy/rss,6,Entretenimiento,40,Chipre,el,False,30 +3367,Pafos Entertainment,Conciertos arte eventos y espectaculos en Pafos,https://www.visitpafos.org.cy/rss,6,Entretenimiento,40,Chipre,en,True,0 +3354,Phileleftheros Entertainment,Cine TV musica y agenda de ocio,https://www.philenews.com/rss,6,Entretenimiento,40,Chipre,el,True,0 +3353,Politis Entertainment,Entretenimiento local eventos y cultura pop,https://politis.com.cy/feed/,6,Entretenimiento,40,Chipre,el,True,0 +3424,SigmaLive Entertainment,Espectaculos TV musica y entretenimiento digital,https://www.sigmalive.com/rss,6,Entretenimiento,40,Chipre,el,True,0 +3355,TimeOut Cyprus,Ocio urbano restaurantes bares conciertos y eventos,https://www.timeoutcyprus.com/feeds/all,6,Entretenimiento,40,Chipre,en,False,30 +3429,Caritas Cyprus Health,Ayuda social salud bienestar y asistencia,https://caritas.cy/feed/,12,Salud,40,Chipre,en,False,30 +3335,Cyprus Institute of Neurology and Genetics,Investigacion medica genetica y salud avanzada,https://www.cing.ac.cy/rss,12,Salud,40,Chipre,en,False,30 +3428,Cyprus Red Cross Health,Accion humanitaria salud comunitaria y asistencia,https://www.redcross.org.cy/rss,12,Salud,40,Chipre,en,False,30 +3452,EuroHealthNet Cyprus,Salud publica bienestar y politicas sanitarias,https://eurohealthnet.eu/feed/,12,Salud,40,Chipre,en,True,0 +3451,European Centre for Disease Prevention ECDC,Enfermedades contagiosas salud publica europea,https://www.ecdc.europa.eu/en/news-and-events/rss,12,Salud,40,Chipre,en,False,30 +3449,European Medicines Agency Cyprus,Regulacion farmaceutica noticias de medicamentos,https://www.ema.europa.eu/en/news-events/press-releases/rss,12,Salud,40,Chipre,en,False,30 +3453,European Public Health Alliance Cyprus,Politicas sanitarias y salud europea,https://epha.org/feed/,12,Salud,40,Chipre,en,True,0 +3445,GESY Cyprus Health System,Sistema de salud chipriota atencion y servicios,https://www.gesy.org.cy/rss,12,Salud,40,Chipre,en,False,30 +3444,Ministry of Health Cyprus,Comunicados oficiales salud publica y regulacion,https://www.moh.gov.cy/rss,12,Salud,40,Chipre,en,False,30 +3229,WHO Cyprus,Salud global OMS epidemias y comunicados,https://www.who.int/feeds/entity/china/rss.xml,12,Salud,40,Chipre,en,False,30 +3425,Cyprus Family Planning Society,Planificacion familiar poblacion y salud comunitaria,https://www.family.org.cy/rss,13,Sociedad,40,Chipre,en,False,30 +3432,Cyprus Girl Guides Association,Organizacion juvenil comunidad educacion y sociedad,https://girlguides.org.cy/rss,13,Sociedad,40,Chipre,en,True,0 +3431,Cyprus Refugee Council,Refugiados migracion derechos y sociedad civil,https://cyrefugeecouncil.org/feed/,13,Sociedad,40,Chipre,en,True,0 +3433,Cyprus Scouts Association,Actividades juveniles comunidad y educacion social,https://scouts.org.cy/rss,13,Sociedad,40,Chipre,en,False,30 +3389,European Commission Cyprus Social,Politicas sociales europeas aplicadas a Chipre,https://ec.europa.eu/info/rss_en,13,Sociedad,40,Chipre,en,False,30 +3430,European Youth Cyprus,Juventud proyectos sociales y comunidad,https://www.eayouth.org.cy/feed/,13,Sociedad,40,Chipre,en,False,30 +3426,UN Cyprus Social Issues,Derechos humanos igualdad migracion y sociedad,https://www.un.org/press/en/rss.xml,13,Sociedad,40,Chipre,en,True,0 +3413,Cyprus Blockchain Summit,Blockchain fintech y criptotecnologia,https://blockchainsummitcyprus.com/feed/,14,Tecnología,40,Chipre,en,False,30 +3414,Cyprus IT Professionals Network,Tecnologia datos ciberseguridad y tendencias TIC,https://www.itprofessionalscyprus.com/feed/,14,Tecnología,40,Chipre,en,False,30 +3331,Cyprus Institute Technology,Investigacion tecnologica clima energia y simulacion,https://www.cyi.ac.cy/feed.html,14,Tecnología,40,Chipre,en,False,30 +3332,Cyprus University of Technology Tech,Tecnologia ingenieria software y aplicaciones digitales,https://www.cut.ac.cy/news/rss,14,Tecnología,40,Chipre,en,False,30 +3409,Digital Cyprus,Digitalizacion gobierno electronico y servicios TIC,https://digitalcyprus.gov.cy/rss,14,Tecnología,40,Chipre,en,False,30 +3410,EU Digital Europe Cyprus,Tecnologia europea con proyectos aplicados a Chipre,https://digital-strategy.ec.europa.eu/en/news/feed,14,Tecnología,40,Chipre,en,False,30 +3411,Euronews Technology Cyprus,Tecnologia europea con foco mediterraneo,https://www.euronews.com/rss?level=theme&name=technology,14,Tecnología,40,Chipre,en,True,0 +3370,Financial Mirror Tech,Tecnologia negocios fintech y ciberseguridad,https://www.financialmirror.com/feed/,14,Tecnología,40,Chipre,en,True,0 +3334,Research and Innovation Foundation Cyprus,Tecnologia investigacion IA y proyectos digitales,https://www.research.org.cy/feed/,14,Tecnología,40,Chipre,en,True,0 +3373,StockWatch Tech,Innovacion empresas digitales y tecnologia,https://www.stockwatch.com.cy/en/rss,14,Tecnología,40,Chipre,en,False,30 +3412,TechIsland Cyprus,Startups ecosistema digital e innovacion empresarial,https://techisland.org/feed/,14,Tecnología,40,Chipre,en,False,30 +3514,Agencia de Noticias UNAL Ciencia,Ciencia academica investigacion nacional,https://agenciadenoticias.unal.edu.co/rss.xml,1,Ciencia,41,Colombia,es,False,30 +3499,Caracol Ciencia,Articulos cientificos salud y espacio,https://caracol.com.co/tag/ciencia/a/rss,1,Ciencia,41,Colombia,es,False,30 +3516,Ciencia Medica Colombia,Salud investigacion clinica y medicina,https://revistas.unal.edu.co/index.php/medicociencia/issue/current/feed,1,Ciencia,41,Colombia,es,False,30 +3513,Ciudad del Saber Colombia Ciencia,Innovacion ciencia y desarrollo,https://connectbogota.org/feed/,1,Ciencia,41,Colombia,es,False,30 +3519,Colciencias Historico,Ciencia nacional convocatorias y publicaciones,https://www.colciencias.gov.co/rss.xml,1,Ciencia,41,Colombia,es,False,30 +3500,El Colombiano Ciencia,Articulos de ciencia salud y ambiente,https://www.elcolombiano.com/rss/ciencia,1,Ciencia,41,Colombia,es,False,30 +3496,El Espectador Ciencia,Ciencia salud biodiversidad y descubrimientos,https://www.elespectador.com/rss/ciencia,1,Ciencia,41,Colombia,es,False,30 +3495,El Tiempo Ciencia,Ciencia investigacion salud y tecnologia aplicada,https://www.eltiempo.com/rss/ciencia,1,Ciencia,41,Colombia,es,False,30 +3512,Instituto SINCHI Amazonia,Ciencia selva biodiversidad y recursos naturales,https://www.sinchi.org.co/rss,1,Ciencia,41,Colombia,es,False,30 +3506,MinCiencias Colombia,Ciencia innovacion becas y programas nacionales,https://minciencias.gov.co/rss.xml,1,Ciencia,41,Colombia,es,True,0 +3510,Observatorio Astronomico Nacional,Astronomia espacio y estudios celestes,https://oan.unal.edu.co/rss/,1,Ciencia,41,Colombia,es,False,30 +3498,RCN Ciencia,Ciencia educacion salud y tecnologia,https://www.rcnradio.com/taxonomy/term/19/feed,1,Ciencia,41,Colombia,es,False,30 +3517,Revista de la Academia Colombiana de Ciencias,Ciencia pura investigacion y publicaciones,https://www.accefyn.org.co/ojs/index.php/accefyn/gateway/plugin/WebFeedGatewayPlugin/atom,1,Ciencia,41,Colombia,es,False,30 +3497,Semana Ciencia,Avances cientificos salud y medio ambiente,https://www.semana.com/rss/sections/ciencia/,1,Ciencia,41,Colombia,es,False,30 +3518,Universidad EAFIT Ciencia,Investigacion aplicada tecnologias y ciencia,https://www.eafit.edu.co/rss/noticias,1,Ciencia,41,Colombia,es,False,30 +3515,Universidad Industrial de Santander Ciencia,Investigacion salud ingenieria y fisica,https://www.uis.edu.co/rss/,1,Ciencia,41,Colombia,es,False,30 +3503,Universidad Javeriana Ciencia,Investigacion biomedica y ciencia aplicada,https://www.javeriana.edu.co/rss,1,Ciencia,41,Colombia,es,False,30 +3501,Universidad Nacional Ciencia,Investigacion academica ciencia y tecnologia,https://unperiodico.unal.edu.co/rss/,1,Ciencia,41,Colombia,es,False,30 +3505,Universidad de Antioquia Ciencia,Investigacion biologia salud y fisica,https://www.udea.edu.co/wps/portal/udea/web/noticias/rss,1,Ciencia,41,Colombia,es,False,30 +3502,Universidad de los Andes Ciencia,Investigacion ciencias sociales y naturales,https://uniandes.edu.co/rss.xml,1,Ciencia,41,Colombia,es,False,30 +3504,Universidad del Valle Ciencia,Ciencia ingenieria salud y ambiente,https://www.univalle.edu.co/rss/noticias,1,Ciencia,41,Colombia,es,False,30 +3542,AP News World,Mundo conflictos politica y diplomacia,https://apnews.com/rss/apf-international,7,Internacional,41,Colombia,en,False,30 +3534,Amnesty International Global,DDHH crisis y conflictos internacionales,https://www.amnesty.org/en/latest/rss/,7,Internacional,41,Colombia,en,True,0 +3543,BBC Mundo Internacional,Noticias globales en español,https://www.bbc.com/mundo/ultimas_noticias/index.xml,7,Internacional,41,Colombia,es,True,0 +3526,Blu Radio Internacional,Diplomacia conflictos y noticias del mundo,https://www.bluradio.com/rss/internacional,7,Internacional,41,Colombia,es,False,30 +3528,Cancilleria Colombia,Diplomacia politica exterior relaciones internacionales,https://www.cancilleria.gov.co/rss.xml,7,Internacional,41,Colombia,es,False,30 +3523,Caracol Internacional,Noticias internacionales diplomacia y crisis globales,https://caracol.com.co/tag/mundo/a/rss,7,Internacional,41,Colombia,es,False,30 +3544,Deutsche Welle Latinoamerica,Mundo noticias globales y geopolitica,https://rss.dw.com/rdf/rss-es-latam,7,Internacional,41,Colombia,es,False,30 +3540,EU External Action,Politica exterior de la Union Europea,https://www.eeas.europa.eu/eeas-news/rss_en,7,Internacional,41,Colombia,en,False,30 +3525,El Colombiano Internacional,Noticias internacionales y panorama global,https://www.elcolombiano.com/rss/internacional,7,Internacional,41,Colombia,es,True,0 +3521,El Espectador Internacional,Conflictos globales diplomacia y mundo,https://www.elespectador.com/rss/mundo,7,Internacional,41,Colombia,es,False,30 +3520,El Tiempo Internacional,Noticias globales politica internacional y mundo,https://www.eltiempo.com/rss/mundo,7,Internacional,41,Colombia,es,False,30 +3529,Gobierno de Colombia Internacional,Comunicados internacionales y relaciones exteriores,https://www.gov.co/rss,7,Internacional,41,Colombia,es,False,30 +3533,Human Rights Watch Internacional,DDHH crisis globales y reportes,https://www.hrw.org/world/feed,7,Internacional,41,Colombia,en,False,30 +3539,NATO Noticias Internacional,Seguridad global y alianzas,https://www.nato.int/cps/en/natohq/news.rss,7,Internacional,41,Colombia,en,False,30 +3531,OEA Colombia Internacional,Democracia seguridad y relaciones hemisfericas,https://www.oas.org/es/centro_noticias/rss.asp,7,Internacional,41,Colombia,es,False,30 +3530,ONU Colombia Internacional,Acciones globales y cooperacion internacional,https://colombia.un.org/rss.xml,7,Internacional,41,Colombia,en,True,0 +3524,RCN Internacional,Politica internacional y eventos globales,https://www.rcnradio.com/tags/internacional/feed,7,Internacional,41,Colombia,es,False,30 +3522,Semana Internacional,Geopolitica conflictos y panorama mundial,https://www.semana.com/rss/sections/mundo/,7,Internacional,41,Colombia,es,False,30 +3532,UNHCR ACNUR Internacional,Refugiados emergencias y diplomacia internacional,https://www.acnur.org/noticias/rss,7,Internacional,41,Colombia,es,False,30 +3527,W Radio Internacional,Noticias del mundo diplomacia y conflictos,https://www.wradio.com.co/rss/mundo,7,Internacional,41,Colombia,es,False,30 +3561,Amazon Conservation Team Colombia,Amazonia pueblos indigenas y conservacion,https://www.amazonteam.org/feed/,8,Medio Ambiente,41,Colombia,en,True,0 +3557,Autoridad Nacional de Licencias Ambientales,Evaluacion ambiental y proyectos,https://www.anla.gov.co/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3535,CEPAL Medio Ambiente,Analisis ambiental latinoamericano,https://www.cepal.org/es/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3564,CIDH Ambiente,DDHH ambientales en la region,https://www.oas.org/es/cidh/prensa/notas.asp?rss=1,8,Medio Ambiente,41,Colombia,es,False,30 +3548,Caracol Medio Ambiente,Naturaleza clima incendios y biodiversidad,https://caracol.com.co/tag/medio_ambiente/a/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3556,Cormagdalena Medio Ambiente,Rio magdalena ecosistemas y navegabilidad,https://www.cormagdalena.gov.co/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3550,El Colombiano Medio Ambiente,Ecosistemas clima y sostenibilidad,https://www.elcolombiano.com/rss/medio-ambiente,8,Medio Ambiente,41,Colombia,es,True,0 +3546,El Espectador Medio Ambiente,Ambiente cambio climatico y biodiversidad,https://www.elespectador.com/rss/medio-ambiente,8,Medio Ambiente,41,Colombia,es,False,30 +3545,El Tiempo Medio Ambiente,Clima ecosistemas naturaleza y biodiversidad,https://www.eltiempo.com/rss/vida/medio-ambiente,8,Medio Ambiente,41,Colombia,es,False,30 +3560,Fundacion Natura Colombia,Fauna flora restauracion y ecosistemas,https://www.natura.org.co/rss,8,Medio Ambiente,41,Colombia,es,True,0 +3562,GAIA Amazonia,Medio ambiente amazonia y comunidades,https://gaiaamazonas.org/rss,8,Medio Ambiente,41,Colombia,es,True,0 +3559,Greenpeace Latinoamerica,Clima energia y proteccion ambiental,https://www.greenpeace.org/latam/feed/,8,Medio Ambiente,41,Colombia,es,False,30 +3508,IDEAM Colombia,Clima hidrologia desastres y meteorologia,https://www.ideam.gov.co/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3507,Instituto Humboldt Colombia,Biodiversidad fauna flora y ecosistemas,https://www.humboldt.org.co/es/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3555,MinAmbiente Colombia,Politica ambiental clima sostenibilidad y ecosistemas,https://www.minambiente.gov.co/rss,8,Medio Ambiente,41,Colombia,es,True,0 +3563,Oceana Colombia,Proteccion marina pesca y ecosistemas oceanicos,https://oceana.org/rss,8,Medio Ambiente,41,Colombia,en,True,0 +3511,Parques Nacionales Naturales Colombia,Conservacion fauna ecosistemas y naturaleza,https://www.parquesnacionales.gov.co/rss,8,Medio Ambiente,41,Colombia,es,True,0 +3549,RCN Medio Ambiente,Medio ambiente clima y ecosistemas,https://www.rcnradio.com/tags/medio-ambiente/feed,8,Medio Ambiente,41,Colombia,es,False,30 +3547,Semana Medio Ambiente,Amazonia clima conservacion y ecosistemas,https://www.semana.com/rss/sections/medio-ambiente/,8,Medio Ambiente,41,Colombia,es,False,30 +3509,Servicio Geologico Colombiano,Sismos volcanes geologia y riesgos naturales,https://www.sgc.gov.co/infogeologico/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3558,WWF Colombia,Conservacion fauna selvas y clima,https://www.wwf.org.co/rss,8,Medio Ambiente,41,Colombia,es,False,30 +3599,Amnesty Comoros Politics,DDHH y politica en Comoras,https://www.amnesty.org/en/location/africa/east-africa/comoros/feed/,11,Política,42,Comoras,en,True,0 +3583,COMESA Regional Politics,Politica regional del mercado comun,https://www.comesa.int/feed/,11,Política,42,Comoras,en,True,0 +3582,East Africa Monitor Comoros,Noticias politicas del este de africa,https://www.eastafricanmonitor.com/feed/,11,Política,42,Comoras,en,False,30 +3576,France24 Afrique Politique,Politica africana y Comoras,https://www.france24.com/fr/tag/afrique/rss,11,Política,42,Comoras,fr,True,0 +3581,Union Africaine Comores,Informes politicos sobre Comoras,https://au.int/en/news/rss,11,Política,42,Comoras,en,False,30 +3623,Africa Business Tech,Innovacion africana empresas y startups,https://africabusiness.com/feed/,14,Tecnología,42,Comoras,en,True,0 +3622,Africa ICT News,TIC telecom redes y servicios digitales,https://www.ict.africa/feed/,14,Tecnología,42,Comoras,en,False,30 +3570,Al Watwan Tech,Noticias locales de tecnologia y sociedad,https://www.alwatanpress.com/feed/,14,Tecnología,42,Comoras,fr,True,0 +3612,AllAfrica Tech,Innovacion africana startups y telecom,https://allafrica.com/tools/headlines/rdf/sci-tech/headlines.rdf,14,Tecnología,42,Comoras,en,False,30 +3598,Comision Oceano Indico Tech,Digitalizacion regional y telecom,https://www.commissionoceanindien.org/feed/,14,Tecnología,42,Comoras,fr,True,0 +3571,Comores Infos Tech,Tecnologia digital y noticias locales,https://comoresinfos.net/feed/,14,Tecnología,42,Comoras,fr,True,0 +3610,France24 Tech,Africa tech innovacion y tecnologia,https://www.france24.com/fr/technologies/rss,14,Tecnología,42,Comoras,fr,False,30 +3573,Habariza Comores Tech,Tecnologia telefonia y servicios digitales,https://www.habarizacomores.com/feeds/posts/default,14,Tecnología,42,Comoras,fr,True,0 +3572,La Gazette des Comores Tech,Noticias tecnicas y digitales,https://lagazettedescomores.com/feed/,14,Tecnología,42,Comoras,fr,False,30 +3574,Masiwa Comores Tech,Innovacion local y tecnologia social,https://masiwa-comores.com/feed/,14,Tecnología,42,Comoras,fr,True,0 +3609,RFI Afrique Tech,Tecnologia africana y mundo digital,https://www.rfi.fr/fr/science-et-tech/rss,14,Tecnología,42,Comoras,fr,False,30 +3621,Smart Africa Alliance,Tecnologia digital africa y transformacion,https://smartafrica.org/feed/,14,Tecnología,42,Comoras,en,False,30 +3669,Asian Development Bank East Asia,Economia este asiatico y riesgo regional,https://www.adb.org/rss.xml,4,Economía,43,Corea del Norte,en,False,30 +2822,AfricaNews – North Korea,Informacion africana sobre Corea del Norte,https://www.africanews.com/rss,11,Política,43,Corea del Norte,en,False,30 +3579,AllAfrica – North Korea,Noticias Pan-Africa y Corea del Norte,https://allafrica.com/tools/headlines/rdf/comoros/headlines.rdf,11,Política,43,Corea del Norte,en,True,0 +3647,Amnesty NK Politics,DDHH y politica sobre Corea del Norte,https://www.amnesty.org/en/location/asia-and-the-pacific/north-korea/feed/,11,Política,43,Corea del Norte,en,True,0 +3625,Daily NK,Noticias sobre Corea del Norte desde el interior y exiliados,https://dailynk.com/english/feed,11,Política,43,Corea del Norte,en,True,0 +3600,Human Rights Watch NK,Analisis politico y violacion de derechos,https://www.hrw.org/rss,11,Política,43,Corea del Norte,en,True,0 +3577,Indian Ocean Times – Corea del Norte (genérico),Cobertura internacional incluido Asia Oriental,https://www.indianoceantimes.com/feed/,11,Política,43,Corea del Norte,en,False,30 +3603,OIC DPRK Affairs,Asuntos politicos de paises miembros,https://www.oic-oci.org/rss,11,Política,43,Corea del Norte,en,False,30 +1697,RFI Afrique – Coree du Nord,Politica norcoreana desde medio africano francofono,https://www.rfi.fr/fr/afrique/rss,11,Política,43,Corea del Norte,fr,True,0 +3650,US State Department DPRK,Comunicados politicos oficiales,https://www.state.gov/feed/,11,Política,43,Corea del Norte,en,False,30 +3624,38North Tech,Analisis tecnologico militar y digital,https://www.38north.org/feed/,14,Tecnología,43,Corea del Norte,en,False,30 +3693,Cybersecurity Asia,Seguridad digital en Asia Este,https://www.cybersecasia.net/feed,14,Tecnología,43,Corea del Norte,en,True,0 +3635,DailyNK Tech,Tecnologia digital y telecomunicaciones internas,https://www.dailynk.com/english/feed/,14,Tecnología,43,Corea del Norte,en,True,0 +3642,Japan Times NK Tech,Tecnologia militar y seguridad regional,https://www.japantimes.co.jp/feed/,14,Tecnología,43,Corea del Norte,en,True,0 +3626,KCNA Watch Tech,Publicaciones tecnologicas estatales,https://kcnawatch.org/rss.xml,14,Tecnología,43,Corea del Norte,en,False,30 +3637,NK News Technology,Tecnologia militar satelital y ciberseguridad,https://www.nknews.org/feed/,14,Tecnología,43,Corea del Norte,en,True,0 +3638,NorthKorea Times Tech,Tecnologia ciencia y avances militares,https://www.northkoreatimes.com/rss/,14,Tecnología,43,Corea del Norte,en,False,30 +3566,FAO Asia Science,Agricultura ciencia y recursos,https://www.fao.org/rss-feed/en/,1,Ciencia,44,Corea del Sur,en,False,30 +3736,KAIST Science,Investigacion cientifica academica,https://www.kaist.ac.kr/en/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3735,KIST Science,Instituto coreano de ciencia y tecnologia,https://eng.kist.re.kr/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3739,Korea Astronomy Institute,Astronomia y ciencia espacial,https://www.kasi.re.kr/eng/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3738,Korea Bio Science,Investigacion biomolecular y salud,https://www.koreabio.org/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3737,Korean Academy of Science,Publicaciones academicas,https://www.kast.or.kr/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3732,Nature Asia Korea,Ciencia avanzada en Asia Este,https://www.natureasia.com/en/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3733,Science News Asia,Ciencia y tecnologia regional,https://www.sciencenews.org/feed,1,Ciencia,44,Corea del Sur,en,True,0 +3731,The Scientist Korea,Ciencia global con cobertura coreana,https://www.the-scientist.com/rss,1,Ciencia,44,Corea del Sur,en,False,30 +3757,Allkpop News,Kpop artistas lanzamientos y cultura pop,https://www.allkpop.com/rss,2,Cultura,44,Corea del Sur,en,False,30 +3760,AsianWiki KDrama,Series cine y cultura coreana,https://asianwiki.com/wiki/index.php?title=Special:RecentChanges&feed=rss,2,Cultura,44,Corea del Sur,en,False,30 +3765,Busan Film Festival,Cine coreano y asiatico noticias del festival,https://www.biff.kr/eng/rss,2,Cultura,44,Corea del Sur,en,False,30 +3767,CJ ENM Culture,Kpop produccion audiovisual y entretenimiento,https://www.cjenm.com/en/rss,2,Cultura,44,Corea del Sur,en,False,30 +3770,HYBE Labels News,Kpop artistas contenido cultural,https://www.hybecorp.com/rss,2,Cultura,44,Corea del Sur,en,False,30 +3769,JYP Entertainment News,Kpop grupos noticias y lanzamientos,https://www.jype.com/rss,2,Cultura,44,Corea del Sur,en,False,30 +3766,Jeonju Film Festival,Cine independiente coreano,https://www.jiff.or.kr/rss,2,Cultura,44,Corea del Sur,en,False,30 +3771,Korea Foundation Culture,Promocion de cultura coreana internacional,https://en.kf.or.kr/rss,2,Cultura,44,Corea del Sur,en,False,30 +3749,Korea Times Culture,Cultura espectaculos Kpop y cine,https://www.koreatimes.co.kr/www/rss/culture.xml,2,Cultura,44,Corea del Sur,en,False,30 +3759,Koreaboo Culture,Cultura Kpop viral y entretenimiento,https://www.koreaboo.com/feed/,2,Cultura,44,Corea del Sur,en,True,0 +3763,MMCA Korea,Arte contemporaneo exposiciones y eventos,https://www.mmca.go.kr/eng/rss,2,Cultura,44,Corea del Sur,en,False,30 +3762,National Museum of Korea,Cultura patrimonio exposiciones,https://www.museum.go.kr/site/eng/rss,2,Cultura,44,Corea del Sur,en,False,30 +3768,SM Entertainment News,Kpop industria musical y artistas,https://www.smentertainment.com/rss,2,Cultura,44,Corea del Sur,en,True,0 +3764,Seoul Art Guide,Arte coreano exposiciones y galerias,https://www.seoulartguide.com/rss,2,Cultura,44,Corea del Sur,en,False,30 +3758,Soompi Culture,Kpop dramas y cultura pop,https://www.soompi.com/feed,2,Cultura,44,Corea del Sur,en,True,0 +3756,The Korea Times Kpop,Kpop grupos artistas y eventos,https://www.koreatimes.co.kr/www/rss/entertainment.xml,2,Cultura,44,Corea del Sur,en,True,0 +3896,Asian Games Korea,Deportes asiaticos y competiciones,https://www.hangzhou2022.cn/en/rss,3,Deportes,44,Corea del Sur,en,False,30 +3893,Esports Korea,Esports ligas torneos y gaming,https://www.inven.co.kr/rss/news,3,Deportes,44,Corea del Sur,ko,False,30 +3884,K League Official,Futbol profesional primera division,https://kleague.com/rss,3,Deportes,44,Corea del Sur,en,False,30 +3885,KBO League Baseball,Beisbol profesional de Corea,https://www.koreabaseball.com/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3889,Korea Badminton Association,Badminton ligas y eventos,https://www.badmintonkorea.co.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3887,Korea Basketball League,Baloncesto profesional coreano,https://www.kbl.or.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3890,Korea Judo Association,Artes marciales judo y competiciones,https://www.judo.or.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3892,Korea Ski Association,Deportes de invierno y competiciones,https://www.ski.or.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3888,Korea Table Tennis,Tenis de mesa y torneos,https://www.koreatta.or.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3891,Korea Taekwondo Association,Taekwondo torneos y eventos,https://www.koreataekwondo.org/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3874,Korea Times Sports,Deportes futbol beisbol y competiciones,https://www.koreatimes.co.kr/www/rss/sports.xml,3,Deportes,44,Corea del Sur,en,True,0 +3886,Korea Volleyball Federation,Voleibol profesional coreano,https://www.kovo.co.kr/rss,3,Deportes,44,Corea del Sur,ko,False,30 +3894,League of Legends LCK,Liga coreana LCK esports,https://lolesports.com/rss,3,Deportes,44,Corea del Sur,en,False,30 +3808,Bank of Korea Reports,Informes macroeconomicos oficiales,https://www.bok.or.kr/eng/rss,4,Economía,44,Corea del Sur,en,False,30 +3822,Deloitte Korea Economy,Analisis economico y empresarial,https://www2.deloitte.com/global/en/insights/industry.html?format=xml,4,Economía,44,Corea del Sur,en,False,30 +3821,KPMG Korea Insights,Analisis empresarial y economia,https://home.kpmg/kr/en/home/insights.rss.html,4,Economía,44,Corea del Sur,en,False,30 +3810,Korea Development Institute,Analisis economico y estudios,https://www.kdi.re.kr/kdi_eng/rss,4,Economía,44,Corea del Sur,en,False,30 +3819,Korea Economic Daily,Empresas coreanas comercio y mercados,https://www.hankyung.com/rss,4,Economía,44,Corea del Sur,en,False,30 +3800,Korea Times Economy,Economia coreana industria y comercio,https://www.koreatimes.co.kr/www/rss/biz.xml,4,Economía,44,Corea del Sur,en,True,0 +3811,Korea Trade Investment KOTRA,Comercio exterior e inversion,https://www.kotra.or.kr/foreign/rss,4,Economía,44,Corea del Sur,en,False,30 +3809,Ministry of Finance Korea,Politica fiscal y economia nacional,https://www.moef.go.kr/eng/rss.jsp,4,Economía,44,Corea del Sur,en,False,30 +3064,Asia Times Korea World,Geopolitica Asia Pacifico,https://asiatimes.com/feed/,7,Internacional,44,Corea del Sur,en,True,0 +3818,Business Korea World,Economia mundial y relaciones exteriores,https://www.businesskorea.co.kr/rss,7,Internacional,44,Corea del Sur,en,False,30 +3786,Defense Ministry Korea International,Seguridad regional y alianzas,https://www.mnd.go.kr/mbshome/mbs/mndEng/rss.jsp,7,Internacional,44,Corea del Sur,en,False,30 +3899,Korea Times World,Mundo relaciones exteriores y geopolitica,https://www.koreatimes.co.kr/www/rss/world.xml,7,Internacional,44,Corea del Sur,en,True,0 +3785,Ministry of Foreign Affairs Korea,Comunicados diplomaticos y politica exterior,https://www.mofa.go.kr/eng/brd/m_5676/rssList.do,7,Internacional,44,Corea del Sur,en,False,30 +3820,Pulse Korea World,Politica internacional y relaciones Asia Pac,https://pulsenews.co.kr/rss.jsp,7,Internacional,44,Corea del Sur,en,False,30 +3922,Reuters Asia Korea,Noticias internacionales y Asia,https://www.reuters.com/world/asia-pacific/rss,7,Internacional,44,Corea del Sur,en,False,30 +3050,SCMP Korea World,Asia Este y relaciones China Corea,https://www.scmp.com/rss/91/feed,7,Internacional,44,Corea del Sur,en,True,0 +3726,Arirang Environment,Clima energia y sostenibilidad,https://www.arirang.com/rss/index.asp,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3729,Chosun Environment,Politica ambiental contaminacion y clima,https://english.chosun.com/site/data/rss/rss.xml,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3298,Greenpeace East Asia,Clima energia activismo y medio ambiente,https://www.greenpeace.org/eastasia/feed/,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3778,Hankyoreh Environment,Medio ambiente justicia climatica y biodiversidad,https://english.hani.co.kr/rss,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3730,JoongAng Daily Environment,Medio ambiente clima y energia,https://koreajoongangdaily.joins.com/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3728,KBS World Environment,Noticias ambientales y cambio climatico,https://world.kbs.co.kr/service/rss_list.htm,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3935,KEEI Energy Institute,Energia renovable y estudios ambientales,https://www.keei.re.kr/eng/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3937,Korea Forest Service,Bosques incendios y biodiversidad,https://english.forest.go.kr/newkfsweb/kfi/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3639,Korea Herald Environment,Medio ambiente clima energia y sostenibilidad,https://www.koreaherald.com/rss/,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3939,Korea Institute of Ocean Science,Oceanos pesca clima y ecosistemas,https://www.kiost.ac.kr/eng/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3740,Korea Meteorological Administration,Clima meteorologia y emergencias,https://www.kma.go.kr/eng/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3938,Korea National Park Service,Parques nacionales y biodiversidad,https://english.knps.or.kr/etc/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3640,Korea Times Environment,Medio ambiente clima y politicas ecologicas,https://www.koreatimes.co.kr/www/rss/nation.xml,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3761,Korea.net Environment,Politica ambiental oficial y clima,https://www.korea.net/rss/rss.xml,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3727,Maeil Business Green,Energia verde y sostenibilidad,https://www.mk.co.kr/rss/english/,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3934,Ministry of Environment Korea,Politica ambiental y comunicados oficiales,https://eng.me.go.kr/eng/web/rss/rssView.do,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3755,Seoul Times Environment,Medio ambiente y ecologia urbana,https://theseoultimes.com/ST/?url=rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3303,UNEP Asia Environment,Medio ambiente y sostenibilidad en Asia,https://www.unep.org/rss.xml,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3946,WWF Korea,Conservacion fauna y ecosistemas,https://www.wwf.or.kr/eng/rss,8,Medio Ambiente,44,Corea del Sur,en,False,30 +3641,Yonhap Environment,Clima contaminacion y politica ambiental,https://en.yna.co.kr/RSS/news.xml,8,Medio Ambiente,44,Corea del Sur,en,True,0 +3784,Ministry of Unification Politics,Politica intercoreana y seguridad,https://www.unikorea.go.kr/eng_unikorea/notice/notice/rss/,11,Política,44,Corea del Sur,en,False,30 +3787,NIS Korea Intelligence,Seguridad nacional e informes,https://www.nis.go.kr/eng/rss,11,Política,44,Corea del Sur,en,False,30 +3788,National Assembly Korea,Informes parlamentarios y legislativos,https://www.assembly.go.kr/portal/rss.do,11,Política,44,Corea del Sur,en,False,30 +3644,RFA Korea Politics,Politica intercoreana y seguridad,https://www.rfa.org/english/rss/RSS,11,Política,44,Corea del Sur,en,False,30 +3645,VOA Korea Politics,Politica coreana y relaciones con EEUU,https://www.voanews.com/api/zumeiq$ome,11,Política,44,Corea del Sur,en,False,30 +3870,Busan Metropolitan Society,Ciudadania comunidad y vida urbana,https://www.busan.go.kr/eng/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3868,ILO Korea Labor,Trabajo sociedad y condiciones laborales,https://www.ilo.org/global/about-the-ilo/newsroom/news/WCMS_211096/lang--en/rss?,13,Sociedad,44,Corea del Sur,en,False,30 +3871,Korea NGO Council,Sociedad civil y organizaciones sociales,https://ngokorea.org/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3860,Korea Youth Policy,Juventud educacion empleo y sociedad,https://www.youth.go.kr/eng/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3861,Korean Statistical Society,Datos demograficos y sociales,https://kostat.go.kr/portal/eng/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3859,Korean Women Society,Iglesia mujeres igualdad y derechos,https://www.kwdi.re.kr/eng/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3862,National Human Rights Korea,Derechos humanos y sociedad,https://www.humanrights.go.kr/rss,13,Sociedad,44,Corea del Sur,en,False,30 +3869,Seoul Metropolitan Government,Sociedad urbana vida publica y comunidad,https://english.seoul.go.kr/feed/,13,Sociedad,44,Corea del Sur,en,False,30 +3835,ETNews Korea,Electronica semiconductores y startups,https://english.etnews.com/news/articleRSS.html,14,Tecnología,44,Corea del Sur,en,False,30 +2843,GSMA Asia Tech,Moviles redes y telecomunicaciones,https://www.gsma.com/newsroom/feed/,14,Tecnología,44,Corea del Sur,en,True,0 +3615,ITU Asia Tech,Telecom redes y regulacion digital,https://www.itu.int/en/mediacentre/Pages/rss.aspx,14,Tecnología,44,Corea del Sur,en,False,30 +3833,Korea IT Times,Industria TI telecomunicaciones y seguridad,https://www.koreaittimes.com/rss/all,14,Tecnología,44,Corea del Sur,en,False,30 +3824,Korea Times Tech,Tecnologia digital ciberseguridad y startups,https://www.koreatimes.co.kr/www/rss/tech.xml,14,Tecnología,44,Corea del Sur,en,True,0 +3087,TechNode Korea,Startups tecnologia y ecosistema digital,https://technode.com/feed/,14,Tecnología,44,Corea del Sur,en,False,30 +3836,The Register Asia Tech,Analisis TI con cobertura coreana,https://www.theregister.com/headlines.atom,14,Tecnología,44,Corea del Sur,en,True,0 +3834,ZDNet Korea,Noticias TI software y hardware,https://zdnet.co.kr/news/?mode=xml,14,Tecnología,44,Corea del Sur,ko,False,30 +3956,CONICIT Ciencia,Innovacion proyectos e investigacion,https://www.conicit.go.cr/rss,1,Ciencia,46,Costa Rica,es,False,30 +3957,Minae Ciencia,Ciencia ambiental y estudios de ecosistemas,https://www.minae.go.cr/rss.asp,1,Ciencia,46,Costa Rica,es,False,30 +3982,AECID Cultura Centroamerica,Cultura iberoamericana y eventos,https://www.aecid.es/rss,2,Cultura,46,Costa Rica,es,False,30 +3978,EnLinea Cultura CR,Arte literatura y exposiciones,https://enlineacr.com/feed/,2,Cultura,46,Costa Rica,es,False,30 +3979,IFAC Costa Rica Cultura,Arte contemporaneo e iniciativas culturales,https://ifac.art/feed/,2,Cultura,46,Costa Rica,en,False,30 +3981,Ibercultura Viva CR,Red cultural iberoamericana y proyectos,https://iberculturaviva.org/feed/,2,Cultura,46,Costa Rica,es,True,0 +3971,Ministerio de Cultura CR,Institucional arte patrimonio y cultura,https://mcj.go.cr/rss,2,Cultura,46,Costa Rica,es,False,30 +3974,Museo Nacional CR,Cultura historia y patrimonio,https://www.museocostarica.go.cr/rss,2,Cultura,46,Costa Rica,es,True,0 +3991,Al Dia Deportes,Futbol nacional y Liga Promerica,https://aldia.cr/rss/,3,Deportes,46,Costa Rica,es,False,30 +3995,CONCRC Surf,Federacion de surf y competiciones,https://www.surfingcr.net/feed/,3,Deportes,46,Costa Rica,es,False,30 +3999,Ciclismo Costa Rica,Ciclismo nacional y eventos,https://crciclismo.com/rss,3,Deportes,46,Costa Rica,es,True,0 +3994,Concacaf Costa Rica,Competiciones regionales y selecciones,https://www.concacaf.com/rss,3,Deportes,46,Costa Rica,en,False,30 +3992,Fedefutbol CR,Seleccion nacional y futbol femenino,https://www.fedefutbol.com/rss,3,Deportes,46,Costa Rica,es,True,0 +3996,ICODER Costa Rica,Deporte nacional y alto rendimiento,https://www.icoder.go.cr/rss,3,Deportes,46,Costa Rica,es,False,30 +3895,Olympics Costa Rica,Juegos Olimpicos y clasificaciones,https://olympics.com/en/news/rss,3,Deportes,46,Costa Rica,en,False,5 +4000,Triatlon Costa Rica,Competencias de triatlon y eventos,https://www.fecotri.org/feed/,3,Deportes,46,Costa Rica,es,False,30 +3993,UNAFUT Liga Promerica,Futbol primera division Costa Rica,https://www.unafut.com/rss,3,Deportes,46,Costa Rica,es,True,0 +3998,World Surf League Costa Rica,Surf profesional y atletas costarricenses,https://www.worldsurfleague.com/rss,3,Deportes,46,Costa Rica,en,False,30 +4012,BCCR Economia,Indicadores economia nacional y reportes,https://www.bccr.fi.cr/SitePages/rss.aspx,4,Economía,46,Costa Rica,es,False,30 +4011,BNCR Economia,Banco Nacional economia datos y finanzas,https://www.bncr.fi.cr/rss,4,Economía,46,Costa Rica,es,False,5 +4014,COMEX Costa Rica,Comercio exterior inversion y acuerdos,https://www.comex.go.cr/rss,4,Economía,46,Costa Rica,es,False,30 +4013,MEIC Economia,Industria comercio y regulacion,https://www.meic.go.cr/rss,4,Economía,46,Costa Rica,es,False,30 +4015,Procomer Comercio,Exportaciones e inversion extranjera,https://www.procomer.com/rss,4,Economía,46,Costa Rica,es,True,0 +4016,Sutel Regulacion,Economia digital y telecomunicaciones,https://sutel.go.cr/rss,4,Economía,46,Costa Rica,es,False,30 +4034,1047 Hit Radio,Musica entretenimiento y artistas,https://1047hit.com/feed/,6,Entretenimiento,46,Costa Rica,es,True,0 +4032,Canal 6 Repretel Espectaculos,TV famosos y espectaculos,https://www.repretel.com/feed/,6,Entretenimiento,46,Costa Rica,es,True,0 +3973,Centro Cultural de Espana CR,Eventos culturales cine y musica,https://ccemx.org/feed/,6,Entretenimiento,46,Costa Rica,es,False,30 +3975,Museos del Banco Central,Arte cine eventos y exposiciones,https://museosdelbancocentral.org/rss,6,Entretenimiento,46,Costa Rica,es,True,0 +4035,NRG Radio Entretenimiento,Musica pop y espectaculos,https://nrgradio.cr/feed/,6,Entretenimiento,46,Costa Rica,es,False,30 +3972,Teatro Nacional CR,Espectaculos obras y presentaciones,https://www.teatronacional.go.cr/rss,6,Entretenimiento,46,Costa Rica,es,False,30 +3954,UNA Cultura,Eventos culturales artes y espectaculos,https://www.una.ac.cr/index.php?format=feed&type=rss,6,Entretenimiento,46,Costa Rica,es,False,30 +3955,Universidad de Costa Rica Cultura,Agenda cultural musica y eventos,https://www.ucr.ac.cr/feeds/noticias.xml,6,Entretenimiento,46,Costa Rica,es,False,30 +4020,IADB Internacional,Noticias globales de desarrollo e inversion,https://www.iadb.org/en/news/rss,7,Internacional,46,Costa Rica,en,False,30 +3960,OPS OMS Internacional,Salud internacional y reportes globales,https://www.paho.org/es/rss,7,Internacional,46,Costa Rica,es,False,30 +3958,UNDP Costa Rica Internacional,Desarrollo global y cooperacion,https://www.undp.org/costa-rica/rss.xml,7,Internacional,46,Costa Rica,en,False,30 +3952,AmPrensa Politica,Politica nacional instituciones y actualidad,https://amprensa.com/feed/,11,Política,46,Costa Rica,es,False,30 +4073,Asamblea Legislativa CR,Legislacion y actividad parlamentaria,https://www.asamblea.go.cr/rss,11,Política,46,Costa Rica,es,False,30 +3949,CRHoy Politica,Politica nacional gobierno y poder judicial,https://www.crhoy.com/feed/,11,Política,46,Costa Rica,es,False,30 +4075,Casa Presidencial CR,Comunicados gubernamentales,https://www.presidencia.go.cr/rss,11,Política,46,Costa Rica,es,False,30 +4077,Defensoria de los Habitantes CR,Politica social y derechos ciudadanos,https://www.dhr.go.cr/rss,11,Política,46,Costa Rica,es,False,30 +3950,Diario Extra Politica,Gobierno partidos y noticias politicas,https://www.diarioextra.com/rss/,11,Política,46,Costa Rica,es,True,0 +3953,El Mundo CR Politica,Politica nacional y asamblea,https://www.elmundo.cr/feed/,11,Política,46,Costa Rica,es,True,0 +4071,El Observador Politica,Politica economia publica y decisiones,https://observador.cr/feed/,11,Política,46,Costa Rica,es,True,0 +3948,La Nacion Politica,Politica costarricense y asamblea legislativa,https://www.nacion.com/rss/,11,Política,46,Costa Rica,es,True,1 +3966,La Republica Politica,Analisis politico y economia publica,https://www.larepublica.net/rss/,11,Política,46,Costa Rica,es,False,30 +4052,Ministerio de Relaciones Exteriores CR,Diplomacia y posicion politica internacional,https://www.rree.go.cr/rss,11,Política,46,Costa Rica,es,False,30 +4033,Monumental Politica,Politica nacional y region,https://www.monumental.co.cr/feed/,11,Política,46,Costa Rica,es,False,30 +4080,OEA Costa Rica Politica,Democracia y procesos politicos,https://www.oas.org/es/rss/,11,Política,46,Costa Rica,es,False,30 +4062,Reuters Costa Rica Politica,Cobertura internacional sobre politica CR,https://www.reuters.com/world/americas/rss,11,Política,46,Costa Rica,en,False,30 +3951,Semanario Universidad Politica,Politica sociedad y analisis critico,https://semanariouniversidad.com/feed/,11,Política,46,Costa Rica,es,True,0 +3967,Teletica Politica,Gobierno nacional y decisiones politicas,https://www.teletica.com/rss,11,Política,46,Costa Rica,es,False,30 +4074,Tribunal Supremo de Elecciones,Politica electoral y comunicados,https://www.tse.go.cr/rss,11,Política,46,Costa Rica,es,False,30 +3743,WHO Afrique,Salud publica investigacion cientifica,https://www.who.int/rss-feeds/news-english.xml,1,Ciencia,45,Costa de Marfil,en,True,0 +4113,Afrik Culture,Cultura africana arte moda y musica,https://www.afrik.com/feed,2,Cultura,45,Costa de Marfil,fr,True,0 +4106,Bonjour Afrique Culture,Cultura africana literatura y arte,https://bonjourafrique.net/feed/,2,Cultura,45,Costa de Marfil,fr,False,30 +4112,Goethe Institut Afrique,Cultura y arte africano,https://www.goethe.de/ins/xx/en/rss.html,2,Cultura,45,Costa de Marfil,en,False,30 +4111,Institut Francais Afrique,Eventos culturales francofonos,https://www.institutfrancais.com/en/rss.xml,2,Cultura,45,Costa de Marfil,fr,True,0 +4117,OIF Francophonie Culture,Cultura francofona global y africana,https://www.francophonie.org/rss,2,Cultura,45,Costa de Marfil,fr,False,30 +1797,UNESCO Afrique Culture,Patrimonio arte y educacion cultural,https://www.unesco.org/rss,2,Cultura,45,Costa de Marfil,fr,False,30 +4132,CEPICI Investissement,Inversion extranjera y empresas,https://www.cepici.gouv.ci/rss,4,Economía,45,Costa de Marfil,fr,False,30 +1755,Financial Afrik,Economia africana y mercados financieros,https://www.financialafrik.com/feed/,4,Economía,45,Costa de Marfil,fr,True,0 +4131,Ministère de lEconomie CI,Politica economica y comunicados,https://www.economie.gouv.ci/rss,4,Economía,45,Costa de Marfil,fr,False,30 +3670,OECD Africa Economie,Analisis economico internacional,https://www.oecd.org/newsroom/feeds/,4,Economía,45,Costa de Marfil,en,False,30 +4114,Africanews Africa,Noticias africanas region CEDEAO y global,https://www.africanews.com/feed/,7,Internacional,45,Costa de Marfil,en,True,0 +4021,ECLAC International,Economia global y America Latina,https://www.cepal.org/en/rss.xml,7,Internacional,45,Costa de Marfil,en,False,30 +3388,IMF International,Politica internacional y economia global,https://www.imf.org/en/News/rss,7,Internacional,45,Costa de Marfil,en,True,0 +3536,ONU News International,Noticias globales ONU,https://news.un.org/feed/subscribe/en/news/all/rss.xml,7,Internacional,45,Costa de Marfil,en,True,0 +2830,World Bank International,Noticias economicas internacionales,https://www.worldbank.org/en/news/all?format=rss,7,Internacional,45,Costa de Marfil,en,False,30 +4088,7info Politique,Politica nacional instituciones y elecciones,https://www.7info.ci/feed/,11,Política,45,Costa de Marfil,fr,True,0 +4084,AIP Politique,Politica institucional y comunicados oficiales,https://www.aip.ci/feed/,11,Política,45,Costa de Marfil,fr,True,0 +4089,AbidjanNet Politique,Gobierno asamblea y politica nacional,https://news.abidjan.net/feed,11,Política,45,Costa de Marfil,fr,False,30 +4105,AbidjanTV Politique,Politica africana y nacional,https://abidjantv.net/feed/,11,Política,45,Costa de Marfil,fr,True,0 +4086,Afrique Sur 7 Politique,Politica africana y asuntos marfilenos,https://www.afrique-sur7.ci/rss,11,Política,45,Costa de Marfil,fr,False,30 +4157,BBC Africa Politique,Politica africana noticias globales,https://feeds.bbci.co.uk/news/world/africa/rss.xml,11,Política,45,Costa de Marfil,en,True,0 +4130,BCEAO Politique,Politica monetaria y economica en UEMOA,https://www.bceao.int/fr/rss,11,Política,45,Costa de Marfil,fr,False,30 +4083,Fraternite Matin Politique,Politica marfilena gobierno y partidos,https://www.fratmat.info/rss,11,Política,45,Costa de Marfil,fr,False,30 +3649,ICG Politique Afrique,Analisis de riesgo politico y conflictos,https://www.crisisgroup.org/rss.xml,11,Política,45,Costa de Marfil,en,True,0 +4090,Jeune Afrique Politique,Politica africana y gobierno,https://www.jeuneafrique.com/feeds/home,11,Política,45,Costa de Marfil,fr,False,30 +4087,Koaci Politique,Politica africana y actualidad marfilena,https://www.koaci.com/rss,11,Política,45,Costa de Marfil,fr,False,30 +4166,Le Patriote Politique,Politica partidaria y gobierno,https://www.lepatriote.ci/rss,11,Política,45,Costa de Marfil,fr,False,30 +4085,Linfodrome Politique,Politica nacional y region CEDEAO,https://www.linfodrome.com/rss,11,Política,45,Costa de Marfil,fr,True,0 +4167,Notre Voie Politique,Politica nacional y oposicion,https://www.notrevoie.com/feed/,11,Política,45,Costa de Marfil,fr,False,30 +4171,ONU News Afrique,Politica internacional ONU y Africa,https://news.un.org/feed/subscribe/fr/news/all/rss.xml,11,Política,45,Costa de Marfil,fr,True,0 +1835,RFI Afrique Politique,Politica africana y relaciones internacionales,https://www.rfi.fr/fr/rss,11,Política,45,Costa de Marfil,fr,True,0 +4156,Reuters Africa Politique,Politica africana y global,https://www.reuters.com/world/africa/rss,11,Política,45,Costa de Marfil,en,False,30 +4093,UNDP CI Politique,Politicas publicas y gobernanza,https://www.undp.org/cote-divoire/rss.xml,11,Política,45,Costa de Marfil,fr,False,30 +4173,Union Africaine Politique,Politica continental africana,https://au.int/en/rss.xml,11,Política,45,Costa de Marfil,en,True,0 +4198,24sata Znanost,Noticias de ciencia y descubrimientos publicadas por 24sata,https://www.24sata.hr/feeds/znanost.xml,1,Ciencia,47,Croacia,hr,True,0 +4200,Glas Istre Znanost,Seccion de ciencia del periodico regional Glas Istre,https://www.glasistre.hr/rss/znanost,1,Ciencia,47,Croacia,hr,False,30 +4199,HRT Znanost,Contenido cientifico del portal de la television publica HRT,https://vijesti.hrt.hr/rss/znanost,1,Ciencia,47,Croacia,hr,False,30 +4194,Index hr Znanost,Seccion de ciencia y avances tecnologicos del portal Index hr,https://www.index.hr/rss/znanost,1,Ciencia,47,Croacia,hr,False,30 +4195,Jutarnji Znanost,Noticias de ciencia e investigacion publicadas en Jutarnji List,https://www.jutarnji.hr/rss/znanost,1,Ciencia,47,Croacia,hr,False,30 +4197,Nacional Znanost,Informacion sobre ciencia e innovacion en Croacia desde Nacional hr,https://www.nacional.hr/category/znanost/feed/,1,Ciencia,47,Croacia,hr,True,0 +4201,Science Croatia EN,Portal croata de divulgacion cientifica en ingles,https://www.croatianscience.org/feed,1,Ciencia,47,Croacia,en,False,30 +4196,Vecernji Znanost,Articulos cientificos y de investigacion del diario Vecernji List,https://www.vecernji.hr/premium/feed/znanost,1,Ciencia,47,Croacia,hr,False,30 +4204,24sata Kultura,Eventos culturales y noticias de entretenimiento,https://www.24sata.hr/feeds/kultura.xml,2,Cultura,47,Croacia,hr,False,30 +4209,Croatia Week Culture,Portal en ingles con informacion cultural de Croacia,https://www.croatiaweek.com/category/culture/feed/,2,Cultura,47,Croacia,en,True,0 +4206,HRT Kultura,Noticias culturales de la television publica croata HRT,https://vijesti.hrt.hr/rss/kultura,2,Cultura,47,Croacia,hr,False,30 +4202,Jutarnji Kultura,Seccion cultural del diario Jutarnji List con noticias de arte y eventos,https://www.jutarnji.hr/rss/kultura,2,Cultura,47,Croacia,hr,False,30 +4208,Kulturpunkt,Portal independiente sobre cultura contemporanea en Croacia,https://www.kulturpunkt.hr/feeds,2,Cultura,47,Croacia,hr,False,30 +4207,Nacional Kultura,Seccion cultural del portal Nacional hr,https://www.nacional.hr/category/kultura/feed/,2,Cultura,47,Croacia,hr,True,0 +4205,Slobodna Dalmacija Kultura,Articulos culturales y agenda del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/kultura,2,Cultura,47,Croacia,hr,False,30 +4203,Vecernji Kultura,Noticias de cultura del diario Vecernji List,https://www.vecernji.hr/premium/feed/kultura,2,Cultura,47,Croacia,hr,False,30 +4213,24sata Sport,Cobertura deportiva de 24sata,https://www.24sata.hr/feeds/sport.xml,3,Deportes,47,Croacia,hr,True,0 +4217,Croatia Week Sports,Portal en ingles con noticias deportivas de Croacia,https://www.croatiaweek.com/category/sport/feed/,3,Deportes,47,Croacia,en,True,0 +4214,HRT Sport,Deportes y competiciones en la television publica croata HRT,https://vijesti.hrt.hr/rss/sport,3,Deportes,47,Croacia,hr,False,30 +4182,Index hr Sport,Seccion deportiva del portal Index hr con noticias y resultados,https://www.index.hr/rss/sport,3,Deportes,47,Croacia,hr,True,0 +4211,Jutarnji Sport,Noticias deportivas nacionales e internacionales de Jutarnji List,https://www.jutarnji.hr/rss/sport,3,Deportes,47,Croacia,hr,False,30 +4215,Slobodna Dalmacija Sport,Seccion deportiva del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/sport,3,Deportes,47,Croacia,hr,False,30 +4212,Vecernji Sport,Actualidad deportiva del diario Vecernji List,https://www.vecernji.hr/premium/feed/sport,3,Deportes,47,Croacia,hr,False,30 +4221,24sata Economia,Economia y finanzas en el portal 24sata,https://www.24sata.hr/feeds/biznis.xml,4,Economía,47,Croacia,hr,True,0 +4225,Croatia Week Business,Portal en ingles con economia y negocios en Croacia,https://www.croatiaweek.com/category/business/feed/,4,Economía,47,Croacia,en,True,0 +4223,HRT Economia,Economia y mercado en la television publica croata HRT,https://vijesti.hrt.hr/rss/gospodarstvo,4,Economía,47,Croacia,hr,False,30 +4219,Jutarnji Economia,Seccion economica del diario Jutarnji List,https://www.jutarnji.hr/rss/biznis,4,Economía,47,Croacia,hr,False,30 +4218,Poslovni Dnevnik Economia,Noticias de economia y negocios en Croacia,https://www.poslovni.hr/rss,4,Economía,47,Croacia,hr,False,30 +4224,Slobodna Dalmacija Economia,Seccion economica del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/gospodarstvo,4,Economía,47,Croacia,hr,False,30 +4220,Vecernji Economia,Noticias de economia y empresas desde Vecernji List,https://www.vecernji.hr/premium/feed/biznis,4,Economía,47,Croacia,hr,False,30 +4230,24sata Show,Articulos de espectaculos y ocio del medio 24sata,https://www.24sata.hr/feeds/show.xml,6,Entretenimiento,47,Croacia,hr,True,0 +4234,Croatia Week Entertainment,Portal en ingles con noticias de entretenimiento de Croacia,https://www.croatiaweek.com/category/entertainment/feed/,6,Entretenimiento,47,Croacia,en,True,0 +4232,HRT Zabava,Seccion de entretenimiento de la television publica HRT,https://magazin.hrt.hr/rss,6,Entretenimiento,47,Croacia,hr,False,30 +4226,Index hr Show,Noticias de espectaculos y ocio en Croacia,https://www.index.hr/rss/show,6,Entretenimiento,47,Croacia,hr,False,30 +4228,Jutarnji Life,Seccion de ocio y entretenimiento del diario Jutarnji List,https://www.jutarnji.hr/rss/life,6,Entretenimiento,47,Croacia,hr,False,30 +4233,Nacional Zabava,Noticias de ocio y espectaculos del portal Nacional hr,https://www.nacional.hr/category/zabava/feed/,6,Entretenimiento,47,Croacia,hr,True,0 +4231,Slobodna Dalmacija Scena,Noticias de entretenimiento y cultura pop del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/scena,6,Entretenimiento,47,Croacia,hr,False,30 +4229,Vecernji Showbiz,Noticias de cine musica y famosos del portal Vecernji,https://www.vecernji.hr/premium/feed/show,6,Entretenimiento,47,Croacia,hr,False,30 +4238,24sata Svijet,Actualidad internacional de 24sata,https://www.24sata.hr/feeds/svijet.xml,7,Internacional,47,Croacia,hr,True,0 +4242,Croatia Week World,Portal en ingles con noticias internacionales relevantes para Croacia,https://www.croatiaweek.com/category/news/feed/,7,Internacional,47,Croacia,en,True,0 +4240,HRT Svijet,Noticias internacionales de la television publica HRT,https://vijesti.hrt.hr/rss/svijet,7,Internacional,47,Croacia,hr,False,30 +4181,Index hr Vijesti Svijet,Noticias internacionales del portal Index hr,https://www.index.hr/rss/vijesti-svijet,7,Internacional,47,Croacia,hr,True,0 +4236,Jutarnji Svijet,Seccion de noticias globales de Jutarnji List,https://www.jutarnji.hr/rss/svijet,7,Internacional,47,Croacia,hr,False,30 +4241,Slobodna Dalmacija Svijet,Noticias internacionales del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/svijet,7,Internacional,47,Croacia,hr,False,30 +4237,Vecernji Svijet,Noticias del mundo publicadas por Vecernji List,https://www.vecernji.hr/premium/feed/svijet,7,Internacional,47,Croacia,hr,False,30 +4246,24sata Okolis,Informacion ambiental de 24sata,https://www.24sata.hr/feeds/okolis.xml,8,Medio Ambiente,47,Croacia,hr,False,30 +4250,Croatia Week Environment,Portal en ingles con noticias ambientales de Croacia y la region,https://www.croatiaweek.com/category/environment/feed/,8,Medio Ambiente,47,Croacia,en,True,0 +4247,HRT Okolis,Reportajes y noticias ambientales de la television publica HRT,https://vijesti.hrt.hr/rss/okolis,8,Medio Ambiente,47,Croacia,hr,False,30 +4243,Index hr Natur,Noticias sobre medio ambiente y naturaleza en Croacia,https://www.index.hr/rss/natur,8,Medio Ambiente,47,Croacia,hr,False,30 +4244,Jutarnji Okolis,Articulos sobre ecologia y medio ambiente del diario Jutarnji List,https://www.jutarnji.hr/rss/okolis,8,Medio Ambiente,47,Croacia,hr,False,30 +4249,Nacional Okolis,Noticias sobre medio ambiente en el portal Nacional hr,https://www.nacional.hr/category/okolis/feed/,8,Medio Ambiente,47,Croacia,hr,True,0 +4248,Slobodna Dalmacija Okolis,Seccion ambiental del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/okolis,8,Medio Ambiente,47,Croacia,hr,False,30 +4245,Vecernji Okolis,Noticias ambientales y temas ecologicos de Vecernji List,https://www.vecernji.hr/premium/feed/okolis,8,Medio Ambiente,47,Croacia,hr,False,30 +4190,24sata Politika,Seccion politica del medio 24sata,https://www.24sata.hr/feeds/politika.xml,11,Política,47,Croacia,hr,True,0 +4189,Dnevnik Politika,Actualidad politica desde el portal Dnevnik hr,https://dnevnik.hr/rss/politika,11,Política,47,Croacia,hr,False,30 +4183,Gobierno de Croacia HR,Noticias oficiales del gobierno de la Republica de Croacia en croata,https://vlada.gov.hr/rss.aspx?ID=8,11,Política,47,Croacia,hr,True,0 +4185,Gobierno de Croacia Sesiones,Informes sobre sesiones y decisiones del gobierno de Croacia,https://vlada.gov.hr/rss.aspx?ID=9,11,Política,47,Croacia,hr,True,0 +4192,HRT Politika,Noticias politicas de la television publica HRT,https://vijesti.hrt.hr/rss/politika,11,Política,47,Croacia,hr,False,30 +4186,Jutarnji Politika,Seccion politica del diario Jutarnji List,https://www.jutarnji.hr/rss/politika,11,Política,47,Croacia,hr,False,30 +4188,N1 HR Politika,Noticias politicas del canal N1 Croacia,https://n1info.hr/feed/,11,Política,47,Croacia,hr,True,0 +4191,Slobodna Dalmacija Politika,Noticias politicas del diario Slobodna Dalmacija,https://slobodnadalmacija.hr/rss/politika,11,Política,47,Croacia,hr,False,30 +4187,Vecernji Politika,Noticias politicas nacionales y regionales de Vecernji List,https://www.vecernji.hr/premium/feed/politika,11,Política,47,Croacia,hr,False,30 +4184,Vlada RH EN,Comunicados oficiales del gobierno de Croacia en ingles,https://vlada.gov.hr/rss.aspx?ID=14956,11,Política,47,Croacia,en,True,0 +4178,Index hr Najnovije,Portal general con ultimas noticias de Croacia en linea,https://www.index.hr/rss,13,Sociedad,47,Croacia,hr,True,0 +4179,Index hr Vijesti,Seccion de noticias generales del portal Index hr,https://www.index.hr/rss/vijesti,13,Sociedad,47,Croacia,hr,True,0 +4180,Index hr Vijesti Hrvatska,Noticias sobre temas nacionales y locales de Croacia,https://www.index.hr/rss/vijesti-hrvatska,13,Sociedad,47,Croacia,hr,True,0 +4265,Revista Juventud Tecnica,Portal de ciencia tecnologia e inovacion cubana,https://www.juventudtecnica.cu/rss.xml,1,Ciencia,48,Cuba,es,False,30 +4279,ACN Deportes,Informacion deportiva de la Agencia Cubana de Noticias,https://www.acn.cu/deportes/feed,3,Deportes,48,Cuba,es,False,30 +4282,Cuba Si Deportes,Seccion de deportes del portal Cuba Si,https://cubasi.cu/categoria/deportes/rss,3,Deportes,48,Cuba,es,False,30 +4275,CubaDebate Deportes,Noticias deportivas de Cuba publicadas en Cubadebate,https://www.cubadebate.cu/categoria/temas/deportes/feed/,3,Deportes,48,Cuba,es,False,30 +4276,Granma Deportes,Seccion deportiva del periodico Granma,https://www.granma.cu/feed/deportes,3,Deportes,48,Cuba,es,True,2 +4277,Juventud Rebelde Deportes,Articulos deportivos de Juventud Rebelde,https://www.juventudrebelde.cu/seccion/deportes/rss,3,Deportes,48,Cuba,es,False,30 +4281,Radio Habana Cuba Deportes,Contenido deportivo emitido por Radio Habana Cuba,https://www.radiohc.cu/rss/deportes,3,Deportes,48,Cuba,es,False,30 +4280,Radio Reloj Deportes,Noticias deportivas en Radio Reloj,https://www.radioreloj.cu/category/deportes/feed/,3,Deportes,48,Cuba,es,False,30 +4287,ACN Economia,Informacion economica de la Agencia Cubana de Noticias,https://www.acn.cu/economia/feed,4,Economía,48,Cuba,es,False,30 +4290,Cuba Si Economia,Seccion economica del portal Cuba Si,https://cubasi.cu/categoria/economia/rss,4,Economía,48,Cuba,es,False,30 +4283,CubaDebate Economia,Noticias de economia y negocios en Cuba publicadas en Cubadebate,https://www.cubadebate.cu/categoria/temas/economia/feed/,4,Economía,48,Cuba,es,False,30 +4284,Granma Economia,Seccion economica del periodico Granma,https://www.granma.cu/feed/economia,4,Economía,48,Cuba,es,False,30 +4285,Juventud Rebelde Economia,Articulos economicos en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/economia/rss,4,Economía,48,Cuba,es,False,30 +4289,Radio Habana Cuba Economia,Contenido economico emitido por Radio Habana Cuba,https://www.radiohc.cu/rss/economia,4,Economía,48,Cuba,es,False,30 +4288,Radio Reloj Economia,Noticias economicas en Radio Reloj,https://www.radioreloj.cu/category/economia/feed/,4,Economía,48,Cuba,es,False,30 +4295,ACN Educacion,Informacion educativa de la Agencia Cubana de Noticias,https://www.acn.cu/educacion/feed,5,Educación,48,Cuba,es,False,30 +4298,Cuba Si Educacion,Seccion educativa del portal Cuba Si,https://cubasi.cu/categoria/educacion/rss,5,Educación,48,Cuba,es,False,30 +4291,CubaDebate Educacion,Noticias educativas en Cuba publicadas por Cubadebate,https://www.cubadebate.cu/categoria/temas/educacion/feed/,5,Educación,48,Cuba,es,False,30 +4292,Granma Educacion,Seccion de educacion del periodico Granma,https://www.granma.cu/feed/educacion,5,Educación,48,Cuba,es,False,30 +4293,Juventud Rebelde Educacion,Articulos sobre educacion en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/educacion/rss,5,Educación,48,Cuba,es,False,30 +4297,Radio Habana Cuba Educacion,Programas y noticias educativas de Radio Habana Cuba,https://www.radiohc.cu/rss/educacion,5,Educación,48,Cuba,es,False,30 +4296,Radio Reloj Educacion,Contenido educativo de Radio Reloj,https://www.radioreloj.cu/category/educacion/feed/,5,Educación,48,Cuba,es,False,30 +4271,ACN Cultura y Ocio,Informacion cultural y de entretenimiento de la Agencia Cubana de Noticias,https://www.acn.cu/cultura/feed,6,Entretenimiento,48,Cuba,es,False,30 +4274,Cuba Si Espectaculos,Seccion de entretenimiento y espectaculos del portal Cuba Si,https://cubasi.cu/categoria/cultura/rss,6,Entretenimiento,48,Cuba,es,False,30 +4267,CubaDebate Entretenimiento,Noticias de ocio espectaculos y cultura pop en CubaDebate,https://www.cubadebate.cu/categoria/temas/cultura/feed/,6,Entretenimiento,48,Cuba,es,False,30 +4268,Granma Cultura y Ocio,Seccion de ocio espectaculos y actividades culturales del diario Granma,https://www.granma.cu/feed/cultura,6,Entretenimiento,48,Cuba,es,True,0 +4269,Juventud Rebelde Espectaculos,Articulos de espectaculos y entretenimiento en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/cultura/rss,6,Entretenimiento,48,Cuba,es,False,30 +4273,Radio Habana Cuba Entretenimiento,Programas y noticias de ocio y espectaculos de Radio Habana Cuba,https://www.radiohc.cu/rss/cultura,6,Entretenimiento,48,Cuba,es,False,30 +4272,Radio Reloj Cultura y Ocio,Contenido cultural y de entretenimiento emitido por Radio Reloj,https://www.radioreloj.cu/category/cultura/feed/,6,Entretenimiento,48,Cuba,es,False,30 +4311,ACN Internacional,Informacion internacional de la Agencia Cubana de Noticias,https://www.acn.cu/mundo/feed,7,Internacional,48,Cuba,es,False,30 +4314,Cuba Si Internacional,Seccion internacional del portal Cuba Si,https://cubasi.cu/categoria/mundo/rss,7,Internacional,48,Cuba,es,False,30 +4307,CubaDebate Internacional,Noticias internacionales publicadas por CubaDebate,https://www.cubadebate.cu/categoria/temas/internacional/feed/,7,Internacional,48,Cuba,es,False,30 +4308,Granma Internacional,Seccion internacional del periodico Granma,https://www.granma.cu/feed/mundo,7,Internacional,48,Cuba,es,True,0 +4309,Juventud Rebelde Internacional,Articulos internacionales de Juventud Rebelde,https://www.juventudrebelde.cu/seccion/internacionales/rss,7,Internacional,48,Cuba,es,False,30 +4313,Radio Habana Cuba Internacional,Contenido internacional emitido por Radio Habana Cuba,https://www.radiohc.cu/rss/internacional,7,Internacional,48,Cuba,es,False,30 +4312,Radio Reloj Internacional,Noticias internacionales en Radio Reloj,https://www.radioreloj.cu/category/internacionales/feed/,7,Internacional,48,Cuba,es,False,30 +4319,ACN Politica,Informacion politica de la Agencia Cubana de Noticias,https://www.acn.cu/politica/feed,11,Política,48,Cuba,es,False,30 +4322,Cuba Si Politica,Seccion politica del portal Cuba Si,https://cubasi.cu/categoria/politica/rss,11,Política,48,Cuba,es,False,30 +4315,CubaDebate Politica,Noticias politicas de Cuba publicadas por CubaDebate,https://www.cubadebate.cu/categoria/temas/politica/feed/,11,Política,48,Cuba,es,False,30 +4316,Granma Politica,Seccion politica del periodico Granma,https://www.granma.cu/feed/politica,11,Política,48,Cuba,es,False,30 +4317,Juventud Rebelde Politica,Articulos politicos en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/politica/rss,11,Política,48,Cuba,es,False,30 +4321,Radio Habana Cuba Politica,Programas y noticias politicas en Radio Habana Cuba,https://www.radiohc.cu/rss/politica,11,Política,48,Cuba,es,False,30 +4320,Radio Reloj Politica,Contenido politico emitido por Radio Reloj,https://www.radioreloj.cu/category/politica/feed/,11,Política,48,Cuba,es,False,30 +4327,ACN Salud,Informacion de salud y bienestar de la Agencia Cubana de Noticias,https://www.acn.cu/salud/feed,12,Salud,48,Cuba,es,False,30 +4330,Cuba Si Salud,Seccion de salud del portal Cuba Si,https://cubasi.cu/categoria/salud/rss,12,Salud,48,Cuba,es,False,30 +4323,CubaDebate Salud,Noticias de salud medicina y bienestar publicadas por CubaDebate,https://www.cubadebate.cu/categoria/temas/salud/feed/,12,Salud,48,Cuba,es,False,30 +4324,Granma Salud,Seccion de salud del periodico Granma,https://www.granma.cu/feed/salud,12,Salud,48,Cuba,es,True,0 +4325,Juventud Rebelde Salud,Articulos de salud y medicina en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/salud/rss,12,Salud,48,Cuba,es,False,30 +4329,Radio Habana Cuba Salud,Programas y contenidos sobre salud en Radio Habana Cuba,https://www.radiohc.cu/rss/salud,12,Salud,48,Cuba,es,False,30 +4328,Radio Reloj Salud,Noticias de salud publicadas en Radio Reloj,https://www.radioreloj.cu/category/salud/feed/,12,Salud,48,Cuba,es,False,30 +4335,ACN Sociedad,Informacion social de la Agencia Cubana de Noticias,https://www.acn.cu/sociedad/feed,13,Sociedad,48,Cuba,es,False,30 +4338,Cuba Si Sociedad,Seccion sociedad del portal Cuba Si,https://cubasi.cu/categoria/sociedad/rss,13,Sociedad,48,Cuba,es,False,30 +4331,CubaDebate Sociedad,Noticias sociales y vida cotidiana publicadas por CubaDebate,https://www.cubadebate.cu/categoria/temas/sociedad/feed/,13,Sociedad,48,Cuba,es,False,30 +4332,Granma Sociedad,Seccion de sociedad del periodico Granma,https://www.granma.cu/feed/sociedad,13,Sociedad,48,Cuba,es,False,30 +4333,Juventud Rebelde Sociedad,Articulos sociales en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/sociedad/rss,13,Sociedad,48,Cuba,es,False,30 +4337,Radio Habana Cuba Sociedad,Programas y contenidos sociales en Radio Habana Cuba,https://www.radiohc.cu/rss/sociedad,13,Sociedad,48,Cuba,es,False,30 +4336,Radio Reloj Sociedad,Noticias sociales emitidas por Radio Reloj,https://www.radioreloj.cu/category/sociedad/feed/,13,Sociedad,48,Cuba,es,False,30 +4264,ACN Tecnologia,Informacion de innovacion y tecnologia en Agencia Cubana de Noticias,https://www.acn.cu/ciencia/feed,14,Tecnología,48,Cuba,es,False,30 +4346,Cuba Si Tecnologia,Seccion de tecnologia e innovacion del portal Cuba Si,https://cubasi.cu/categoria/ciencia-tecnologia/rss,14,Tecnología,48,Cuba,es,False,30 +4259,CubaDebate Tecnologia,Noticias de tecnologia y avances digitales en CubaDebate,https://www.cubadebate.cu/categoria/temas/ciencia-tecnologia/feed/,14,Tecnología,48,Cuba,es,False,30 +4261,Granma Tecnologia,Seccion de ciencia y tecnologia del periodico Granma,https://www.granma.cu/feed/ciencia,14,Tecnología,48,Cuba,es,True,0 +4260,Juventud Rebelde Tecnologia,Articulos de tecnologia en Juventud Rebelde,https://www.juventudrebelde.cu/seccion/ciencia/rss,14,Tecnología,48,Cuba,es,False,30 +4262,Prensa Latina Tecnologia,Noticias tecnologicas y de innovacion en Prensa Latina,https://www.prensa-latina.cu/feed,14,Tecnología,48,Cuba,es,True,0 +4266,Radio Habana Cuba Tecnologia,Programas y noticias tecnologicas de Radio Habana Cuba,https://www.radiohc.cu/rss/ciencia,14,Tecnología,48,Cuba,es,False,30 +4263,Radio Reloj Tecnologia,Contenido sobre tecnologia y ciencia en Radio Reloj,https://www.radioreloj.cu/category/ciencia/feed/,14,Tecnología,48,Cuba,es,False,30 +4353,Copenhagen Science City,Noticias de investigacion e innovacion en Copenhague,https://copenhagensciencecity.dk/feed/,1,Ciencia,49,Dinamarca,en,True,0 +4348,Ingenioren Science,Articulos de ciencia tecnologia e ingenieria,https://pro.ing.dk/rss,1,Ciencia,49,Dinamarca,da,True,0 +4354,SCIENCE KU,Facultad de ciencias Universidad de Copenhague noticias cientificas,https://science.ku.dk/news/rss/,1,Ciencia,49,Dinamarca,en,False,30 +4349,Videnskab dk,Portal danés de divulgacion cientifica,https://videnskab.dk/rss,1,Ciencia,49,Dinamarca,da,False,30 +4357,Bold dk,Futbol danes noticias resultados y fichajes,https://www.bold.dk/rss,3,Deportes,49,Dinamarca,da,False,30 +4355,DR Sport,Noticias deportivas del ente publico DR,https://www.dr.dk/nyheder/service/feeds/sport,3,Deportes,49,Dinamarca,da,False,30 +4356,TV2 Sport,Noticias de deportes del canal TV2,https://sport.tv2.dk/rss,3,Deportes,49,Dinamarca,da,False,30 +4363,Borsen Economia,Principal portal economico y de negocios de Dinamarca,https://borsen.dk/rss,4,Economía,49,Dinamarca,da,True,0 +4365,DR Erhverv,Seccion de economia y negocios del ente publico DR,https://www.dr.dk/nyheder/service/feeds/penge,4,Economía,49,Dinamarca,da,True,0 +4364,Finans dk,Noticias financieras empresas y mercado laboral,https://finans.dk/rss,4,Economía,49,Dinamarca,da,False,30 +4377,Aarhus University News,Noticias educativas e investigacion de la Universidad de Aarhus,https://newsroom.au.dk/feed/en/,5,Educación,49,Dinamarca,en,False,30 +4376,Uniavisen KU,Noticias educativas de la Universidad de Copenhague,https://uniavisen.dk/en/feed/,5,Educación,49,Dinamarca,en,False,30 +4379,DR Underholdning,Noticias de entretenimiento musica cine y television del ente publico DR,https://www.dr.dk/nyheder/service/feeds/kultur,6,Entretenimiento,49,Dinamarca,da,True,0 +4380,TV2 Underholdning,Contenido de entretenimiento del canal TV2 incluyendo reality series y celebridades,https://underholdning.tv2.dk/rss,6,Entretenimiento,49,Dinamarca,da,False,30 +4387,DR Udland,Noticias internacionales del ente publico DR,https://www.dr.dk/nyheder/service/feeds/udland,7,Internacional,49,Dinamarca,da,True,0 +4386,Aarhus Today Environment,Noticias ambientales de Aarhus Today en ingles,https://aarhustoday.dk/feed/,8,Medio Ambiente,49,Dinamarca,en,False,30 +4347,DR Miljoe,Noticias ambientales y de clima del ente publico DR,https://www.dr.dk/nyheder/service/feeds/viden,8,Medio Ambiente,49,Dinamarca,da,True,0 +4375,Kristeligt Dagblad Klima,Analisis ambientales y de sostenibilidad,https://www.kristeligt-dagblad.dk/rss,8,Medio Ambiente,49,Dinamarca,da,False,30 +4366,TV2 Klima,Noticias de clima y sostenibilidad del canal TV2,https://nyheder.tv2.dk/rss,8,Medio Ambiente,49,Dinamarca,da,False,30 +4350,Berlingske RSS,Feed del diario Berlingske con contenido politico y de actualidad,https://www.berlingske.dk/rss,11,Política,49,Dinamarca,da,False,30 +4362,Copenhagen Post RSS,"Feed del medio en ingles ""Copenhagen Post"" con cobertura politica en Dinamarca",https://cphpost.dk/feed/,11,Política,49,Dinamarca,en,True,0 +4417,DR Politik (via Mastodon bot),Feed automatizado desde DR Politica para contenido politico,https://expressional.social/%40DRPolitik,11,Política,49,Dinamarca,da,False,30 +4358,Ekstra Bladet RSS,Feed del tabloide Ekstra Bladet con noticias politicas y de actualidad,https://ekstrabladet.dk/rss,11,Política,49,Dinamarca,da,False,30 +4352,Information RSS,Feed del diario Dagbladet Information con secciones politicas y sociales,https://www.information.dk/rss,11,Política,49,Dinamarca,da,False,30 +4416,Last Week in Denmark Politics,Feed del portal LWID centrado en politica y sociedad danesa,https://lwid.dk/,11,Política,49,Dinamarca,en,False,30 +4351,Politiken RSS,Feed general del diario Politiken incluyendo seccion politica,https://politiken.dk/rss,11,Política,49,Dinamarca,da,False,30 +4418,The Local Denmark Politics,Feed del medio anglophone sobre noticias politicas danesas,https://feeds.thelocal.dk/denmark-news.rss,11,Política,49,Dinamarca,en,False,30 +4420,Caribbean News Global Politics,Cobertura politica caribena con enfasis en Dominica,https://caribbeannewsglobal.com/feed/,11,Política,50,Dominica,en,True,0 +4421,DA Vibes Politics,Noticias politicas y gubernamentales de Dominica,https://www.dominicavibes.dm/feed/,11,Política,50,Dominica,en,False,30 +4419,Loop News Caribbean Politics,Noticias politicas del Caribe incluyendo Dominica,https://caribbean.loopnews.com/rss.xml,11,Política,50,Dominica,en,False,30 +4425,The Sun Dominica Politics,Contenido politico del periodico The Sun Dominica,https://sundominica.com/articles/rss/,11,Política,50,Dominica,en,False,30 +4436,El Telégrafo Ciencia,Noticias de ciencia innovacion y desarrollo,https://www.eltelegrafo.com.ec/rss/actualidad,1,Ciencia,51,Ecuador,es,False,30 +4444,El Telegrafo Cultura,Articulos de cultura identidad patrimonio y artes,https://www.eltelegrafo.com.ec/rss/cultura,2,Cultura,51,Ecuador,es,False,30 +4439,El Universo Cultura,Seccion de cultura del diario El Universo,https://www.eluniverso.com/rss/cultura/,2,Cultura,51,Ecuador,es,False,30 +4453,Bendito Futbol RSS,Portal especializado en futbol ecuatoriano y mundial,https://www.benditofutbol.com/rss/,3,Deportes,51,Ecuador,es,True,0 +4452,El Telegrafo Deportes,Noticias de deportes en Ecuador y el mundo,https://www.eltelegrafo.com.ec/rss/deportes,3,Deportes,51,Ecuador,es,False,30 +4447,El Universo Deportes,Seccion deportiva del diario El Universo,https://www.eluniverso.com/rss/deportes/,3,Deportes,51,Ecuador,es,False,30 +4460,El Telegrafo Economia,Noticias economicas del diario El Telegrafo,https://www.eltelegrafo.com.ec/rss/economia,4,Economía,51,Ecuador,es,False,30 +4455,El Universo Economia,Seccion de economia del diario El Universo,https://www.eluniverso.com/rss/economia/,4,Economía,51,Ecuador,es,False,30 +4461,Revista Lideres Economia,Portal especializado en economia negocios y emprendimiento,https://www.revistalideres.ec/rss.xml,4,Economía,51,Ecuador,es,False,30 +4468,El Telegrafo Politica,Noticias politicas del diario El Telegrafo,https://www.eltelegrafo.com.ec/rss/politica,11,Política,51,Ecuador,es,False,30 +4463,El Universo Politica,Seccion politica del diario El Universo,https://www.eluniverso.com/rss/politica/,11,Política,51,Ecuador,es,False,30 +4437,Plan V Politica,Analisis politico y reportajes investigativos de Plan V,https://www.planv.com.ec/rss.xml,11,Política,51,Ecuador,es,False,30 +4477,Dinero Digital Ecuador,Medio especializado en tecnologia financiera y economia digital,https://www.dinerodigital.ec/feed/,14,Tecnología,51,Ecuador,es,False,30 +4435,Ecuavisa Tecnologia,Noticias sobre tecnologia avances y nuevas aplicaciones,https://www.ecuavisa.com/rss,14,Tecnología,51,Ecuador,es,False,30 +4431,El Comercio Tecnologia,Seccion de tecnologia del diario El Comercio,https://www.elcomercio.com/feed/,14,Tecnología,51,Ecuador,es,True,0 +4476,El Telegrafo Tecnologia,Noticias de tecnologia e innovacion del diario El Telegrafo,https://www.eltelegrafo.com.ec/rss/tecnologia,14,Tecnología,51,Ecuador,es,False,30 +4430,El Universo Tecnologia,Noticias de tecnologia innovacion y ciencia aplicada,https://www.eluniverso.com/rss/tecnologia/,14,Tecnología,51,Ecuador,es,False,30 +4432,La Republica Tecnologia,Informacion tecnologica del portal La Republica Ecuador,https://www.larepublica.ec/feed/,14,Tecnología,51,Ecuador,es,True,0 +4433,Metro Ecuador Tecnologia,Contenido tecnologico del portal Metro Ecuador,https://www.metroecuador.com.ec/rss/,14,Tecnología,51,Ecuador,es,False,30 +4434,Primicias Tecnologia,Articulos de innovacion startups y tecnologia,https://www.primicias.ec/feed/,14,Tecnología,51,Ecuador,es,False,30 +4478,Egypt Independent Science,Noticias de ciencia medio ambiente y arqueologia,https://egyptindependent.com/category/science/feed/,1,Ciencia,52,Egipto,en,True,0 +4485,Egypt Today Culture,Portal con noticias culturales eventos y patrimonio de Egipto,https://www.egypttoday.com/Home/SectionRss?id=5,2,Cultura,52,Egipto,en,False,30 +4489,Egypt Today Politics,Noticias politicas gobierno y asuntos estatales,https://www.egypttoday.com/Home/SectionRss?id=1,11,Política,52,Egipto,en,False,30 +4480,Daily News Egypt Tech,Articulos sobre tecnologia innovacion y economia digital,https://dailynewsegypt.com/feed/,14,Tecnología,52,Egipto,en,True,0 +4479,Egypt Independent Tech,Noticias de tecnologia y ciencia aplicada en Egipto,https://egyptindependent.com/feed/,14,Tecnología,52,Egipto,en,True,0 +4493,Egypt Today Tech,Noticias de tecnologia startups y digitalizacion en Egipto,https://www.egypttoday.com/Home/SectionRss?id=8,14,Tecnología,52,Egipto,en,False,30 +4481,Egyptian Streets Tech,Contenido tecnologico cultura digital y avances tecnologicos,https://egyptianstreets.com/feed/,14,Tecnología,52,Egipto,en,True,0 +2011,Diario El Salvador - Noticias,"Medio gubernamental con enfoque en desarrollo nacional, economía y políticas públicas.",https://diarioelsalvador.com/feed/,7,Internacional,53,El Salvador,es,False,30 +2007,El Diario de Hoy - Noticias,"Medio histórico salvadoreño con noticias nacionales, opinión, deportes y actualidad.",https://www.elsalvador.com/feed/,7,Internacional,53,El Salvador,es,True,0 +2010,Radio YSKL - Noticias,"Emisora salvadoreña con información de política, deportes, seguridad y actualidad.",https://radiokl.com/feed/,7,Internacional,53,El Salvador,es,True,0 +2008,La Página - Noticias,"Cobertura de sucesos, política, espectáculos y eventos relevantes en El Salvador.",https://lapagina.com.sv/feed/,13,Sociedad,53,El Salvador,es,True,0 +2013,Noticias Menotty - Actualidad,"Sitio informativo con reportes de sucesos, política y temas sociales.",https://menotty.com/feed/,13,Sociedad,53,El Salvador,es,False,30 +2009,Solo Noticias - Nacionales,"Noticias de última hora, sucesos y reportes del país actualizados constantemente.",https://www.solonoticias.com/feed/,13,Sociedad,53,El Salvador,es,False,30 +2012,UDN - Noticias de El Salvador,"Portal informativo digital con noticias de seguridad, economía y sociedad.",https://udn.news/feed/,13,Sociedad,53,El Salvador,es,False,30 +2014,Diario El Salvador - Tecnología,"Noticias de innovación, startups, ciberseguridad y avances tecnológicos con enfoque nacional.",https://diarioelsalvador.com/seccion/tecnologia/feed/,14,Tecnología,53,El Salvador,es,True,0 +2016,El Blog - Tecnología,"Artículos sobre tecnología, redes sociales, gadgets y cultura digital.",https://elblog.com/tag/tecnologia/feed/,14,Tecnología,53,El Salvador,es,True,0 +2017,El Universal Centroamérica - Tecnología,Portal regional con noticias tecnológicas relevantes para Centroamérica y El Salvador.,https://eluniversalcentroamerica.com/category/tecnologia/feed/,14,Tecnología,53,El Salvador,es,False,30 +2015,La Página - Tecnología,"Cobertura de novedades tecnológicas, redes sociales y tendencias digitales.",https://lapagina.com.sv/category/tecnologia/feed/,14,Tecnología,53,El Salvador,es,True,0 +2018,PC World Latinoamérica - Tecnología,"Tendencias tecnológicas, innovación y análisis con cobertura para El Salvador y la región.",https://www.pcworldenespanol.com/feed/,14,Tecnología,53,El Salvador,es,False,30 +2019,Revista IT Now - Centroamérica,"Medio especializado en TI, negocios digitales, ciberseguridad y transformación tecnológica.",https://revistaitnow.com/feed/,14,Tecnología,53,El Salvador,es,False,30 +5412,AME Info – Business & Finance,"Portal financiero y empresarial con fuerte enfoque en economía, energía y mercados del Golfo y Emiratos Árabes Unidos.",https://www.ameinfo.com/feed,4,Economía,54,Emiratos Árabes Unidos,en,False,30 +5406,Gulf Business – UAE Business News,"Medio especializado en negocios, finanzas, energía, empresas y economía del Golfo con cobertura destacada de Emiratos Árabes Unidos.",https://www.gulfbusiness.com/feed/,4,Economía,54,Emiratos Árabes Unidos,en,True,0 +5408,Khaleej Times – Business,Cobertura económica y financiera de Emiratos y la región del Golfo.,https://www.khaleejtimes.com/business/rss,4,Economía,54,Emiratos Árabes Unidos,en,False,30 +5405,The Arabian Post – Business & Gulf,"Medio digital centrado en economía y negocios del Golfo, con noticias sobre banca, energía, tecnología, marketing y política económica.",https://thearabianpost.com/feed,4,Economía,54,Emiratos Árabes Unidos,en,True,0 +5401,Emirates News Agency (WAM) – English,"Agencia oficial de noticias de Emiratos Árabes Unidos, con cobertura de política, economía, sociedad y actualidad del país y la región.",https://www.wam.ae/en/rss,7,Internacional,54,Emiratos Árabes Unidos,en,False,30 +5402,Dubai Chronicle – General,"Revista digital sobre Dubái y Emiratos Árabes Unidos, con noticias de negocios, finanzas, estilo de vida y tendencias.",https://www.dubaichronicle.com/feed,13,Sociedad,54,Emiratos Árabes Unidos,en,True,0 +5407,Khaleej Times – Lifestyle,"Sección de estilo de vida del periódico Khaleej Times con artículos sobre cultura, sociedad y tendencias de Emiratos.",https://www.khaleejtimes.com/lifestyle/rss,13,Sociedad,54,Emiratos Árabes Unidos,en,False,30 +5410,Lovin Dubai – Lifestyle News,"Noticias ligeras, sucesos virales, cultura urbana y vida cotidiana en Dubái y Emiratos.",https://lovindubai.com/feed,13,Sociedad,54,Emiratos Árabes Unidos,en,True,0 +5403,UAE24x7 – News & Features,"Portal de noticias y reportajes sobre Emiratos, con foco en sociedad, cultura, estilo de vida y actualidad.",https://uae24x7.com/feed,13,Sociedad,54,Emiratos Árabes Unidos,en,False,30 +5404,Dubay Blog – Dubai & UAE,"Blog y portal sobre Dubái y Oriente Medio, con artículos sobre viajes, vida en Emiratos, negocios y cultura local.",https://dubayblog.com/feed,15,Viajes,54,Emiratos Árabes Unidos,en,True,0 +5411,Hotelier Middle East – Hospitality,Noticias del sector hotelero y turístico en Emiratos y Oriente Medio.,https://www.hoteliermiddleeast.com/rss.xml,15,Viajes,54,Emiratos Árabes Unidos,en,False,30 +5409,Time Out Dubai – Things to do,"Guía de ocio, eventos, restaurantes y actividades en Dubái y Emiratos Árabes Unidos.",https://www.timeoutdubai.com/rss,15,Viajes,54,Emiratos Árabes Unidos,en,False,30 +5503,AllAfrica – Eritrea News,"Agregador consolidado de noticias africanas con sección dedicada a Eritrea, cubriendo política, sociedad y economía.",https://allafrica.com/tools/headlines/rdf/eritrea/headlines.rdf,7,Internacional,55,Eritrea,en,True,0 +5505,East Africa Monitor – Eritrea,Análisis y noticias generales del Cuerno de África con sección dedicada a Eritrea.,https://eastafricamonitor.com/category/eritrea/feed/,7,Internacional,55,Eritrea,en,False,30 +5502,Eritrea Ministry of Information – General News,"Noticias generales, declaraciones oficiales y artículos del Ministerio de Información de Eritrea.",https://shabait.com/category/news/feed/,7,Internacional,55,Eritrea,en,True,0 +5504,Eritrean Press – News,"Portal de noticias con cobertura general sobre la situación política, economía y temas sociales en Eritrea.",https://eritreapress.net/feed/,7,Internacional,55,Eritrea,en,False,30 +5501,Shabait – Eritrean News Agency,"Agencia oficial de noticias del Gobierno de Eritrea, con información sobre política, sociedad, comunicados oficiales y eventos nacionales.",https://shabait.com/feed/,7,Internacional,55,Eritrea,en,True,0 +5619,Bratislava Sports – Local Sports Updates,"Portal regional con noticias deportivas, clubes locales, eventos y competiciones en Bratislava.",https://bratislava-sport.sk/feed/,3,Deportes,56,Eslovaquia,sk,False,30 +5620,Hockey Slovakia – Oficial,"Noticias de la federación de hockey de Eslovaquia, con ligas, selecciones, resultados y comunicados.",https://www.hockeyslovakia.sk/sk/rss,3,Deportes,56,Eslovaquia,sk,False,30 +5617,Pravda – Šport,"Noticias deportivas nacionales e internacionales: fútbol, hockey, torneos europeos y más.",https://rss.pravda.sk/rss/pravda-sport.xml,3,Deportes,56,Eslovaquia,sk,False,30 +5616,SME – Šport,"Sección de deportes del diario SME: fútbol, hockey, tenis, ciclismo y competiciones internacionales.",https://rss.sme.sk/rss/rss.asp?sek=sport,3,Deportes,56,Eslovaquia,sk,False,30 +5618,TA3 – Šport,"Cobertura deportiva del canal informativo TA3, con especial atención al fútbol eslovaco y hockey sobre hielo.",https://www.ta3.com/rss/sport,3,Deportes,56,Eslovaquia,sk,False,30 +5615,Šport – Sports News,"El principal medio deportivo eslovaco con noticias de fútbol, hockey, ligas europeas y competiciones locales.",https://sport.aktuality.sk/rss/,3,Deportes,56,Eslovaquia,sk,True,0 +5614,Forbes Slovakia – Business,"Revista de negocios e innovación con foco en empresas, liderazgo, emprendimiento y finanzas.",https://www.forbes.sk/feed/,4,Economía,56,Eslovaquia,sk,True,0 +5613,HN Online (Hospodárske noviny) – Ekonomika,"Diario económico líder en Eslovaquia con informes financieros, empresas, inversión y política económica.",https://hnonline.sk/rss/sekcia/ekonomika,4,Economía,56,Eslovaquia,sk,True,0 +5605,Pravda.sk – Ekonomika,"Noticias y análisis económicos sobre Eslovaquia, la UE y el mercado global.",https://rss.pravda.sk/rss/pravda-ekonomika.xml,4,Economía,56,Eslovaquia,sk,False,30 +5612,Trend.sk – Ekonomika,"El mayor portal económico del país con análisis de negocios, finanzas, energía y mercado laboral.",https://www.trend.sk/rss,4,Economía,56,Eslovaquia,sk,False,30 +5603,SME – Svet (Internacional),"Noticias internacionales del periódico SME, con cobertura europea y global.",https://rss.sme.sk/rss/rss.asp?sek=svet,7,Internacional,56,Eslovaquia,sk,False,30 +5606,TA3 – Slovak News TV,"Canal de noticias continuo con cobertura política, economía y eventos actuales.",https://www.ta3.com/rss,7,Internacional,56,Eslovaquia,sk,False,30 +5601,The Slovak Spectator – News,"Periódico en inglés con noticias generales, economía, política, cultura y análisis sobre Eslovaquia.",https://spectator.sme.sk/rss,7,Internacional,56,Eslovaquia,en,True,0 +5608,Bratislava Region – News Updates,Noticias institucionales y comunicados oficiales de la región de Bratislava.,https://bratislavskykraj.sk/feed/,13,Sociedad,56,Eslovaquia,sk,True,0 +5604,Pravda.sk – Správy,"Periódico nacional con noticias políticas, económicas, deportivas y sociales de Eslovaquia.",https://rss.pravda.sk/rss/pravda-spravy.xml,13,Sociedad,56,Eslovaquia,sk,False,30 +5602,SME – Správy (Noticias),"Uno de los periódicos más importantes de Eslovaquia, con noticias nacionales, política y sociedad.",https://rss.sme.sk/rss/rss.asp?sek=spravy,13,Sociedad,56,Eslovaquia,sk,False,30 +5607,Československá televízia – Novinky,"Noticias y reportes sobre política, sociedad y cultura, estilo medio público regional.",https://www.csmtv.sk/wp/feed/,13,Sociedad,56,Eslovaquia,sk,False,30 +5611,Pravda – Technika,"Noticias tecnológicas, ciencia aplicada y novedades digitales del medio nacional Pravda.",https://rss.pravda.sk/rss/pravda-technika.xml,14,Tecnología,56,Eslovaquia,sk,False,30 +5609,SME – Tech,"Noticias de tecnología, innovación, seguridad informática y dispositivos del principal medio eslovaco SME.",https://rss.sme.sk/rss/rss.asp?sek=tech,14,Tecnología,56,Eslovaquia,sk,False,30 +5610,Žive.sk – Technológie,"Portal tecnológico líder en Eslovaquia, con noticias sobre tecnología, software, hardware, ciberseguridad e innovación.",https://www.zive.sk/rss/,14,Tecnología,56,Eslovaquia,sk,False,30 +5722,Delo – Znanost (Ciencia),"Publicaciones científicas, investigaciones, descubrimientos y temas de ciencia aplicada del periódico Delo.",https://www.delo.si/znanost/rss,1,Ciencia,57,Eslovenia,sl,False,30 +5723,Dnevnik – Znanost,"Noticias científicas, investigación, biología, física, medio ambiente e innovación tecnológica.",https://www.dnevnik.si/znanost/rss,1,Ciencia,57,Eslovenia,sl,False,30 +5724,RTV Slovenija – Znanost in okolje,"Sección científica y ambiental de RTV: clima, biología, física, ecología y nuevas investigaciones.",https://www.rtvslo.si/znanost-in-okolje/rss,1,Ciencia,57,Eslovenia,sl,False,30 +5727,Science Slovenia – Research & Innovation,"Publicaciones sobre ciencia, innovación, investigación aplicada y proyectos científicos en Eslovenia.",https://www.scienceinslovenia.com/feed/,1,Ciencia,57,Eslovenia,en,False,30 +5726,Siol – Znanost,"Artículos científicos populares, tecnología avanzada, medicina, espacio y ciencia contemporánea.",https://siol.net/novice/znanost/rss,1,Ciencia,57,Eslovenia,sl,False,30 +5725,Slovenian Academy of Sciences and Arts – Announcements,"Comunicados oficiales, actividades científicas, simposios y publicaciones académicas.",https://www.sazu.si/feed,1,Ciencia,57,Eslovenia,sl,False,30 +5707,RTV Slovenija – Kultura,"Sección cultural de la radiotelevisión pública: arte, cine, literatura, patrimonio y eventos culturales.",https://www.rtvslo.si/kultura/rss,2,Cultura,57,Eslovenia,sl,True,0 +5706,Finance.si – Business,"Principal medio económico esloveno con noticias de negocios, mercados, empresas y finanzas.",https://www.finance.si/rss,4,Economía,57,Eslovenia,sl,False,30 +5708,RTV Slovenija – Svet (Internacional),Noticias internacionales del servicio público RTV Slovenija.,https://www.rtvslo.si/svet/rss,7,Internacional,57,Eslovenia,sl,True,0 +5701,STA – Slovenian Press Agency (English),"Agencia oficial de noticias de Eslovenia con cobertura nacional, política, economía e internacional.",https://english.sta.si/rss.xml,7,Internacional,57,Eslovenia,en,False,30 +5728,Delo – Politika,"Noticias políticas nacionales, gobierno, parlamento, elecciones y análisis del periódico Delo.",https://www.delo.si/politika/rss,11,Política,57,Eslovenia,sl,False,30 +5729,Dnevnik – Politika,"Cobertura política completa: gobierno, partidos, políticas públicas y asuntos legislativos.",https://www.dnevnik.si/politika/rss,11,Política,57,Eslovenia,sl,False,30 +5731,RTV Slovenija – Politika,"Información política del servicio público RTV sobre el gobierno, parlamento, decisiones estatales y geopolítica.",https://www.rtvslo.si/politika/rss,11,Política,57,Eslovenia,sl,False,30 +5733,STA – Politics (Slovenian Press Agency),Noticias políticas nacionales e internacionales de la agencia oficial STA.,https://www.sta.si/rss/politics,11,Política,57,Eslovenia,sl,False,30 +5730,Siol – Politika,"Noticias políticas, entrevistas, reportajes y análisis del panorama político esloveno.",https://siol.net/novice/politika/rss,11,Política,57,Eslovenia,sl,False,30 +5732,Slovenian Times – Politics,"Cobertura política en inglés: gobierno, relaciones exteriores, instituciones y actualidad legislativa.",https://sloveniatimes.com/category/politics/feed/,11,Política,57,Eslovenia,en,False,30 +5734,Total Slovenia News – Politics,Noticias políticas en inglés orientadas a expatriados y análisis gubernamentales.,https://www.total-slovenia-news.com/news/politics?format=feed&type=rss,11,Política,57,Eslovenia,en,True,0 +5704,24ur – Slovenija,"Noticias generales de Eslovenia: política, sociedad y actualidad.",https://www.24ur.com/bin/24ur/content/feed.xml,13,Sociedad,57,Eslovenia,sl,False,30 +5702,Delo – Novice,"Periódico nacional esloveno con noticias generales, política, sociedad y reportajes.",https://www.delo.si/rss,13,Sociedad,57,Eslovenia,sl,True,0 +5703,Dnevnik – Novice,"Noticias nacionales, análisis políticos y temas sociales publicados por Dnevnik.",https://www.dnevnik.si/rss,13,Sociedad,57,Eslovenia,sl,False,30 +5705,Siol – Novice,"Portal digital con noticias nacionales, economía, sociedad y reportajes.",https://siol.net/rss,13,Sociedad,57,Eslovenia,sl,False,30 +5713,Delo – Znanost & Tehnologija,Sección de ciencia y tecnología del periódico Delo con artículos sobre innovación e investigación.,https://www.delo.si/znanost/tehnologija/rss,14,Tecnología,57,Eslovenia,sl,False,30 +5714,Dnevnik – Znanost & Tehnologija,"Publicaciones tecnológicas y científicas de Dnevnik: innovación, investigación y tendencias digitales.",https://www.dnevnik.si/science/rss,14,Tecnología,57,Eslovenia,sl,False,30 +5711,Monitor.si – IT & Tech News,"Revista tecnológica eslovena con análisis de hardware, software, informática, seguridad y mercado TI.",https://www.monitor.si/rss,14,Tecnología,57,Eslovenia,sl,True,0 +5710,RTV Slovenija – Tehnologija,"Artículos sobre tecnología, ciberseguridad, innovación, startups y ciencia en la radiotelevisión pública eslovena.",https://www.rtvslo.si/tehnologija/rss,14,Tecnología,57,Eslovenia,sl,False,30 +5712,Racunalniske Novice – IT News,"Uno de los principales portales de informática en Eslovenia, especializado en tecnología, hardware, gadgets y ciberseguridad.",https://racunalniske-novice.com/feed/,14,Tecnología,57,Eslovenia,sl,True,0 +5709,Siol – Tehnologija,"Noticias tecnológicas, innovación, ciencia aplicada, móviles, internet y tendencias digitales en Eslovenia.",https://siol.net/novice/tehnologija/rss,14,Tecnología,57,Eslovenia,sl,False,30 +5805,20 Minutos – Política,"Actualidad política española, encuestas, elecciones y actividad institucional.",https://www.20minutos.es/rss/politica/,11,Política,58,España,es,False,30 +5803,ABC – Política,"Noticias políticas de ABC con foco en gobierno, instituciones, elecciones y tribunales.",https://www.abc.es/rss/feeds/abc_Politica.xml,11,Política,58,España,es,True,0 +5817,Ara.cat – Política,"Medio catalán con noticias de política catalana, española y europea.",https://www.ara.cat/rss/politica,11,Política,58,España,ca,True,0 +5826,Canarias7 – Política,"Actualidad política en Canarias: Parlamento Canario, Cabildos y partidos.",https://www.canarias7.es/rss/politica,11,Política,58,España,es,False,30 +5827,Diari de Tarragona – Política,"Política catalana, provincial y española desde perspectiva territorial.",https://www.diaridetarragona.com/rss/politica,11,Política,58,España,ca,False,30 +5835,Diario de Cádiz – Política,Política andaluza y española desde perspectiva gaditana.,https://www.diariodecadiz.es/rss/tag/politica,11,Política,58,España,es,False,30 +5832,Diario de Mallorca – Política,"Política balear, gobierno autonómico, partidos y actualidad nacional.",https://www.diariodemallorca.es/rss/section/politica,11,Política,58,España,es,False,30 +5824,El Comercio – Política Asturias,Actualidad política asturiana y española.,https://www.elcomercio.es/rss/2.728/politica,11,Política,58,España,es,False,30 +5806,El Confidencial – Política,Investigación y actualidad política nacional y económica con enfoque analítico.,https://www.elconfidencial.com/rss/politica/,11,Política,58,España,es,False,30 +5813,El Correo – Política País Vasco,"Noticias políticas regionales del País Vasco: gobierno vasco, partidos y actualidad institucional.",https://www.elcorreo.com/rss/2.728/portada/politica,11,Política,58,España,es,False,30 +5822,El Diario Montañés – Política Cantabria,"Noticias políticas de Cantabria: Parlamento regional, partidos y gobierno autonómico.",https://www.eldiariomontanes.es/rss/2.728/politica,11,Política,58,España,es,False,30 +5816,El Diario Vasco – Política,"Sección política del Diario Vasco: Euskadi, gobierno regional y política española.",https://www.diariovasco.com/rss/2.728/politica,11,Política,58,España,es,False,30 +5829,El Diario de Sevilla – Política,"Noticias políticas de Andalucía, Sevilla y política nacional.",https://www.diariodesevilla.es/rss/tag/politica,11,Política,58,España,es,False,30 +5828,El Día – Política Canarias,Noticias políticas de Canarias en clave regional y nacional.,https://www.eldia.es/rss/section/1264,11,Política,58,España,es,True,0 +5825,El Faro de Vigo – Política Galicia,Noticias políticas de Galicia y política nacional contextualizada.,https://www.farodevigo.es/rss/section/1264,11,Política,58,España,es,True,0 +5834,El Heraldo de Aragón – Política,"Información política sobre Aragón, gobierno autonómico y política nacional.",https://www.heraldo.es/rss/section/politica,11,Política,58,España,es,False,30 +5833,El Ideal – Política Andalucía,"Noticias políticas de Granada, Andalucía y España.",https://www.ideal.es/rss/section/politica,11,Política,58,España,es,False,30 +5802,El Mundo – España,"Información política nacional: gobierno, partidos, Congreso y actualidad legislativa.",https://e00-elmundo.uecdn.es/elmundo/rss/espana.xml,11,Política,58,España,es,True,0 +5818,El Nacional – Política,"Cobertura política catalana: Parlament, Generalitat, partidos y actualidad española desde visión catalana.",https://www.elnacional.cat/es/rss/politica,11,Política,58,España,es,False,30 +5831,El Norte de Castilla – Política,Noticias políticas de Castilla y León y política nacional.,https://www.elnortedecastilla.es/rss/section/politica,11,Política,58,España,es,False,30 +5801,El País – Política,"Sección política de El País: gobierno, partidos, parlamento y análisis nacional.",https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/section/politica/portada,11,Política,58,España,es,True,0 +5836,El Periodico de España – Política,"Actualidad política nacional con especial foco en análisis, gobierno y parlamento.",https://www.epe.es/es/rss/politica/rss.xml,11,Política,58,España,es,True,0 +5820,El Periódico de Aragón – Política,"Gobierno aragonés, Cortes, partidos y política nacional.",https://www.elperiodicodearagon.com/rss/politica/rss.xml,11,Política,58,España,es,False,30 +5811,El Periódico – Política,"Noticias políticas de Cataluña y España, con enfoque autonómico y estatal.",https://www.elperiodico.com/es/rss/politica/rss.xml,11,Política,58,España,es,True,0 +5807,Eldiario.es – Política,"Noticias políticas, Congreso, Gobierno, oposición y actualidad legislativa.",https://www.eldiario.es/rss/politica/,11,Política,58,España,es,True,0 +5809,Europa Press – Política,Servicio de noticias políticas de una de las principales agencias españolas.,https://www.europapress.es/rss/rss.aspx?ch=00066,11,Política,58,España,es,True,0 +5812,InfoLibre – Política,"Análisis político, actualidad parlamentaria y reportajes en profundidad.",https://www.infolibre.es/rss/politica/,11,Política,58,España,es,True,0 +5819,La Nueva España – Política Asturias,"Política regional asturiana, instituciones autonómicas y política española.",https://www.lne.es/rss/section/politica,11,Política,58,España,es,False,30 +5823,La Opinión de Murcia – Política,Política regional murciana y actualidad autonómica.,https://www.laopiniondemurcia.es/rss/section/1264,11,Política,58,España,es,True,0 +5837,La Provincia – Política Canarias,"Política regional en Canarias, cabildos y política estatal.",https://www.laprovincia.es/rss/section/politica,11,Política,58,España,es,False,30 +5814,La Razón – Política,"Cobertura política nacional, declaraciones, parlamento y actualidad del gobierno.",https://www.larazon.es/rss/politica.xml,11,Política,58,España,es,True,0 +5804,La Vanguardia – Política,Cobertura política nacional y autonómica con reportajes y análisis.,https://www.lavanguardia.com/mvc/feed/rss/politica,11,Política,58,España,es,False,30 +5830,La Verdad de Murcia – Política,Cobertura política regional murciana y española.,https://www.laverdad.es/rss/section/politica,11,Política,58,España,es,False,30 +5815,La Voz de Galicia – Política,"Política gallega y nacional, con foco en instituciones autonómicas y parlamento regional.",https://www.lavozdegalicia.es/rss/politica,11,Política,58,España,es,False,30 +5821,Levante-EMV – Política Comunidad Valenciana,"Política valenciana: Generalitat, Corts, partidos y política estatal.",https://www.levante-emv.com/rss/section/1264,11,Política,58,España,es,True,0 +5808,Público – Política,"Cobertura política, movimientos sociales y análisis contextual de la actualidad española.",https://www.publico.es/rss/politica/,11,Política,58,España,es,False,30 +5810,RTVE – Nacional,Información de política española desde el servicio público RTVE.,https://www.rtve.es/rss/nacional.xml,11,Política,58,España,es,False,30 +5838,Ultima Hora – Política Baleares,Noticias políticas de Baleares y política española.,https://www.ultimahora.es/rss/politica.xml,11,Política,58,España,es,False,30 +5868,ADSLZone,"Telecomunicaciones, fibra, telefonía, routers y velocidad de internet.",https://www.adslzone.net/feed/,14,Tecnología,58,España,es,True,0 +5862,Applesfera,"Noticias y análisis del ecosistema Apple: iPhone, Mac, iOS, hardware y software.",https://www.applesfera.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5884,BitLife Media – Tecnología,"Ciberseguridad, tecnología empresarial, IA, software y política digital.",https://bitlifemedia.com/feed/,14,Tecnología,58,España,es,True,0 +5876,CSIC – Noticias de Ciencia y Tecnología,"Divulgación científica, avances tecnológicos, investigaciones y descubrimientos en España.",https://www.csic.es/rss-csic-noticias,14,Tecnología,58,España,es,False,30 +5866,Clipset – Tecnología,"Gadgets, análisis, vídeo reviews y cultura geek.",https://clipset.com/feed/,14,Tecnología,58,España,es,True,0 +5867,ComputerHoy – Portada,"Tecnología generalista: análisis, noticias, móviles, informática y guías.",https://computerhoy.com/rss,14,Tecnología,58,España,es,True,0 +5895,Drone DJI España – Tecnología y Drones,"Novedades sobre drones, fotografía aérea, industria dron y actualizaciones tecnológicas.",https://www.djiarsmadrid.com/feed/,14,Tecnología,58,España,es,False,30 +5864,El Androide Libre,"Noticias de Android, móviles, apps y Google.",https://www.elespanol.com/elandroidelibre/feed/,14,Tecnología,58,España,es,False,30 +5880,El Español – Tecnología y Ciencia,"Innovación, investigación, IA, biotecnología y novedades tecnológicas.",https://www.elespanol.com/rss/tecnologia/,14,Tecnología,58,España,es,False,30 +5,El País – Tecnología,"Noticias de tecnología, digitalización, seguridad e innovación.",https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/section/tecnologia/portada,14,Tecnología,58,España,es,True,0 +5860,Genbeta,"Software, productividad, internet, redes sociales y servicios digitales.",https://www.genbeta.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5861,Genbeta Dev,"Desarrollo, programación, ingeniería de software y cultura dev.",https://www.genbetadev.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5870,HardZone,"Hardware, componentes, tarjetas gráficas, CPUs y PC gaming.",https://hardzone.es/feed/,14,Tecnología,58,España,es,True,0 +5894,Hipertextual – Ciencia y Tecnología Avanzada,"Ciencia, espacio, IA, innovación, gadgets y tecnología del futuro.",https://hipertextual.com/tag/tecnologia/feed,14,Tecnología,58,España,es,False,30 +5865,Hipertextual – Tecnología,"Tecnología, ciencia, internet, movilidad, inteligencia artificial.",https://hipertextual.com/feed,14,Tecnología,58,España,es,True,0 +5888,I+D El Mundo (Innovación y Desarrollo),"Investigación, desarrollo, ciencia, tecnología e innovación española.",https://e00-elmundo.uecdn.es/elmundo/rss/innovacion.xml,14,Tecnología,58,España,es,False,30 +5877,INCIBE – Ciberseguridad,"Alertas, informes, consejos y novedades de ciberseguridad del Instituto Nacional de Ciberseguridad.",https://www.incibe.es/feed,14,Tecnología,58,España,es,False,30 +5875,La Vanguardia – Tecnología,"Innovación, startups, móviles, redes, ciencia y tendencias tecnológicas.",https://www.lavanguardia.com/mvc/feed/rss/tecnologia,14,Tecnología,58,España,es,False,30 +5878,Maldita Tecnología,"Verificación, análisis tecnológico, redes sociales, algoritmos, privacidad y desmontando bulos tecnológicos.",https://maldita.es/malditatecnologia/rss/,14,Tecnología,58,España,es,False,30 +5869,MovilZona,"Noticias y análisis de móviles, hardware, software y operadores.",https://www.movilzona.es/feed/,14,Tecnología,58,España,es,True,0 +5892,Newtral Tecnología,"Tecnología, verificación de noticias digitales, redes sociales y análisis digital.",https://www.newtral.es/tecnologia/feed/,14,Tecnología,58,España,es,False,30 +5873,Nobbot – Tecnología,"Divulgación tecnológica, curiosidades digitales, innovación y vida conectada.",https://www.nobbot.com/feed/,14,Tecnología,58,España,es,False,30 +5863,Omicrono – El Español,"Tecnología, ciencia, innovación, espacio y ciberseguridad.",https://www.elespanol.com/elandroidelibre/feeds/omicrono.xml,14,Tecnología,58,España,es,False,30 +5893,Omicrono – Innovación y Tecnología,"Tecnología avanzada, ciencia, curiosidades digitales, IA y dispositivos innovadores.",https://omicrono.elconfidencial.com/rss/,14,Tecnología,58,España,es,False,30 +5871,Profesional Review – Hardware,"Componentes de PC, reviews, overclock, gaming y hardware técnico.",https://www.profesionalreview.com/feed/,14,Tecnología,58,España,es,True,0 +5883,RedesZone – Redes y Ciberseguridad,"Routers, WiFi, seguridad en redes, ciberseguridad y comunicaciones.",https://www.redeszone.net/feed/,14,Tecnología,58,España,es,True,0 +5885,Revista BYTE TI,"Tecnología profesional, digitalización, cloud, software empresarial e innovación IT.",https://revistabyte.es/feed/,14,Tecnología,58,España,es,True,0 +5891,SINC – Agencia de Noticias Científicas,"Ciencia, tecnología, espacio, biomedicina e innovación en España.",https://www.agenciasinc.es/rss,14,Tecnología,58,España,es,False,30 +5882,Silicon.es – Tecnología Profesional,"Medio especializado en tecnología empresarial, cloud, IA, data centers y soluciones IT.",https://www.silicon.es/feed,14,Tecnología,58,España,es,False,30 +5886,Think Big – Innovación (Telefónica),"Innovación digital, IA, ciencia, tecnología social y futuro digital.",https://thinkbig.telefonica.com/feed,14,Tecnología,58,España,es,False,30 +5881,TicBeat – Tecnología,"Tecnología empresarial, startups, innovación digital y economía tecnológica.",https://www.ticbeat.com/feed/,14,Tecnología,58,España,es,False,30 +5872,TreceBits – Redes Sociales y Tecnología,"Redes sociales, social media, aplicaciones, plataformas y tendencias digitales.",https://www.trecebits.com/feed/,14,Tecnología,58,España,es,True,0 +5887,UNIR – Tecnología y Ciencia,"Artículos de divulgación tecnológica, IA, programación, informática y tendencias digitales.",https://www.unir.net/ingenieria-tecnologia/rss/,14,Tecnología,58,España,es,False,30 +5889,Universidad Politécnica de Madrid – Innovación,"Noticias de investigación, ingeniería, robótica, IA y desarrollo tecnológico.",https://www.upm.es/e-politecnica/feed,14,Tecnología,58,España,es,True,0 +5890,Universidad de Barcelona – Investigación y Ciencia,"Publicaciones sobre estudios científicos, tecnología y avances de investigación.",https://www.ub.edu/web/ub/ca/rss/rss.xml,14,Tecnología,58,España,es,False,30 +6,Xataka - Tecnología,"Noticias y análisis de tecnología, gadgets e innovación de Xataka.",https://www.xataka.com/tag/xataka/rss2.xml,14,Tecnología,58,España,es,True,0 +5855,Xataka Android,"Noticias y análisis de Android, móviles, apps y Google.",https://www.xatakandroid.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5859,Xataka Ciencia,"Divulgación científica, física, biología, espacio e innovación.",https://www.xatakaciencia.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5857,Xataka Foto,"Fotografía digital, cámaras, edición y cultura visual.",https://www.xatakafoto.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5879,Xataka IoT,"Noticias sobre internet de las cosas, automatización, sensores y sistemas conectados.",https://feeds.weblogssl.com/xatakaxatakaiot,14,Tecnología,58,España,es,False,30 +5856,Xataka Móvil,"Telefonía, tarifas, 5G, redes y operadores móviles en España.",https://www.xatakamovil.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +5858,Xataka Smart Home,"Domótica, IoT, hogar digital, dispositivos conectados.",https://www.xatakasmarthome.com/feedburner.xml,14,Tecnología,58,España,es,False,30 +5854,Xataka – Tecnología,"Noticias tecnológicas, análisis, ciencia, movilidad, innovación y cultura digital.",https://www.xataka.com/feedburner.xml,14,Tecnología,58,España,es,True,0 +1313,AAAS EurekAlert,Science press releases and research news from US institutions.,https://www.eurekalert.org/rss.xml,1,Ciencia,59,Estados Unidos,en,False,30 +1315,Harvard Science News,Scientific research and discoveries from Harvard University.,https://news.harvard.edu/feed/,1,Ciencia,59,Estados Unidos,en,True,0 +1318,Johns Hopkins Science News,Medical and scientific research news from Johns Hopkins.,https://hub.jhu.edu/rss/,1,Ciencia,59,Estados Unidos,en,True,0 +1319,Lawrence Berkeley Lab News,Energy physics and science research news from Berkeley Lab.,https://newscenter.lbl.gov/feed/,1,Ciencia,59,Estados Unidos,en,True,0 +1307,MIT News Research,Research and science news from MIT.,https://news.mit.edu/rss/research,1,Ciencia,59,Estados Unidos,en,True,0 +1314,NOAA Science,Climate ocean and atmospheric science news from NOAA.,https://www.noaa.gov/rss.xml,1,Ciencia,59,Estados Unidos,en,True,0 +1306,NPR Science,Science research health and discovery news.,https://feeds.npr.org/1007/rss.xml,1,Ciencia,59,Estados Unidos,en,True,0 +1310,National Institutes of Health News,Medical and scientific research news from NIH.,https://www.nih.gov/news-events/news-releases/rss.xml,1,Ciencia,59,Estados Unidos,en,False,30 +1320,Oak Ridge National Laboratory News,Science and technology research from ORNL.,https://www.ornl.gov/rss.xml,1,Ciencia,59,Estados Unidos,en,True,0 +1312,Phys.org Science,Science and technology research news with US focus.,https://phys.org/rss-feed/,1,Ciencia,59,Estados Unidos,en,True,0 +1304,Popular Science News,Science innovation and technology explained.,https://www.popsci.com/feed/,1,Ciencia,59,Estados Unidos,en,True,0 +1317,Princeton Research News,Scientific research and innovation from Princeton University.,https://www.princeton.edu/news/feed.xml,1,Ciencia,59,Estados Unidos,en,False,30 +1305,Smithsonian Science and Nature,Science and nature articles from Smithsonian Magazine.,https://www.smithsonianmag.com/rss/science-nature/,1,Ciencia,59,Estados Unidos,en,True,0 +1311,Space.com Science,Space science astronomy and research news from the US.,https://www.space.com/feeds/all,1,Ciencia,59,Estados Unidos,en,True,0 +1308,Stanford News Research,University research and science discoveries.,https://news.stanford.edu/feed/,1,Ciencia,59,Estados Unidos,en,False,30 +1309,US Geological Survey News,Geology earthquakes and earth science news.,https://www.usgs.gov/rss.xml,1,Ciencia,59,Estados Unidos,en,False,30 +1316,Yale Science News,Science research and academic discoveries from Yale University.,https://news.yale.edu/rss,1,Ciencia,59,Estados Unidos,en,False,30 +1113,Artforum News,Noticias y actualidad del arte contemporaneo internacional.,https://www.artforum.com/rss/news.xml,2,Cultura,59,Estados Unidos,en,False,30 +1111,Hollywood Reporter Culture,Cobertura cultural de cine television y la industria creativa.,https://www.hollywoodreporter.com/culture/feed/,2,Cultura,59,Estados Unidos,en,True,0 +1104,Los Angeles Times Arts and Culture,Cobertura cultural cine arte musica y vida cultural estadounidense.,https://www.latimes.com/entertainment-arts/rss2.0.xml,2,Cultura,59,Estados Unidos,en,True,0 +1106,NPR Arts and Culture,Noticias culturales arte musica libros y sociedad desde NPR.,https://feeds.npr.org/1008/rss.xml,2,Cultura,59,Estados Unidos,en,True,0 +1112,National Geographic Culture,Reportajes culturales historia arte y sociedades humanas.,https://www.nationalgeographic.com/content/natgeo/en_us/rss/culture.xml,2,Cultura,59,Estados Unidos,en,False,30 +1101,New York Times Arts,Noticias y reportajes sobre arte cultura libros teatro y exposiciones en Estados Unidos.,https://rss.nytimes.com/services/xml/rss/nyt/Arts.xml,2,Cultura,59,Estados Unidos,en,True,0 +1102,New York Times Books,Cobertura cultural centrada en libros autores y mundo editorial.,https://rss.nytimes.com/services/xml/rss/nyt/Books.xml,2,Cultura,59,Estados Unidos,en,True,0 +1114,Pitchfork News,Cobertura cultural y musical critica y tendencias sonoras.,https://pitchfork.com/rss/news/,2,Cultura,59,Estados Unidos,en,True,0 +1115,Poetry Foundation News,Noticias y contenidos culturales sobre poesia y literatura.,https://www.poetryfoundation.org/rss/articles,2,Cultura,59,Estados Unidos,en,False,30 +1109,Rolling Stone Culture,Cobertura cultural musica cine television y cultura popular.,https://www.rollingstone.com/culture/feed/,2,Cultura,59,Estados Unidos,en,True,0 +1105,Smithsonian Magazine Arts,Cultura historia arte y patrimonio desde el Smithsonian.,https://www.smithsonianmag.com/rss/arts-culture/,2,Cultura,59,Estados Unidos,en,True,0 +1107,The Atlantic Culture,Analisis cultural literatura ideas y sociedad en Estados Unidos.,https://www.theatlantic.com/feed/channel/entertainment/,2,Cultura,59,Estados Unidos,en,False,30 +1108,Time Magazine Culture,Noticias y analisis cultural cine musica libros y tendencias.,https://time.com/arts/feed/,2,Cultura,59,Estados Unidos,en,True,0 +1110,Variety Culture,Noticias de cultura audiovisual cine television y entretenimiento.,https://variety.com/c/feed/,2,Cultura,59,Estados Unidos,en,False,30 +1103,Washington Post Arts,Noticias culturales arte musica y eventos culturales en Estados Unidos.,https://feeds.washingtonpost.com/rss/arts-and-entertainment,2,Cultura,59,Estados Unidos,en,False,30 +1411,Bleacher Report Sports,Trending US sports news and opinions.,https://bleacherreport.com/articles/feed,3,Deportes,59,Estados Unidos,en,False,30 +1406,CBS Sports News,US sports news scores and league coverage.,https://www.cbssports.com/rss/headlines/,3,Deportes,59,Estados Unidos,en,True,0 +1419,College Basketball News,NCAA college basketball news and scores.,https://www.espn.com/espn/rss/ncb/news,3,Deportes,59,Estados Unidos,en,True,0 +1418,College Football News,NCAA college football news and analysis.,https://www.espn.com/espn/rss/ncf/news,3,Deportes,59,Estados Unidos,en,True,0 +1404,ESPN MLB News,Major League Baseball news and results.,https://www.espn.com/espn/rss/mlb/news,3,Deportes,59,Estados Unidos,en,True,0 +1403,ESPN NBA News,NBA basketball news scores and analysis.,https://www.espn.com/espn/rss/nba/news,3,Deportes,59,Estados Unidos,en,True,0 +1402,ESPN NFL News,NFL news games analysis and league updates.,https://www.espn.com/espn/rss/nfl/news,3,Deportes,59,Estados Unidos,en,True,0 +1405,ESPN NHL News,NHL hockey news scores and standings.,https://www.espn.com/espn/rss/nhl/news,3,Deportes,59,Estados Unidos,en,True,0 +4002,ESPN Top Sports,Top sports news and results from the United States.,https://www.espn.com/espn/rss/news,3,Deportes,59,Estados Unidos,en,True,0 +1408,FOX Sports News,US sports headlines and league analysis.,https://www.foxsports.com/rss,3,Deportes,59,Estados Unidos,en,False,30 +1415,MLB Official News,Official Major League Baseball news.,https://www.mlb.com/rss/news,3,Deportes,59,Estados Unidos,en,False,30 +1417,MLS Soccer News,Major League Soccer news and US soccer coverage.,https://www.mlssoccer.com/rss/news,3,Deportes,59,Estados Unidos,en,False,30 +1420,MotorSport US News,Auto racing and motorsport news from US events.,https://www.motorsport.com/rss/news/,3,Deportes,59,Estados Unidos,en,False,30 +1414,NBA Official News,Official NBA league news and announcements.,https://www.nba.com/rss/nba_rss.xml,3,Deportes,59,Estados Unidos,en,False,30 +1407,NBC Sports News,Sports news and live coverage across US leagues.,https://www.nbcsports.com/rss/news,3,Deportes,59,Estados Unidos,en,False,30 +1413,NFL Official News,Official National Football League news and updates.,https://www.nfl.com/rss/rsslanding?searchString=home,3,Deportes,59,Estados Unidos,en,False,30 +1416,NHL Official News,Official National Hockey League news.,https://www.nhl.com/rss/news.xml,3,Deportes,59,Estados Unidos,en,False,30 +1412,Sports Illustrated News,In depth sports journalism and US league coverage.,https://www.si.com/rss/si_topstories.xml,3,Deportes,59,Estados Unidos,en,False,30 +1410,USA Today Sports,National sports coverage and major league news.,https://rssfeeds.usatoday.com/usatodaysports,3,Deportes,59,Estados Unidos,en,False,30 +1409,Yahoo Sports News,Comprehensive US sports news and commentary.,https://sports.yahoo.com/rss/,3,Deportes,59,Estados Unidos,en,True,0 +1013,ABC News Politics,Titulares politicos nacionales y cobertura del gobierno federal.,https://feeds.abcnews.com/abcnews/politicsheadlines,11,Política,59,Estados Unidos,en,True,0 +1016,AP News Politics,Noticias politicas nacionales y del gobierno federal de Estados Unidos.,[https://apnews.com/hub/politics?output=rss,11,Política,59,Estados Unidos,11,False,59 +1020,Axios Politics,Analisis politico conciso sobre el gobierno y el poder en Estados Unidos.,[https://www.axios.com/feeds/politics.xml,11,Política,59,Estados Unidos,11,False,59 +1021,Bloomberg Politics,Cobertura politica y de gobierno de Estados Unidos con enfoque economico.,https://www.bloomberg.com/feed/politics,11,Política,59,Estados Unidos,en,False,30 +1014,CBS News Politics,Noticias politicas y analisis del gobierno estadounidense.,https://www.cbsnews.com/latest/rss/politics,11,Política,59,Estados Unidos,en,True,0 +1023,Fox News Politics,Cobertura politica y elecciones en Estados Unidos.,https://moxie.foxnews.com/google-publisher/politics.xml,11,Política,59,Estados Unidos,en,True,0 +1018,Los Angeles Times Politics,Noticias politicas nacionales y estatales desde California.,[https://www.latimes.com/politics/rss2.0.xml,11,Política,59,Estados Unidos,11,False,59 +1022,NBC News Politics,Noticias politicas nacionales y del gobierno federal de Estados Unidos.,https://feeds.nbcnews.com/nbcnews/public/politics,11,Política,59,Estados Unidos,en,True,0 +1006,NPR Politics,Cobertura politica y elecciones en Estados Unidos.,https://feeds.npr.org/1014/rss.xml,11,Política,59,Estados Unidos,en,True,0 +1004,Politico US News,Noticias y analisis politico centrado en Washington.,https://www.politico.com/rss/politicopicks.xml,11,Política,59,Estados Unidos,en,False,30 +1010,Politico US Politics,Analisis politico especializado en Estados Unidos y Washington.,https://www.politico.com/rss/politics08.xml,11,Política,59,Estados Unidos,en,False,30 +1025,ProPublica Politics,Investigaciones periodisticas sobre politica y poder en Estados Unidos.,https://www.propublica.org/feeds/politics.rss,11,Política,59,Estados Unidos,en,False,30 +1015,Reuters US Politics,Cobertura politica de Estados Unidos desde una perspectiva global y objetiva.,[https://www.reuters.com/world/us/politics/rss,11,Política,59,Estados Unidos,11,False,59 +1005,The Hill Politics,Actualidad politica del Congreso y la Casa Blanca.,https://thehill.com/rss/syndicator/19109,11,Política,59,Estados Unidos,en,True,0 +1017,USA Today Politics,Actualidad politica estadounidense y cobertura electoral.,[https://rssfeeds.usatoday.com/UsatodaycomPolitics,11,Política,59,Estados Unidos,11,False,59 +1024,Vox Politics,Analisis y explicacion de la politica estadounidense contemporanea.,https://www.vox.com/rss/politics/index.xml,11,Política,59,Estados Unidos,en,True,0 +1019,Wall Street Journal Politics,Informacion politica y economica con enfoque institucional.,[https://feeds.a.dj.com/rss/RSSPolitics.xml,11,Política,59,Estados Unidos,11,False,59 +3146,Android Authority,Android news mobile technology and reviews.,https://www.androidauthority.com/feed/,14,Tecnología,59,Estados Unidos,en,True,0 +1520,AppleInsider,Apple technology news hardware and software.,https://appleinsider.com/rss/news/,14,Tecnología,59,Estados Unidos,en,True,0 +1504,Ars Technica,Technology analysis software hardware and security news.,https://feeds.arstechnica.com/arstechnica/index,14,Tecnología,59,Estados Unidos,en,True,0 +1505,CNET Technology,Consumer technology reviews news and buying guides.,https://www.cnet.com/rss/all/,14,Tecnología,59,Estados Unidos,en,True,0 +1518,Digital Trends,Technology lifestyle gadgets and consumer tech.,https://www.digitaltrends.com/feed/,14,Tecnología,59,Estados Unidos,en,True,0 +1506,Engadget,Technology gadgets reviews and industry news.,https://www.engadget.com/rss.xml,14,Tecnología,59,Estados Unidos,en,True,0 +1511,Gizmodo,Consumer technology science and digital culture news.,https://gizmodo.com/rss,14,Tecnología,59,Estados Unidos,en,True,0 +1515,Hacker News,Technology and startup discussions from Y Combinator.,https://news.ycombinator.com/rss,14,Tecnología,59,Estados Unidos,en,True,0 +1509,InfoWorld,Software development cloud and enterprise technology.,https://www.infoworld.com/index.rss,14,Tecnología,59,Estados Unidos,en,False,30 +1512,Mashable Tech,Technology trends social media and digital innovation.,https://mashable.com/feeds/rss/tech,14,Tecnología,59,Estados Unidos,en,True,0 +1517,PCMag Technology,Product reviews cybersecurity and software news.,https://www.pcmag.com/feeds/rss,14,Tecnología,59,Estados Unidos,en,False,30 +1513,Recode Technology,Technology industry and Silicon Valley analysis.,https://www.vox.com/rss/recode/index.xml,14,Tecnología,59,Estados Unidos,en,True,0 +1514,Slashdot,Technology news discussion and open source topics.,https://rss.slashdot.org/Slashdot/slashdotMain,14,Tecnología,59,Estados Unidos,en,True,0 +1516,Tomshardware,Computer hardware reviews benchmarks and analysis.,https://www.tomshardware.com/feeds/all,14,Tecnología,59,Estados Unidos,en,True,0 +1510,VentureBeat,Technology business artificial intelligence and gaming news.,https://venturebeat.com/feed/,14,Tecnología,59,Estados Unidos,en,True,0 +1508,ZDNet Technology,Enterprise technology IT and business tech news.,https://www.zdnet.com/news/rss.xml,14,Tecnología,59,Estados Unidos,en,True,0 +1619,ERR Science News,Science research and innovation news from Estonia.,https://news.err.ee/rss?cat=science,1,Ciencia,60,Estonia,en,True,0 +1620,ERR Teadus,Eesti teadusuudised teadus ja uurimistoo.,https://www.err.ee/rss?cat=teadus,1,Ciencia,60,Estonia,et,True,0 +1624,Estonian Academy of Sciences News,Scientific institutions research and academic news.,https://www.akadeemia.ee/en/rss,1,Ciencia,60,Estonia,en,True,0 +1625,Estonian Genome Centre News,Genomics biomedical research and population studies.,https://genomics.ut.ee/en/feed/,1,Ciencia,60,Estonia,en,False,30 +1623,Estonian Research Council News,National science research programs and funding news.,https://etag.ee/en/feed/,1,Ciencia,60,Estonia,en,True,0 +1626,Estonian Space Office News,Space science technology and research programs.,https://www.spaceoffice.ee/feed/,1,Ciencia,60,Estonia,en,False,30 +1627,European Bioinformatics Institute Estonia,Bioinformatics and life science research collaboration.,https://www.ebi.ac.uk/about/news/feed,1,Ciencia,60,Estonia,en,False,30 +1621,University of Tartu Science News,Scientific research discoveries from University of Tartu.,https://ut.ee/en/rss,1,Ciencia,60,Estonia,en,False,30 +1605,Baltic Times Economy,Economic and business news from Estonia and Baltic region.,https://www.baltictimes.com/rss.xml,4,Economía,60,Estonia,en,False,30 +1616,Central Bank of Estonia News,Monetary policy and economic analysis from Eesti Pank.,https://www.eestipank.ee/rss.xml,4,Economía,60,Estonia,en,False,30 +1611,Delfi Economy,Estonian economy business and finance news.,https://www.delfi.ee/rss?channel=business,4,Economía,60,Estonia,et,False,30 +1610,ERR Majandus,Eesti majandusuudised ettevõtlus ja rahandus.,https://www.err.ee/rss?cat=majandus,4,Economía,60,Estonia,et,True,0 +1609,ERR News Economy,Estonia economy business and government economic policy news.,https://news.err.ee/rss?cat=business,4,Economía,60,Estonia,en,True,0 +1614,Invest in Estonia News,Official news on investment and economic development.,https://investinestonia.com/feed/,4,Economía,60,Estonia,en,True,0 +1617,Ministry of Finance Estonia,Public finance budget and economic policy updates.,https://www.fin.ee/rss,4,Economía,60,Estonia,en,False,30 +1612,Postimees Economy,Estonia economy markets companies and finance.,https://majandus.postimees.ee/rss,4,Economía,60,Estonia,et,True,0 +1615,Statistics Estonia News,Official statistics and economic indicators from Estonia.,https://www.stat.ee/en/rss,4,Economía,60,Estonia,en,True,0 +1603,Delfi Estonia Politics,Political news and analysis from Estonia.,https://www.delfi.ee/rss,11,Política,60,Estonia,et,False,30 +1601,ERR News Politics,Estonia public broadcaster political news and government coverage.,https://news.err.ee/rss,11,Política,60,Estonia,en,True,0 +1602,ERR Uudised Politics,Eesti poliitilised uudised ja valitsuse kajastus.,https://www.err.ee/rss,11,Política,60,Estonia,et,True,0 +1606,Government of Estonia News,Official government announcements and political updates.,https://www.valitsus.ee/en/rss,11,Política,60,Estonia,en,False,30 +1604,Postimees Politics,Estonian political news parliament and elections.,https://www.postimees.ee/rss,11,Política,60,Estonia,et,True,0 +1608,President of Estonia News,Official statements and political activity of the president.,https://president.ee/en/feed/,11,Política,60,Estonia,en,False,30 +1607,Riigikogu News,Parliament news laws and political activity.,https://www.riigikogu.ee/en/rss/,11,Política,60,Estonia,en,True,0 +1631,Delfi Technology,Technology innovation startups and IT news from Estonia.,https://www.delfi.ee/rss?channel=technology,14,Tecnología,60,Estonia,et,False,30 +1628,ERR Technology News,Technology digital government and innovation news from Estonia.,https://news.err.ee/rss?cat=technology,14,Tecnología,60,Estonia,en,True,0 +1629,ERR Tehnoloogia,Eesti tehnoloogiauudised digiriik ja IT.,https://www.err.ee/rss?cat=tehnoloogia,14,Tecnología,60,Estonia,et,True,0 +1636,Garage48 News,Technology events hackathons and startup innovation.,https://garage48.org/feed/,14,Tecnología,60,Estonia,en,False,30 +1634,Information System Authority News,Cybersecurity digital infrastructure and IT governance.,https://www.ria.ee/en/rss,14,Tecnología,60,Estonia,en,False,30 +1637,Latitude59 News,Technology conference startups and digital innovation.,https://latitude59.ee/feed/,14,Tecnología,60,Estonia,en,True,0 +1630,Postimees Technology,Estonia technology IT startups and digital society news.,https://tehnoloogia.postimees.ee/rss,14,Tecnología,60,Estonia,et,False,30 +1618,Startup Estonia Technology,Startup ecosystem technology and innovation news.,https://startupestonia.ee/feed/,14,Tecnología,60,Estonia,en,True,0 +1622,TalTech Technology News,Engineering IT and applied technology research.,https://taltech.ee/en/rss,14,Tecnología,60,Estonia,en,True,0 +1633,e-Estonia News,Digital government e-residency and e-services news.,https://e-estonia.com/feed/,14,Tecnología,60,Estonia,en,True,0 +1725,World Health Organization Eswatini,Scientific and public health reports related to Eswatini.,https://www.who.int/feeds/entity/countries/swz/en/rss.xml,1,Ciencia,61,Esuatini,en,False,30 +1742,Swazi Observer Sports,Sports coverage teams leagues and events.,https://www.observer.org.sz/rss/sports,3,Deportes,61,Esuatini,en,False,30 +1741,Times of Eswatini Sports,Sports football athletics and national competitions.,https://www.times.co.sz/rss/sports,3,Deportes,61,Esuatini,en,False,30 +1713,AllAfrica Eswatini Business,Business and economic news from Eswatini.,https://allafrica.com/tools/headlines/rdf/swaziland/business.rdf,4,Economía,61,Esuatini,en,False,30 +1710,Central Bank of Eswatini News,Monetary policy banking and financial stability.,https://www.centralbank.org.sz/rss.xml,4,Economía,61,Esuatini,en,False,30 +1712,Eswatini Investment Promotion Authority,Investment trade and economic development news.,https://www.sipa.org.sz/feed/,4,Economía,61,Esuatini,en,False,30 +1709,Independent News Eswatini Economy,Economic analysis public finance and business news.,https://independentnews.co.sz/category/business/feed/,4,Economía,61,Esuatini,en,True,0 +1711,Ministry of Finance Eswatini,Public finance budget and economic policy announcements.,https://www.gov.sz/index.php/component/ninjarsssyndicator/?feed_id=5,4,Economía,61,Esuatini,en,False,30 +1708,Swazi Observer Business,Business economy finance and trade.,https://www.observer.org.sz/rss/business,4,Economía,61,Esuatini,en,False,30 +1707,Times of Eswatini Business,Economy finance markets and companies.,https://www.times.co.sz/rss/business,4,Economía,61,Esuatini,en,False,30 +1747,AllAfrica Eswatini Education,Education policy schools and universities.,https://allafrica.com/tools/headlines/rdf/swaziland/education.rdf,5,Educación,61,Esuatini,en,False,30 +1722,University of Eswatini News,Education research science and academic life.,https://www.uneswa.ac.sz/feed/,5,Educación,61,Esuatini,en,False,30 +1706,AllAfrica Eswatini Headlines,Aggregated news politics economy society and culture.,https://allafrica.com/tools/headlines/rdf/swaziland/headlines.rdf,7,Internacional,61,Esuatini,en,False,30 +1703,Swazi Observer News,National news society politics and economy.,https://www.observer.org.sz/rss/news,7,Internacional,61,Esuatini,en,False,30 +1702,Times of Eswatini News,General national news politics economy and society.,https://www.times.co.sz/rss/news,7,Internacional,61,Esuatini,en,False,30 +1748,AllAfrica Eswatini Environment,Environment climate and conservation topics.,https://allafrica.com/tools/headlines/rdf/swaziland/environment.rdf,8,Medio Ambiente,61,Esuatini,en,False,30 +1724,Eswatini Environment Authority News,Environment conservation climate and sustainability.,https://www.sepa.org.sz/feed/,8,Medio Ambiente,61,Esuatini,en,False,30 +1701,Government of Eswatini News,Official government policy and announcements.,https://www.gov.sz/index.php/component/ninjarsssyndicator/?feed_id=1,11,Política,61,Esuatini,en,False,30 +1726,AllAfrica Eswatini Health,Health science and social development news.,https://allafrica.com/tools/headlines/rdf/swaziland/health.rdf,12,Salud,61,Esuatini,en,False,30 +1723,Eswatini Ministry of Health News,Health policy public health and research updates.,https://www.gov.sz/index.php/component/ninjarsssyndicator/?feed_id=6,12,Salud,61,Esuatini,en,False,30 +1705,Eswatini Broadcasting Corporation News,Public broadcaster news culture and national affairs.,https://www.ebc.co.sz/rss,13,Sociedad,61,Esuatini,en,False,30 +1704,Independent News Eswatini,Independent reporting on politics economy and society.,https://independentnews.co.sz/feed/,13,Sociedad,61,Esuatini,en,True,0 +1743,Eswatini Tourism News,Travel tourism culture and national heritage.,https://www.thekingdomofeswatini.com/feed/,15,Viajes,61,Esuatini,en,True,0 +1833,AllAfrica Ethiopia Science,Science health environment and research news aggregation.,https://allafrica.com/tools/headlines/rdf/ethiopia/health.rdf,1,Ciencia,62,Etiopía,en,False,30 +1830,Ethiopian Ministry of Science and Technology,National science technology and innovation policy news.,https://www.mint.gov.et/rss.xml,1,Ciencia,62,Etiopía,en,False,30 +1829,Ethiopian Public Health Institute,Public health research science and epidemiology updates.,https://ephi.gov.et/feed/,1,Ciencia,62,Etiopía,en,True,0 +1831,Ethiopian Space Science Society,Astronomy space science and research outreach.,https://www.esss.org.et/feed/,1,Ciencia,62,Etiopía,en,False,30 +1834,UNICEF Ethiopia Research,Research science health and development programs.,https://www.unicef.org/ethiopia/rss.xml,1,Ciencia,62,Etiopía,en,False,30 +1828,University of Addis Ababa News,Academic research science and higher education news.,https://www.aau.edu.et/rss.xml,1,Ciencia,62,Etiopía,en,False,30 +1832,World Health Organization Ethiopia,Scientific health reports and research related to Ethiopia.,https://www.who.int/feeds/entity/countries/eth/en/rss.xml,1,Ciencia,62,Etiopía,en,False,30 +1816,Addis Fortune Economy,Business economy markets and corporate news from Ethiopia.,https://addisfortune.news/feed/,4,Economía,62,Etiopía,en,False,30 +1823,African Development Bank Ethiopia,Economic development projects and financing news.,https://www.afdb.org/en/countries/east-africa/ethiopia/rss.xml,4,Economía,62,Etiopía,en,False,30 +1824,AllAfrica Ethiopia Business,Aggregated business and economic news from Ethiopia.,https://allafrica.com/tools/headlines/rdf/ethiopia/business.rdf,4,Economía,62,Etiopía,en,False,30 +1815,Capital Ethiopia Economy,Ethiopian economy business finance and market analysis.,https://www.capitalethiopia.com/feed/,4,Economía,62,Etiopía,en,True,0 +1818,Ethiopian Herald Economy,State newspaper economic policy and development coverage.,https://press.et/herald/?feed=rss2,4,Economía,62,Etiopía,en,False,30 +1821,Ethiopian Investment Commission News,Investment trade and economic development news.,https://www.investethiopia.gov.et/rss.xml,4,Economía,62,Etiopía,en,False,30 +1814,Ethiopian News Agency Economy,Official economic policy development and government economy news.,https://www.ena.et/en/rss,4,Economía,62,Etiopía,en,False,30 +1820,Ministry of Finance Ethiopia,Public finance budget taxation and economic policy.,https://www.mofed.gov.et/rss.xml,4,Economía,62,Etiopía,en,False,30 +1819,National Bank of Ethiopia News,Monetary policy banking and financial stability updates.,https://www.nbe.gov.et/rss.xml,4,Economía,62,Etiopía,en,False,30 +1817,Reporter Ethiopia Business,Ethiopian economy public finance and business reporting.,https://www.thereporterethiopia.com/feed/,4,Economía,62,Etiopía,en,True,0 +1822,World Bank Ethiopia Economy,Development finance projects and economic reports.,https://www.worldbank.org/en/country/ethiopia/rss.xml,4,Economía,62,Etiopía,en,False,30 +1802,Addis Standard Politics,Independent political analysis and governance reporting in Ethiopia.,https://addisstandard.com/feed/,11,Política,62,Etiopía,en,False,30 +1810,AllAfrica Ethiopia Politics,Aggregated political news and regional context.,https://allafrica.com/tools/headlines/rdf/ethiopia/headlines.rdf,11,Política,62,Etiopía,en,True,0 +1811,Borkena Politics,Ethiopian political opinion news and analysis.,https://borkena.com/feed/,11,Política,62,Etiopía,en,False,30 +1812,Ethiopian Insight Politics,In depth political analysis and governance issues.,https://ethiopianinsight.com/feed/,11,Política,62,Etiopía,en,False,30 +1803,Fana Broadcasting Politics,Political news and government affairs coverage.,https://www.fanabc.com/english/feed/,11,Política,62,Etiopía,en,True,0 +1809,House of Peoples Representatives News,Parliamentary activity laws and political sessions.,https://www.hopr.gov.et/rss.xml,11,Política,62,Etiopía,en,False,30 +1808,Ministry of Foreign Affairs Ethiopia,Foreign policy diplomacy and official statements.,https://mfa.gov.et/rss.xml,11,Política,62,Etiopía,en,False,30 +1807,Prime Minister Office News,Official news and political announcements from the Prime Minister office.,https://www.pm.gov.et/rss.xml,11,Política,62,Etiopía,en,False,30 +1813,ZeHabesha Politics,Political commentary and Ethiopian current affairs.,https://zehabesha.com/feed/,11,Política,62,Etiopía,en,True,0 +1844,AllAfrica Ethiopia Technology,Aggregated technology and digital economy news.,https://allafrica.com/tools/headlines/rdf/ethiopia/ict.rdf,14,Tecnología,62,Etiopía,en,False,30 +1843,Ethio ICT News,Information technology software and digital transformation.,https://ethiopiantelecomnews.com/feed/,14,Tecnología,62,Etiopía,en,False,30 +1839,Ethio Telecom News,Telecommunications technology and digital infrastructure updates.,https://www.ethiotelecom.et/feed/,14,Tecnología,62,Etiopía,en,True,0 +1841,IceAddis Innovation Hub News,Startup ecosystem technology and innovation news.,https://iceaddis.com/feed/,14,Tecnología,62,Etiopía,en,True,0 +1840,Information Network Security Administration,ICT policy cybersecurity and digital governance news.,https://www.insa.gov.et/rss.xml,14,Tecnología,62,Etiopía,en,False,30 +1842,Sheba Valley Tech Hub,Technology startups digital economy and innovation.,https://sheba.valley/feed/,14,Tecnología,62,Etiopía,en,False,30 +1935,AllAfrica Philippines Science,Science health and environment news aggregation.,https://allafrica.com/tools/headlines/rdf/philippines/health.rdf,1,Ciencia,63,Filipinas,en,False,30 +5924,Department of Science and Technology Philippines,Official science technology and innovation updates.,https://www.dost.gov.ph/rss.xml,1,Ciencia,63,Filipinas,en,False,30 +5926,Philippine Atmospheric Geophysical Agency,Weather climate earthquakes and earth science updates.,https://www.pagasa.dost.gov.ph/rss,1,Ciencia,63,Filipinas,en,False,30 +5925,Philippine Council for Health Research,Health research science and innovation news.,https://www.pchrd.dost.gov.ph/feed/,1,Ciencia,63,Filipinas,en,False,30 +1932,Philippine Institute of Volcanology and Seismology,Volcano earthquake and geoscience research news.,https://www.phivolcs.dost.gov.ph/rss.xml,1,Ciencia,63,Filipinas,en,False,30 +5927,University of the Philippines Science News,Academic research science and higher education updates.,https://www.up.edu.ph/feed/,1,Ciencia,63,Filipinas,en,False,30 +5928,World Health Organization Philippines,Scientific public health reports related to Philippines.,https://www.who.int/feeds/entity/countries/phl/en/rss.xml,1,Ciencia,63,Filipinas,en,False,30 +5920,AllAfrica Philippines Business,Aggregated business and economic news.,https://allafrica.com/tools/headlines/rdf/philippines/business.rdf,4,Economía,63,Filipinas,en,False,30 +5919,Asian Development Bank Philippines,Development finance projects and economic reports.,https://www.adb.org/countries/philippines/rss,4,Economía,63,Filipinas,en,False,30 +5915,Bangko Sentral ng Pilipinas News,Central bank monetary policy and financial stability updates.,https://www.bsp.gov.ph/MediaAndResearch/rss.aspx,4,Economía,63,Filipinas,en,False,30 +5916,Department of Finance Philippines,Public finance budget taxation and fiscal policy news.,https://www.dof.gov.ph/feed/,4,Economía,63,Filipinas,en,False,30 +5917,National Economic and Development Authority,National planning development and economic policy updates.,https://neda.gov.ph/feed/,4,Economía,63,Filipinas,en,False,30 +5913,Philippine Daily Inquirer Business,Business economy and financial reporting.,https://business.inquirer.net/feed,4,Economía,63,Filipinas,en,True,0 +5918,Philippine Statistics Authority,Official economic statistics and indicators releases.,https://psa.gov.ph/rss.xml,4,Economía,63,Filipinas,en,False,30 +5901,ABS CBN News Politics,Political news elections and government affairs.,https://news.abs-cbn.com/rss/news,11,Política,63,Filipinas,en,False,30 +1914,AllAfrica Philippines Politics,Aggregated political news and regional context.,https://allafrica.com/tools/headlines/rdf/philippines/headlines.rdf,11,Política,63,Filipinas,en,False,30 +5906,BusinessWorld Politics,Political economy and policy impact analysis.,https://www.bworldonline.com/feed/,11,Política,63,Filipinas,en,True,0 +5932,Commission on Elections News,Elections voter registration and political process updates.,https://comelec.gov.ph/rss.xml,11,Política,63,Filipinas,en,False,30 +5931,Department of Foreign Affairs Philippines,Foreign policy diplomacy and official statements.,https://dfa.gov.ph/rss.xml,11,Política,63,Filipinas,en,False,30 +5902,GMA News Politics,Political developments and government coverage.,https://www.gmanetwork.com/news/rss/news/nation/,11,Política,63,Filipinas,en,False,30 +5909,House of Representatives Philippines News,Parliamentary activity and legislative updates.,https://www.congress.gov.ph/rss.php,11,Política,63,Filipinas,en,False,30 +5898,Inquirer Politics,Political news government and public affairs coverage.,https://newsinfo.inquirer.net/feed,11,Política,63,Filipinas,en,True,0 +5903,Manila Bulletin Politics,Political and national affairs reporting.,https://mb.com.ph/feed/,11,Política,63,Filipinas,en,False,30 +5904,Manila Times Politics,Political commentary policy and governance news.,https://www.manilatimes.net/rss,11,Política,63,Filipinas,en,False,30 +5930,Office of the President Philippines,Presidential statements policies and political activity.,https://www.op.gov.ph/feed/,11,Política,63,Filipinas,en,False,30 +5907,Official Gazette Philippines,Official government laws proclamations and announcements.,https://www.officialgazette.gov.ph/rss/,11,Política,63,Filipinas,en,False,30 +5929,PCOO News,Official government communications and political announcements.,https://pcoo.gov.ph/feed/,11,Política,63,Filipinas,en,False,30 +5934,Philippine Center for Investigative Journalism,Investigative reporting on politics corruption and governance.,https://pcij.org/feed/,11,Política,63,Filipinas,en,True,0 +5899,Philippine Daily Inquirer Politics,National politics and governance reporting.,https://www.inquirer.net/fullfeed,11,Política,63,Filipinas,en,True,0 +5897,Philippine News Agency Politics,Official political news and government statements from Philippines.,https://www.pna.gov.ph/rss,11,Política,63,Filipinas,en,False,30 +5900,Rappler Politics,Independent political analysis governance and accountability.,https://www.rappler.com/rss,11,Política,63,Filipinas,en,True,0 +5908,Senate of the Philippines News,Legislative activity laws and senate sessions.,https://legacy.senate.gov.ph/rss.asp,11,Política,63,Filipinas,en,False,30 +5905,SunStar Philippines Politics,Regional and national political news.,https://www.sunstar.com.ph/rss,11,Política,63,Filipinas,en,False,30 +5933,Supreme Court Philippines News,Judicial rulings constitutional law and governance.,https://sc.judiciary.gov.ph/feed/,11,Política,63,Filipinas,en,False,30 +5935,Vera Files Politics,Fact checking and political accountability reporting.,https://verafiles.org/feed,11,Política,63,Filipinas,en,False,30 +2028,Aalto University Research News,Science technology design and innovation research.,https://www.aalto.fi/en/rss.xml,1,Ciencia,64,Finlandia,en,False,30 +2024,Academy of Finland News,National research funding science and innovation updates.,https://www.aka.fi/en/rss,1,Ciencia,64,Finlandia,en,False,30 +2029,Finnish Institute for Health and Welfare,Public health epidemiology and medical research.,https://thl.fi/en/rss,1,Ciencia,64,Finlandia,en,False,30 +5950,Finnish Meteorological Institute Science,Climate weather and atmospheric science research.,https://en.ilmatieteenlaitos.fi/rss,1,Ciencia,64,Finlandia,en,False,30 +2023,Helsingin Sanomat Science,Science research health and environment reporting.,https://www.hs.fi/rss/tiede.xml,1,Ciencia,64,Finlandia,fi,True,0 +2027,University of Helsinki Science News,Academic research discoveries and scientific publications.,https://www.helsinki.fi/en/rss,1,Ciencia,64,Finlandia,en,False,30 +2026,VTT Technical Research Centre,Applied science technology and industrial research news.,https://www.vttresearch.com/en/rss,1,Ciencia,64,Finlandia,en,False,30 +2021,Yle Science News,Science research health environment and innovation news from Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_SCIENCE,1,Ciencia,64,Finlandia,en,False,30 +2022,Yle Tiede,Suomen tiede tutkimus ja innovaatio uutiset.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_TIEDE.rss,1,Ciencia,64,Finlandia,fi,False,30 +2038,Design Museum Finland News,Design culture exhibitions and creative industries.,https://www.designmuseum.fi/en/rss/,2,Cultura,64,Finlandia,en,False,30 +5955,Finland Today Culture,News on Finnish culture arts heritage and society.,https://finlandtoday.fi/feed/,2,Cultura,64,Finlandia,en,True,0 +2039,Finnish Literature Society News,Literature language culture and academic humanities.,https://www.finlit.fi/en/rss,2,Cultura,64,Finlandia,en,True,0 +2037,Finnish National Opera and Ballet,Performing arts opera ballet and cultural events.,https://oopperabaletti.fi/en/rss/,2,Cultura,64,Finlandia,en,True,0 +5953,Helsingin Sanomat Culture,Culture arts literature and cultural debate reporting.,https://www.hs.fi/rss/kulttuuri.xml,2,Cultura,64,Finlandia,fi,True,0 +2036,National Museum of Finland News,Culture history exhibitions and heritage news.,https://www.kansallismuseo.fi/en/rss,2,Cultura,64,Finlandia,en,False,30 +2040,Visit Finland Culture,Cultural tourism heritage and national identity.,https://www.visitfinland.com/feed/,2,Cultura,64,Finlandia,en,False,30 +5951,Yle Culture News,Culture arts literature music and society news from Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_KULTTUURI,2,Cultura,64,Finlandia,en,False,30 +5952,Yle Kulttuuri,Suomen kulttuuri taide musiikki ja kirjallisuus uutiset.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_KULTTUURI.rss,2,Cultura,64,Finlandia,fi,False,30 +5959,Finnish Olympic Committee News,Olympic sports athletes and competitions.,https://www.olympiakomitea.fi/en/rss/,3,Deportes,64,Finlandia,en,True,0 +2043,Helsingin Sanomat Sports,Sports coverage football ice hockey and athletics.,https://www.hs.fi/rss/urheilu.xml,3,Deportes,64,Finlandia,fi,True,0 +5957,Ilta Sanomat Sports,Daily sports news leagues teams and results.,https://www.is.fi/rss/urheilu.xml,3,Deportes,64,Finlandia,fi,True,0 +5956,Iltalehti Sports,Sports headlines ice hockey football and motorsport.,https://www.iltalehti.fi/rss/urheilu.xml,3,Deportes,64,Finlandia,fi,True,0 +2050,International Ski Federation Finland,Skiing competitions athletes and events.,https://www.fis-ski.com/rss,3,Deportes,64,Finlandia,en,False,30 +2048,Liiga Ice Hockey News,Professional ice hockey league news and standings.,https://liiga.fi/fi/rss,3,Deportes,64,Finlandia,fi,False,30 +5958,MTV Sports Finland,Sports news ice hockey football and national teams.,https://www.mtvuutiset.fi/rss/urheilu,3,Deportes,64,Finlandia,fi,False,30 +2047,Veikkausliiga News,Top football league news clubs and matches.,https://www.veikkausliiga.com/rss,3,Deportes,64,Finlandia,fi,False,30 +2041,Yle Sports News,Sports news results and national competitions in Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_URHEILU,3,Deportes,64,Finlandia,en,True,0 +2042,Yle Urheilu,Suomen urheilu uutiset kilpailut ja liitot.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_URHEILU.rss,3,Deportes,64,Finlandia,fi,True,1 +5961,Helsingin Sanomat World News,International politics conflicts and global affairs coverage.,https://www.hs.fi/rss/maailma.xml,7,Internacional,64,Finlandia,fi,True,0 +2069,UN Association of Finland,United Nations international cooperation and peacekeeping.,https://www.ykliitto.fi/rss,7,Internacional,64,Finlandia,en,True,0 +5960,Yle International News,International news world affairs and foreign policy from Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_WORLD,7,Internacional,64,Finlandia,en,False,30 +2062,Yle Maailma,Suomen nakokulma kansainvalisiin uutisiin ja maailmanpolitiikkaan.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_MAAILMA.rss,7,Internacional,64,Finlandia,fi,False,30 +5939,AllAfrica Finland Politics,Aggregated political news related to Finland.,https://allafrica.com/tools/headlines/rdf/finland/headlines.rdf,11,Política,64,Finlandia,en,False,30 +5948,European Affairs Finland,EU policy and Finnish political positioning.,https://vnk.fi/en/rss,11,Política,64,Finlandia,en,False,30 +5938,Finnish Institute of International Affairs,Foreign policy security and political analysis.,https://www.fiia.fi/en/rss,11,Política,64,Finlandia,en,True,0 +5949,Finnish Security and Intelligence Service,National security policy and intelligence updates.,https://supo.fi/en/rss,11,Política,64,Finlandia,en,False,30 +5946,Government Communications Department,Official government press releases and political updates.,https://valtioneuvosto.fi/en/rss/pressreleases,11,Política,64,Finlandia,en,False,30 +2003,Helsingin Sanomat Politics,Political news and policy analysis from Finland.,https://www.hs.fi/rss/politiikka.xml,11,Política,64,Finlandia,fi,True,0 +2004,Helsinki Times Politics,Political news from Finland in English.,https://www.helsinkitimes.fi/?format=feed&type=rss,11,Política,64,Finlandia,en,True,0 +5943,Ilta Sanomat Politics,Daily political news and public affairs coverage.,https://www.is.fi/rss/politiikka.xml,11,Política,64,Finlandia,fi,True,0 +5942,Iltalehti Politics,Political headlines parliament and government news.,https://www.iltalehti.fi/rss/politiikka.xml,11,Política,64,Finlandia,fi,True,0 +5944,Kansan Uutiset Politics,Left wing political news and social policy analysis.,https://www.kansanuutiset.fi/rss/politiikka,11,Política,64,Finlandia,fi,False,30 +5947,Ministry for Foreign Affairs Finland,Foreign policy diplomacy and international relations.,https://um.fi/rss,11,Política,64,Finlandia,en,False,30 +2005,Ministry of Finance Finland News,Government policy and political economy announcements.,https://vm.fi/rss,11,Política,64,Finlandia,en,False,30 +5936,Parliament of Finland News,Legislative activity laws and parliamentary sessions.,https://www.eduskunta.fi/EN/rss/Sivut/default.aspx,11,Política,64,Finlandia,en,False,30 +5937,President of Finland News,Official presidential activities and statements.,https://www.presidentti.fi/en/rss/,11,Política,64,Finlandia,en,True,0 +2006,Prime Minister Office Finland,Official statements and political announcements.,https://valtioneuvosto.fi/en/rss,11,Política,64,Finlandia,en,False,30 +5945,Suomen Kuvalehti Politics,In depth political analysis and long form reporting.,https://suomenkuvalehti.fi/rss/politiikka/,11,Política,64,Finlandia,fi,False,30 +5940,YLE English Politics,Political news and analysis from Finland in English.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_NEWS,11,Política,64,Finlandia,en,True,0 +2001,Yle News Politics,Political news government and parliament coverage in Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_UUTISET,11,Política,64,Finlandia,en,True,0 +2002,Yle Uutiset Politics,Suomen poliittiset uutiset hallitus ja eduskunta.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_UUTISET.rss,11,Política,64,Finlandia,fi,True,0 +5976,Digia Technology News,Enterprise software cloud and digital transformation.,https://digia.com/en/feed/,14,Tecnología,64,Finlandia,en,False,30 +5978,F Secure Labs News,Cybersecurity research threats and digital safety.,https://labs.f-secure.com/feed/,14,Tecnología,64,Finlandia,en,False,30 +5970,Finnish Cyber Security Centre,National cybersecurity incidents and guidance.,https://www.kyberturvallisuuskeskus.fi/en/rss,14,Tecnología,64,Finlandia,en,False,30 +5964,Helsingin Sanomat Technology,Technology innovation startups and digital society.,https://www.hs.fi/rss/teknologia.xml,14,Tecnología,64,Finlandia,fi,True,0 +5979,ICEYE News,Space technology satellite data and earth observation.,https://www.iceye.com/blog/rss.xml,14,Tecnología,64,Finlandia,en,True,0 +5974,ICT Finland News,Information technology digital economy and innovation in Finland.,https://www.ict.fi/rss,14,Tecnología,64,Finlandia,en,False,30 +5968,Ministry of Transport and Communications Finland,Digital infrastructure telecom and technology policy.,https://lvm.fi/en/rss,14,Tecnología,64,Finlandia,en,False,30 +5973,Nokia Newsroom,Telecommunications technology products and R and D.,https://www.nokia.com/about-us/newsroom/rss/,14,Tecnología,64,Finlandia,en,False,30 +5977,Reaktor Technology Insights,Software engineering data and digital product development.,https://www.reaktor.com/blog/feed/,14,Tecnología,64,Finlandia,en,False,30 +5980,Rovio Developer News,Mobile gaming technology and development updates.,https://www.rovio.com/articles/feed/,14,Tecnología,64,Finlandia,en,True,0 +5971,Slush News,Startup ecosystem technology innovation and venture capital.,https://slush.org/feed/,14,Tecnología,64,Finlandia,en,False,30 +5972,Startup Finland News,Startups technology entrepreneurship and innovation.,https://startupfinland.com/feed/,14,Tecnología,64,Finlandia,en,False,30 +2100,Supercell Engineering,Game development technology and engineering culture.,https://supercell.com/en/feed/,14,Tecnología,64,Finlandia,en,False,30 +5975,TEKES Business Finland Innovation,Technology innovation funding research and startups.,https://www.businessfinland.fi/en/rss,14,Tecnología,64,Finlandia,en,False,30 +5969,Traficom Technology,ICT regulation cybersecurity and digital services.,https://www.traficom.fi/en/rss,14,Tecnología,64,Finlandia,en,False,30 +5962,Yle Technology News,Technology digitalization innovation and IT policy in Finland.,https://feeds.yle.fi/uutiset/v1/recent.rss?publisherIds=YLE_TEKNIIKKA,14,Tecnología,64,Finlandia,en,False,30 +5963,Yle Teknologia,Suomen teknologia digitalisaatio ja IT uutiset.,https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_TEKNIIKKA.rss,14,Tecnología,64,Finlandia,fi,False,30 +2219,Fiji Broadcasting Corporation Science,Science environment climate and research news in Fiji.,https://www.fbcnews.com.fj/category/news/science/feed/,1,Ciencia,65,Fiyi,en,True,0 +5998,Fiji Meteorological Service Science,Climate weather oceanography and atmospheric science.,https://www.met.gov.fj/feed/,1,Ciencia,65,Fiyi,en,False,30 +5999,Fiji Ministry of Environment News,Environmental science climate change and conservation.,https://environment.gov.fj/feed/,1,Ciencia,65,Fiyi,en,False,30 +5997,Fiji Ministry of Health Research,Public health research science and epidemiology updates.,https://www.health.gov.fj/feed/,1,Ciencia,65,Fiyi,en,True,0 +5995,Fiji Times Science,Science environment health and research coverage.,https://www.fijitimes.com/category/news/science/feed/,1,Ciencia,65,Fiyi,en,False,30 +6000,Pacific Community Science,Regional science health agriculture and fisheries research.,https://www.spc.int/rss,1,Ciencia,65,Fiyi,en,False,30 +6001,Secretariat of the Pacific Regional Environment Programme,Environmental science biodiversity and climate research.,https://www.sprep.org/rss.xml,1,Ciencia,65,Fiyi,en,False,30 +6003,UNICEF Pacific Science,Science health nutrition and child development research.,https://www.unicef.org/pacificislands/rss.xml,1,Ciencia,65,Fiyi,en,False,30 +5996,University of the South Pacific Research,Academic research science and innovation from USP.,https://www.usp.ac.fj/feed/,1,Ciencia,65,Fiyi,en,False,30 +6002,World Health Organization Fiji,Scientific public health research and reports.,https://www.who.int/feeds/entity/countries/fji/en/rss.xml,1,Ciencia,65,Fiyi,en,False,30 +6024,Fiji Arts Council News,Culture arts heritage and creative activities.,https://www.fijiartscouncil.com/feed/,2,Cultura,65,Fiyi,en,False,30 +6025,Fiji National Archives News,History heritage archives and cultural memory.,https://www.archives.gov.fj/feed/,2,Cultura,65,Fiyi,en,False,30 +6016,Fiji Broadcasting Corporation Sports,Sports football rugby and national competitions.,https://www.fbcnews.com.fj/category/sports/feed/,3,Deportes,65,Fiyi,en,True,0 +6018,Fiji Sun Sports,Sports rugby sevens football and athletics.,https://fijisun.com.fj/category/sports/feed/,3,Deportes,65,Fiyi,en,False,30 +6017,Fiji Times Sports,Sports rugby football and regional competitions.,https://www.fijitimes.com/category/sport/feed/,3,Deportes,65,Fiyi,en,True,0 +2218,AllAfrica Fiji Business,Aggregated business and economic news related to Fiji.,https://allafrica.com/tools/headlines/rdf/fiji/business.rdf,4,Economía,65,Fiyi,en,False,30 +2216,Asian Development Bank Fiji,Economic development projects and finance reports.,https://www.adb.org/countries/fiji/rss,4,Economía,65,Fiyi,en,False,30 +5991,Fiji Broadcasting Corporation Business,Economic policy business and market developments.,https://www.fbcnews.com.fj/category/business/feed/,4,Economía,65,Fiyi,en,True,0 +2215,Fiji Bureau of Statistics Releases,Official statistics indicators and economic data.,https://www.statsfiji.gov.fj/rss,4,Economía,65,Fiyi,en,True,0 +5990,Fiji Sun Business,Economy finance trade and industry coverage.,https://fijisun.com.fj/category/business/feed/,4,Economía,65,Fiyi,en,False,30 +5989,Fiji Times Business,Business economy markets and corporate news in Fiji.,https://www.fijitimes.com/category/business/feed/,4,Economía,65,Fiyi,en,True,0 +5994,Investment Fiji News,Investment trade and economic development updates.,https://www.investmentfiji.org.fj/feed/,4,Economía,65,Fiyi,en,False,30 +5993,Ministry of Economy Fiji,Public finance budget taxation and economic policy.,https://www.economy.gov.fj/rss,4,Economía,65,Fiyi,en,False,30 +5992,Reserve Bank of Fiji News,Monetary policy banking and financial stability updates.,https://www.rbf.gov.fj/feed/,4,Economía,65,Fiyi,en,True,0 +2217,World Bank Fiji Economy,Development finance projects and economic reports.,https://www.worldbank.org/en/country/fiji/rss.xml,4,Economía,65,Fiyi,en,False,30 +6021,Fiji Ministry of Education News,Education policy schools universities and training.,https://www.education.gov.fj/feed/,5,Educación,65,Fiyi,en,False,30 +6027,Fiji National Disaster Management Office,Disaster preparedness response and risk management.,https://www.ndmo.gov.fj/feed/,8,Medio Ambiente,65,Fiyi,en,False,30 +5988,AllAfrica Fiji Politics,Aggregated political news related to Fiji.,https://allafrica.com/tools/headlines/rdf/fiji/headlines.rdf,11,Política,65,Fiyi,en,False,30 +5981,Fiji Broadcasting Corporation News,Political news government activity and national affairs.,https://www.fbcnews.com.fj/feed/,11,Política,65,Fiyi,en,True,0 +5984,Fiji Government News,Official government announcements and political statements.,https://www.fiji.gov.fj/rss,11,Política,65,Fiyi,en,False,30 +5983,Fiji Sun Politics,Political developments parliament and elections.,https://fijisun.com.fj/feed/,11,Política,65,Fiyi,en,False,30 +5982,Fiji Times Politics,Political news governance and public policy in Fiji.,https://www.fijitimes.com/feed/,11,Política,65,Fiyi,en,True,0 +5987,Ministry of Foreign Affairs Fiji,Foreign policy diplomacy and international relations.,https://www.foreignaffairs.gov.fj/rss,11,Política,65,Fiyi,en,True,0 +5985,Parliament of Fiji News,Legislative activity bills and parliamentary sessions.,https://www.parliament.gov.fj/rss,11,Política,65,Fiyi,en,False,30 +5986,Prime Minister of Fiji News,Prime minister speeches policies and political agenda.,https://www.pmoffice.gov.fj/rss,11,Política,65,Fiyi,en,False,30 +6022,Fiji Ministry of Women Children and Poverty Alleviation,Social policy gender equality and community programs.,https://www.mwcp.gov.fj/feed/,13,Sociedad,65,Fiyi,en,False,30 +6026,Fiji Police Force News,Public safety crime prevention and community policing.,https://www.police.gov.fj/feed/,13,Sociedad,65,Fiyi,en,False,30 +6012,Digicel Fiji Technology,Mobile technology networks and digital services.,https://www.digicelgroup.com/en/media/rss.html,14,Tecnología,65,Fiyi,en,False,30 +6004,Fiji Broadcasting Corporation Technology,Technology digital services and innovation news in Fiji.,https://www.fbcnews.com.fj/category/news/technology/feed/,14,Tecnología,65,Fiyi,en,True,0 +6008,Fiji Ministry of Innovation Science and Technology,Government technology innovation and digital strategy.,https://www.msit.gov.fj/feed/,14,Tecnología,65,Fiyi,en,False,30 +6010,Fiji National University Technology,Applied technology engineering and research news.,https://www.fnu.ac.fj/feed/,14,Tecnología,65,Fiyi,en,True,0 +6006,Fiji Sun Technology,Technology telecommunications and digital transformation news.,https://fijisun.com.fj/category/technology/feed/,14,Tecnología,65,Fiyi,en,False,30 +6005,Fiji Times Technology,Technology ICT digital economy and innovation coverage.,https://www.fijitimes.com/category/news/technology/feed/,14,Tecnología,65,Fiyi,en,False,30 +6007,Ministry of Communications Fiji,ICT policy digital infrastructure and technology programs.,https://www.mediagov.com.fj/rss,14,Tecnología,65,Fiyi,en,False,30 +6013,Pacific ICT Alliance News,Regional ICT technology policy and innovation.,https://www.picisoc.org/feed/,14,Tecnología,65,Fiyi,en,True,0 +6011,Telecom Fiji News,Telecommunications infrastructure and digital services.,https://www.telecom.com.fj/feed/,14,Tecnología,65,Fiyi,en,True,0 +6009,University of the South Pacific ICT News,Technology research ICT education and innovation.,https://www.usp.ac.fj/category/ict/feed/,14,Tecnología,65,Fiyi,en,False,30 +6019,Fiji Tourism News,Travel tourism culture and national heritage.,https://www.fiji.travel/feed/,15,Viajes,65,Fiyi,en,False,30 +2337,CEA Actualites,Recherche energie nucleaire climat et technologies.,https://www.cea.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6049,CNRS Actualites,Recherche scientifique fondamentale et appliquee.,https://www.cnrs.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6042,France 24 Science,Science technology health and environment news from France.,https://www.france24.com/en/rss/science,1,Ciencia,66,Francia,en,False,30 +6043,France 24 Science FR,Actualite scientifique recherche sante et environnement.,https://www.france24.com/fr/rss/sciences,1,Ciencia,66,Francia,fr,False,30 +6053,IFREMER Sciences Marines,Recherche oceanographique peche et sciences marines.,https://wwz.ifremer.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6051,INRAE Actualites,Recherche agriculture alimentation et environnement.,https://www.inrae.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6052,IRD Recherche,Recherche scientifique developpement climat et biodiversite.,https://www.ird.fr/rss,1,Ciencia,66,Francia,fr,True,0 +6048,Inserm Actualites,Recherche medicale sante publique et biomedecine.,https://www.inserm.fr/feed/,1,Ciencia,66,Francia,fr,True,0 +6050,Institut Pasteur News,Recherche biomedicale maladies infectieuses et vaccins.,https://www.pasteur.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6045,Le Figaro Sciences,Informations scientifiques sante recherche et technologies.,https://www.lefigaro.fr/rss/figaro_sciences.xml,1,Ciencia,66,Francia,fr,True,0 +6044,Le Monde Sciences,Actualite scientifique recherche et innovations.,https://www.lemonde.fr/sciences/rss_full.xml,1,Ciencia,66,Francia,fr,True,0 +6046,Liberation Sciences,Sciences recherche climat et sante publique.,https://www.liberation.fr/rss,1,Ciencia,66,Francia,fr,False,30 +6054,Meteo France Sciences,Climat meteorologie et sciences atmospheriques.,https://meteofrance.com/rss,1,Ciencia,66,Francia,fr,False,30 +6047,Sciences et Avenir,Magazine de reference en sciences et technologies.,https://www.sciencesetavenir.fr/rss.xml,1,Ciencia,66,Francia,fr,True,0 +6062,BFM Business Economie,Actualite economique marches entreprises et finance.,https://www.bfmtv.com/rss/economie/,4,Economía,66,Francia,fr,True,0 +6064,Banque de France News,Politique monetaire stabilite financiere et economie.,https://www.banque-france.fr/rss,4,Economía,66,Francia,fr,False,30 +6060,Challenges Economie,Analyses economiques entreprises et pouvoir economique.,https://www.challenges.fr/rss/rss_economie.xml,4,Economía,66,Francia,fr,False,30 +6056,France 24 Economie FR,Actualite economique entreprises marches et politiques publiques.,https://www.france24.com/fr/rss/economie,4,Economía,66,Francia,fr,False,30 +6055,France 24 Economy,Economy business markets and public policy news from France.,https://www.france24.com/en/rss/economy,4,Economía,66,Francia,en,False,30 +6063,INSEE Actualites,Statistiques officielles indicateurs et economie francaise.,https://www.insee.fr/fr/rss,4,Economía,66,Francia,fr,False,30 +6061,La Tribune Economie,Actualite economique innovation et entreprises.,https://www.latribune.fr/rss,4,Economía,66,Francia,fr,False,30 +6059,Le Figaro Economie,Actualite economique entreprises finance et industrie.,https://www.lefigaro.fr/rss/figaro_economie.xml,4,Economía,66,Francia,fr,True,0 +6057,Le Monde Economie,Actualite economique nationale entreprises et finances.,https://www.lemonde.fr/economie/rss_full.xml,4,Economía,66,Francia,fr,True,0 +6058,Les Echos Economie,Informations economiques entreprises marches et finance.,https://www.lesechos.fr/rss/rss_economie.xml,4,Economía,66,Francia,fr,False,30 +6065,Ministere de l Economie France,Politiques economiques finances publiques et entreprises.,https://www.economie.gouv.fr/rss,4,Economía,66,Francia,fr,False,30 +6066,OCDE France Economie,Analyses economiques comparaisons et politiques publiques.,https://www.oecd.org/france/rss.xml,4,Economía,66,Francia,en,False,30 +6067,France 24 Monde FR,Actualite internationale geopolitique et conflits.,https://www.france24.com/fr/rss/monde,7,Internacional,66,Francia,fr,False,30 +2369,France 24 World News,International news geopolitics conflicts and diplomacy.,https://www.france24.com/en/rss/world,7,Internacional,66,Francia,en,False,30 +6070,IRIS Geopolitique,Analyses geopolitique conflits et relations internationales.,https://www.iris-france.org/feed/,7,Internacional,66,Francia,fr,True,0 +2374,La Croix International,Actualite internationale conflits humanitaire et diplomatie.,https://www.la-croix.com/rss/international.xml,7,Internacional,66,Francia,fr,False,30 +6069,Le Figaro International,Informations internationales geopolitique et diplomatie.,https://www.lefigaro.fr/rss/figaro_international.xml,7,Internacional,66,Francia,fr,True,0 +6068,Le Monde International,Actualite internationale diplomatie et affaires mondiales.,https://www.lemonde.fr/international/rss_full.xml,7,Internacional,66,Francia,fr,True,0 +2377,Ministere de l Europe et des Affaires Etrangeres,Politique et diplomatie internationale de la France.,https://www.diplomatie.gouv.fr/rss,7,Internacional,66,Francia,fr,False,30 +6071,Politico Europe France,European and international politics with French perspective.,https://www.politico.eu/rss/france/,7,Internacional,66,Francia,en,False,30 +2375,RFI Monde,Informations internationales diplomatie et regions du monde.,https://www.rfi.fr/fr/rss/monde,7,Internacional,66,Francia,fr,False,30 +2376,TV5 Monde International,Actualite mondiale geopolitique culture et societe.,https://www.tv5monde.com/rss/monde.xml,7,Internacional,66,Francia,fr,False,30 +6076,Agence de la Transition Ecologique ADEME,Transition energetique environnement et innovations.,https://www.ademe.fr/rss,8,Medio Ambiente,66,Francia,fr,False,30 +6072,France 24 Environment,Environment climate biodiversity and sustainability news.,https://www.france24.com/en/rss/environment,8,Medio Ambiente,66,Francia,en,False,30 +6073,France 24 Environnement FR,Actualite environnement climat biodiversite et transition ecologique.,https://www.france24.com/fr/rss/environnement,8,Medio Ambiente,66,Francia,fr,False,30 +2386,France Nature Environnement,Actualites ecologie protection nature et biodiversite.,https://fne.asso.fr/rss.xml,8,Medio Ambiente,66,Francia,fr,False,30 +2391,Greenpeace France,Campagnes environnement climat oceans et biodiversite.,https://www.greenpeace.fr/rss,8,Medio Ambiente,66,Francia,fr,True,0 +6074,Le Figaro Environnement,Informations environnement ecologie energie et climat.,https://www.lefigaro.fr/rss/figaro_environnement.xml,8,Medio Ambiente,66,Francia,fr,True,0 +2383,Le Monde Planete,Actualite environnement climat et biodiversite.,https://www.lemonde.fr/planete/rss_full.xml,8,Medio Ambiente,66,Francia,fr,True,0 +2387,Ministere de la Transition Ecologique,Politiques environnementales climat et transition energetique.,https://www.ecologie.gouv.fr/rss,8,Medio Ambiente,66,Francia,fr,False,30 +6077,Office Francais de la Biodiversite,Protection biodiversite faune flore et milieux naturels.,https://ofb.gouv.fr/rss,8,Medio Ambiente,66,Francia,fr,False,30 +6075,Reporterre Ecologie,Journal independant ecologie climat et environnement.,https://reporterre.net/spip.php?page=backend,8,Medio Ambiente,66,Francia,fr,True,0 +6079,WWF France Environnement,Conservation biodiversite climat et nature.,https://www.wwf.fr/rss.xml,8,Medio Ambiente,66,Francia,fr,True,0 +6086,Atlantico Opinions,Opinion analysis politics society and international affairs.,https://atlantico.fr/rss,10,Opinión,66,Francia,fr,False,30 +6083,Challenges Opinions,Opinion and analysis on economy business and politics.,https://www.challenges.fr/rss/rss_opinions.xml,10,Opinión,66,Francia,fr,False,30 +6085,LObs Opinions,Opinion columns ideas and public debate.,https://www.nouvelobs.com/rss/opinion.xml,10,Opinión,66,Francia,fr,False,30 +2396,La Croix Opinions,Editorials opinions and ethical debate.,https://www.la-croix.com/rss/idees.xml,10,Opinión,66,Francia,fr,False,30 +6080,Le Figaro Vox,Opinion columns debates and viewpoints from Le Figaro.,https://www.lefigaro.fr/rss/figaro_vox.xml,10,Opinión,66,Francia,fr,True,0 +2393,Le Monde Opinions,Opinions editorials and commentary on French and global affairs.,https://www.lemonde.fr/idees/rss_full.xml,10,Opinión,66,Francia,fr,True,0 +6082,Les Echos Opinions,Opinion analysis on economy policy and society.,https://www.lesechos.fr/rss/rss_opinions.xml,10,Opinión,66,Francia,fr,False,30 +6081,Liberation Opinions,Opinion pieces social debate and political commentary.,https://www.liberation.fr/idees-et-debats/rss,10,Opinión,66,Francia,fr,False,30 +6084,Marianne Opinions,Opinion essays political ideas and social debate.,https://www.marianne.net/rss/opinions,10,Opinión,66,Francia,fr,False,30 +6087,The Conversation France,Academic opinion analysis and expert commentary.,https://theconversation.com/fr/articles/feed,10,Opinión,66,Francia,fr,False,30 +6034,20 Minutes Politique,Actualite politique accessible et debats publics.,https://www.20minutes.fr/feeds/rss-politique.xml,11,Política,66,Francia,fr,True,0 +6095,Assemblee Nationale France,Activite legislative lois et commissions.,https://www.assemblee-nationale.fr/dyn/rss,11,Política,66,Francia,fr,False,30 +6028,BFM TV Politique,Actualite politique en continu gouvernement et elections.,https://www.bfmtv.com/rss/politique/,11,Política,66,Francia,fr,True,0 +6038,Challenges Politique,Politique publique economie et pouvoir.,https://www.challenges.fr/rss/rss_politique.xml,11,Política,66,Francia,fr,False,30 +6041,Conseil Constitutionnel News,Decisions constitutionnelles et vie institutionnelle.,https://www.conseil-constitutionnel.fr/rss,11,Política,66,Francia,fr,False,30 +6030,Europe 1 Politique,Actualite politique interviews et chroniques.,https://www.europe1.fr/rss/politique.xml,11,Política,66,Francia,fr,True,0 +6089,France 24 Politics EN,Political news and government affairs in France.,https://www.france24.com/en/rss/politics,11,Política,66,Francia,en,False,30 +6088,France 24 Politique,Actualite politique francaise institutions et pouvoir.,https://www.france24.com/fr/rss/politique,11,Política,66,Francia,fr,False,30 +6097,Gouvernement Francais,Communiques officiels et politique gouvernementale.,https://www.gouvernement.fr/rss,11,Política,66,Francia,fr,False,30 +6040,IFRI Politique Internationale,Politique internationale defense et diplomatie francaise.,https://www.ifri.org/rss,11,Política,66,Francia,fr,False,30 +6039,Institut Montaigne,Analyses politiques publiques et reformes.,https://www.institutmontaigne.org/rss.xml,11,Política,66,Francia,fr,False,30 +6029,LCI Politique,Informations politiques debats et analyses en France.,https://www.lci.fr/rss/politique.xml,11,Política,66,Francia,fr,False,30 +6037,LObs Politique,Actualite politique debats et societe.,https://www.nouvelobs.com/rss/politique.xml,11,Política,66,Francia,fr,False,30 +6091,Le Figaro Politique,Informations politiques gouvernement parlement et partis.,https://www.lefigaro.fr/rss/figaro_politique.xml,11,Política,66,Francia,fr,True,0 +6090,Le Monde Politique,Actualite politique nationale elections et gouvernement.,https://www.lemonde.fr/politique/rss_full.xml,11,Política,66,Francia,fr,True,0 +6032,Le Parisien Politique,Actualite politique nationale et regionale.,https://www.leparisien.fr/politique/rss.xml,11,Política,66,Francia,fr,True,0 +6093,Les Echos Politique,Politique publique gouvernance et pouvoir economique.,https://www.lesechos.fr/rss/rss_politique.xml,11,Política,66,Francia,fr,False,30 +6036,Marianne Politique,Analyses politiques idees et debats intellectuels.,https://www.marianne.net/rss/politique,11,Política,66,Francia,fr,False,30 +6035,Mediapart Politique,Enquetes politiques pouvoir et institutions.,https://www.mediapart.fr/rss,11,Política,66,Francia,fr,False,30 +6099,Ministere de l Interieur France,Securite elections et administration territoriale.,https://www.interieur.gouv.fr/rss,11,Política,66,Francia,fr,False,30 +6033,Ouest France Politique,Politique nationale et enjeux regionaux.,https://www.ouest-france.fr/rss/politique,11,Política,66,Francia,fr,False,30 +6098,Presidence de la Republique,Elysee discours et agenda presidentiel.,https://www.elysee.fr/rss,11,Política,66,Francia,fr,False,30 +6094,Public Senat,Debats parlementaires lois et activite du Senat.,https://www.publicsenat.fr/rss,11,Política,66,Francia,fr,True,0 +6031,RTL Politique,Politique francaise institutions et pouvoir executif.,https://www.rtl.fr/actu/politique/rss,11,Política,66,Francia,fr,False,30 +6096,Senat Francais,Actualite du Senat debats et textes legislatifs.,https://www.senat.fr/rss,11,Política,66,Francia,fr,False,30 +2429,ANSSI Cybersecurity,Cybersecurity threats digital safety and policy.,https://www.ssi.gouv.fr/rss,14,Tecnología,66,Francia,fr,False,30 +2428,BPI France Innovation,Innovation financement startups et technologie.,https://www.bpifrance.fr/rss,14,Tecnología,66,Francia,fr,False,30 +6105,Challenges Tech,Actualite technologique innovation et entreprises.,https://www.challenges.fr/rss/rss_tech.xml,14,Tecnología,66,Francia,fr,False,30 +6101,France 24 Technologie FR,Actualite technologie innovation numerique et sciences.,https://www.france24.com/fr/rss/technologies,14,Tecnología,66,Francia,fr,False,30 +6100,France 24 Technology,Technology innovation digital policy and science from France.,https://www.france24.com/en/rss/technology,14,Tecnología,66,Francia,en,False,30 +2424,INRIA Actualites,Recherche informatique intelligence artificielle et logiciels.,https://www.inria.fr/rss,14,Tecnología,66,Francia,fr,False,30 +2427,La French Tech,Startup innovation ecosystem and digital economy.,https://lafrenchtech.com/feed/,14,Tecnología,66,Francia,fr,False,30 +6103,Le Figaro Technologies,Informations technologies innovation startups et numerique.,https://www.lefigaro.fr/rss/figaro_technologies.xml,14,Tecnología,66,Francia,fr,False,30 +6102,Le Monde Technologies,Actualite technologie numerique internet et innovation.,https://www.lemonde.fr/pixels/rss_full.xml,14,Tecnología,66,Francia,fr,True,0 +6104,Les Echos Tech,Technologie innovation startups et economie numerique.,https://www.lesechos.fr/rss/rss_tech.xml,14,Tecnología,66,Francia,fr,False,30 +2430,Orange Innovation,Telecom technology networks and digital services.,https://www.orange.com/en/rss,14,Tecnología,66,Francia,en,False,30 +2426,Station F News,Startup ecosystem technology innovation and venture capital.,https://stationf.co/rss,14,Tecnología,66,Francia,en,False,30 +2423,Usine Digitale,Industrie numerique innovation et technologies avancees.,https://www.usine-digitale.fr/rss,14,Tecnología,66,Francia,fr,True,0 +6119,Agence Nationale des Parcs Nationaux,Environnement biodiversite et recherche ecologique.,https://www.parcsgabon.ga/rss,1,Ciencia,67,Gabón,fr,False,30 +2522,AllAfrica Gabon Science,Actualite science sante et environnement agregee.,https://allafrica.com/tools/headlines/rdf/gabon/health.rdf,1,Ciencia,67,Gabón,fr,False,30 +2519,CENAREST Gabon,Recherche scientifique nationale innovation et developpement.,https://www.cenarest.ga/rss,1,Ciencia,67,Gabón,fr,False,30 +6120,Institut de Recherche en Ecologie Tropicale,Recherche biodiversite forets et climat.,https://www.iretro.org/rss,1,Ciencia,67,Gabón,fr,False,30 +6118,Ministere de la Sante Gabon,Actualites sante publique recherche et politique sanitaire.,https://www.sante.gouv.ga/rss,1,Ciencia,67,Gabón,fr,False,30 +2520,Universite Omar Bongo Recherche,Recherche universitaire sciences et sante.,https://www.uob.ga/rss,1,Ciencia,67,Gabón,fr,True,0 +2521,World Health Organization Gabon,Recherche sante publique et rapports scientifiques.,https://www.who.int/feeds/entity/countries/gab/en/rss.xml,1,Ciencia,67,Gabón,fr,False,30 +6129,AllAfrica Gabon Culture,Actualite culturelle gabonaise agregee.,https://allafrica.com/tools/headlines/rdf/gabon/arts.rdf,2,Cultura,67,Gabón,fr,False,30 +6127,Centre Culturel Francais Libreville,Evenements culturels spectacles et expositions.,https://institutfrancais-gabon.com/feed/,2,Cultura,67,Gabón,fr,True,0 +6125,Institut Gabonais des Arts et Traditions,Arts traditions et identite culturelle.,https://www.igat.ga/rss,2,Cultura,67,Gabón,fr,False,30 +6124,Ministere de la Culture Gabon,Politiques culturelles patrimoine et arts.,https://www.culture.gouv.ga/rss,2,Cultura,67,Gabón,fr,False,30 +6126,Musee National des Arts Traditions Gabon,Patrimoine musees et expositions.,https://www.museenational.ga/rss,2,Cultura,67,Gabón,fr,False,30 +6128,UNESCO Gabon Culture,Patrimoine mondial culture et education.,https://www.unesco.org/fr/countries/gabon/rss,2,Cultura,67,Gabón,fr,False,30 +6138,Agence Nationale de Promotion des Investissements Gabon,Investissement climat des affaires et economie.,https://www.investgabon.ga/rss,4,Economía,67,Gabón,fr,False,30 +6140,AllAfrica Gabon Business,Actualite economique gabonaise agregee.,https://allafrica.com/tools/headlines/rdf/gabon/business.rdf,4,Economía,67,Gabón,fr,False,30 +6136,Banque des Etats de l Afrique Centrale Gabon,Politique monetaire et stabilite financiere.,https://www.beac.int/rss,4,Economía,67,Gabón,fr,False,30 +6137,Direction Generale de la Statistique Gabon,Statistiques officielles indicateurs economiques.,https://www.stat-gabon.ga/rss,4,Economía,67,Gabón,fr,False,30 +6134,Ministere de l Economie Gabon,Politiques economiques budget et finances publiques.,https://www.economie.gouv.ga/rss,4,Economía,67,Gabón,fr,False,30 +6135,Ministere du Budget Gabon,Budget et gestion des finances publiques.,https://www.budget.gouv.ga/rss,4,Economía,67,Gabón,fr,False,30 +6139,World Bank Gabon Economy,Developpement economique projets et analyses.,https://www.worldbank.org/en/country/gabon/rss.xml,4,Economía,67,Gabón,fr,False,30 +6146,CEEAC News,Actualite regionale Afrique centrale et cooperation.,https://ceeac-eccas.org/rss,7,Internacional,67,Gabón,fr,False,30 +6147,ONU Gabon,Actions des Nations unies cooperation et diplomatie.,https://www.un.org/fr/rss,7,Internacional,67,Gabón,fr,False,30 +6145,Union Africaine Gabon,Actualite de l Union africaine et cooperation regionale.,https://au.int/rss.xml,7,Internacional,67,Gabón,fr,True,0 +6155,AllAfrica Gabon Environment,Actualite environnementale gabonaise agregee.,https://allafrica.com/tools/headlines/rdf/gabon/environment.rdf,8,Medio Ambiente,67,Gabón,fr,False,30 +6151,Gabon Bleu Initiative,Protection des oceans peche durable et biodiversite marine.,https://www.gabonbleu.org/rss,8,Medio Ambiente,67,Gabón,fr,False,30 +6152,Global Forest Watch Gabon,Surveillance des forets deforestation et climat.,https://www.globalforestwatch.org/rss.xml,8,Medio Ambiente,67,Gabón,fr,False,30 +2558,Ministere de l Environnement Gabon,Politiques environnementales climat et biodiversite.,https://www.environnement.gouv.ga/rss,8,Medio Ambiente,67,Gabón,fr,False,30 +6161,AllAfrica Gabon Opinions,Tribunes et opinions sur l actualite gabonaise.,https://allafrica.com/tools/headlines/rdf/gabon/opinion.rdf,10,Opinión,67,Gabón,fr,False,30 +6159,Jeune Afrique Gabon Opinions,Analyses et opinions sur le Gabon et l Afrique centrale.,https://www.jeuneafrique.com/rss/,10,Opinión,67,Gabón,fr,False,30 +6160,The Conversation Afrique,Opinions académiques et analyses d experts africains.,https://theconversation.com/africa/articles/feed,10,Opinión,67,Gabón,fr,False,30 +6158,Tribune Gabonaise,Opinions citoyennes debats et chroniques.,https://www.tribunegabonaise.com/feed/,10,Opinión,67,Gabón,fr,False,30 +6113,AllAfrica Gabon Politics,Actualite politique gabonaise et regionale agregee.,https://allafrica.com/tools/headlines/rdf/gabon/headlines.rdf,11,Política,67,Gabón,fr,True,0 +6111,Assemblee Nationale Gabon,Activite legislative lois et debats parlementaires.,https://www.assemblee-nationale.ga/rss,11,Política,67,Gabón,fr,False,30 +2501,Gabon Media Time Politique,Actualite politique institutions et vie publique au Gabon.,https://www.gabonmediatime.com/feed/,11,Política,67,Gabón,fr,False,30 +2503,Gabon Review Politique,Analyse politique gouvernance et pouvoir au Gabon.,https://www.gabonreview.com/feed/,11,Política,67,Gabón,fr,True,0 +6109,Gouvernement Gabonais News,Decisions gouvernementales et politiques publiques.,https://www.gouvernement.ga/rss,11,Política,67,Gabón,fr,True,0 +6106,Info241 Politique,Actualite politique elections et affaires publiques.,https://info241.com/spip.php?page=backend,11,Política,67,Gabón,fr,True,0 +2502,L Union Politique,Informations politiques gouvernement et institutions gabonaises.,https://www.union.sonapresse.com/rss,11,Política,67,Gabón,fr,False,30 +6107,Le Nouveau Gabon Politique,Politique publique economie et gouvernance.,https://www.lenouveaugabon.com/fr/rss,11,Política,67,Gabón,fr,False,30 +2579,Ministere de l Interieur Gabon,Securite administration territoriale et elections.,https://www.interieur.gouv.ga/rss,11,Política,67,Gabón,fr,False,30 +6110,Ministere des Affaires Etrangeres Gabon,Diplomatie politique exterieure et cooperation.,https://www.diplomatie.gouv.ga/rss,11,Política,67,Gabón,fr,False,30 +6108,Presidence de la Republique Gabonaise,Communiques officiels discours et agenda presidentiel.,https://www.presidence.ga/rss,11,Política,67,Gabón,fr,True,0 +6112,Senat Gabonais,Actualite du Senat textes legislatifs et institutions.,https://www.senat.ga/rss,11,Política,67,Gabón,fr,False,30 +6167,Agence Nationale des Infrastructures Numeriques,Infrastructures telecoms internet et digitalisation.,https://www.aninf.ga/rss,14,Tecnología,67,Gabón,fr,True,0 +6169,Airtel Gabon Technologie,Telecoms mobiles innovation et services digitaux.,https://www.airtel.gabon/rss,14,Tecnología,67,Gabón,fr,False,30 +6172,AllAfrica Gabon ICT,Actualite technologie et numerique gabonaise agregee.,https://allafrica.com/tools/headlines/rdf/gabon/ict.rdf,14,Tecnología,67,Gabón,fr,False,30 +6170,Autorite de Regulation des Communications Electroniques,Regulation telecoms internet et TIC.,https://www.arcep.ga/rss,14,Tecnología,67,Gabón,fr,False,30 +6168,Gabon Telecom News,Telecommunications reseaux et services numeriques.,https://www.gabontelecom.ga/rss,14,Tecnología,67,Gabón,fr,False,30 +6166,Ministere de l Economie Numerique Gabon,Politiques numeriques transformation digitale et TIC.,https://www.numerique.gouv.ga/rss,14,Tecnología,67,Gabón,fr,False,30 +6171,Smart Africa Gabon,Transformation numerique innovation et smart governance.,https://smartafrica.org/rss,14,Tecnología,67,Gabón,fr,False,30 +6208,ECOWAS Gambia News,Regional cooperation west africa diplomacy and policy.,https://ecowas.int/rss,7,Internacional,68,Gambia,en,True,0 +6210,United Nations Gambia,UN activities cooperation and international development.,https://www.un.org/rss,7,Internacional,68,Gambia,en,False,30 +6214,AllAfrica Gambia Environment,Aggregated environment and climate news related to Gambia.,https://allafrica.com/tools/headlines/rdf/gambia/environment.rdf,8,Medio Ambiente,68,Gambia,en,False,30 +2840,Gambia Department of Forestry,Forests biodiversity and land management updates.,https://www.forestry.gm/rss,8,Medio Ambiente,68,Gambia,en,False,30 +6212,Gambia National Climate Committee,Climate change policy mitigation and adaptation.,https://www.climatechange.gm/rss,8,Medio Ambiente,68,Gambia,en,False,30 +2838,Ministry of Environment Climate Change Gambia,Environmental policy climate action and sustainability.,https://meccn.gm/rss,8,Medio Ambiente,68,Gambia,en,False,30 +2839,National Environment Agency Gambia,Environmental regulation biodiversity and conservation.,https://nea.gm/rss,8,Medio Ambiente,68,Gambia,en,True,0 +6213,WWF Gambia Environment,Wildlife conservation climate and ecosystems.,https://www.worldwildlife.org/rss.xml,8,Medio Ambiente,68,Gambia,en,False,30 +2811,AllAfrica Gambia Politics,Aggregated political news and regional context.,https://allafrica.com/tools/headlines/rdf/gambia/headlines.rdf,11,Política,68,Gambia,en,True,0 +2802,Foroyaa Newspaper Politics,Independent political reporting democracy and governance.,https://foroyaa.net/feed/,11,Política,68,Gambia,en,True,0 +2806,Gambia Government News,Official government announcements and policy updates.,https://www.gambia.gov.gm/rss,11,Política,68,Gambia,en,False,30 +2804,Gambia Radio and Television Services News,Public broadcaster political and national affairs.,https://grts.gm/feed/,11,Política,68,Gambia,en,False,30 +2809,Independent Electoral Commission Gambia,Elections voter registration and political process updates.,https://iec.gm/rss,11,Política,68,Gambia,en,True,0 +2808,Ministry of Foreign Affairs Gambia,Foreign policy diplomacy and international relations.,https://mofa.gm/rss,11,Política,68,Gambia,en,False,30 +2807,National Assembly Gambia News,Legislative activity bills and parliamentary sessions.,https://www.assembly.gm/rss,11,Política,68,Gambia,en,False,30 +2805,Office of the President Gambia,Official presidential statements and political agenda.,https://op.gov.gm/rss,11,Política,68,Gambia,en,False,30 +2801,The Point Gambia Politics,Political news government and public affairs in Gambia.,https://thepoint.gm/feed/,11,Política,68,Gambia,en,False,30 +2803,The Standard Gambia Politics,Political developments parliament and elections.,https://standard.gm/feed/,11,Política,68,Gambia,en,True,0 +2810,Transparency International Gambia,Governance anti corruption and political accountability.,https://tig.gm/feed/,11,Política,68,Gambia,en,False,30 +6218,AllAfrica Gambia ICT,Aggregated technology and ICT news related to Gambia.,https://allafrica.com/tools/headlines/rdf/gambia/ict.rdf,14,Tecnología,68,Gambia,en,False,30 +6215,Gambia Information Technology Association,ICT industry innovation and digital skills news.,https://gita.gm/feed/,14,Tecnología,68,Gambia,en,False,30 +2863,Gamcel Technology,Mobile technology networks and digital services.,https://www.gamcel.gm/feed/,14,Tecnología,68,Gambia,en,True,0 +2862,Gamtel Technology News,Telecommunications networks broadband and digital services.,https://www.gamtel.gm/feed/,14,Tecnología,68,Gambia,en,True,0 +2860,Ministry of Communications and Digital Economy Gambia,Digital transformation ICT policy and technology programs.,https://mocde.gm/rss,14,Tecnología,68,Gambia,en,False,30 +2864,National Information Technology Agency Gambia,E government digital services and ICT infrastructure.,https://www.nita.gm/rss,14,Tecnología,68,Gambia,en,False,30 +6216,Smart Gambia Initiative,Digital government innovation and smart services.,https://smart.gm/rss,14,Tecnología,68,Gambia,en,False,30 +6217,UNDP Gambia Digital,Digital development innovation and technology projects.,https://www.undp.org/gambia/rss,14,Tecnología,68,Gambia,en,False,30 +2727,Agenda.ge Science,Science research health environment and innovation news in Georgia.,https://agenda.ge/en/rss,1,Ciencia,69,Georgia,en,False,30 +6174,AllAfrica Georgia Science,Aggregated science health and environment news.,https://allafrica.com/tools/headlines/rdf/georgia/health.rdf,1,Ciencia,69,Georgia,en,False,30 +2728,Civil Georgia Science,Science policy research health and environment analysis.,https://civil.ge/feed/,1,Ciencia,69,Georgia,en,True,0 +2734,Georgian National Academy of Sciences,Scientific research programs and publications.,https://www.gnas.org.ge/rss,1,Ciencia,69,Georgia,en,False,30 +2729,Georgian Public Broadcaster Science,Science health environment and education reporting.,https://1tv.ge/lang/en/feed/,1,Ciencia,69,Georgia,en,False,30 +2730,Ilia State University Research News,Academic research science innovation and education.,https://iliauni.edu.ge/en/feed/,1,Ciencia,69,Georgia,en,True,0 +2731,Ivane Javakhishvili Tbilisi State University Science,University research science and higher education news.,https://www.tsu.ge/rss,1,Ciencia,69,Georgia,en,False,30 +6173,Ministry of Environmental Protection Georgia,Environmental science climate and biodiversity news.,https://www.mepa.gov.ge/rss,1,Ciencia,69,Georgia,en,False,30 +2732,National Center for Disease Control Georgia,Public health epidemiology and scientific reports.,https://ncdc.ge/rss,1,Ciencia,69,Georgia,en,False,30 +2736,UNICEF Georgia Research,Health education and social research insights.,https://www.unicef.org/georgia/rss.xml,1,Ciencia,69,Georgia,en,False,30 +2735,World Health Organization Georgia,Public health science research and reports.,https://www.who.int/feeds/entity/countries/geo/en/rss.xml,1,Ciencia,69,Georgia,en,False,30 +2747,AllAfrica Georgia Arts,Culture arts and heritage news aggregation.,https://allafrica.com/tools/headlines/rdf/georgia/arts.rdf,2,Cultura,69,Georgia,en,False,30 +2745,Georgian National Opera and Ballet,Culture opera ballet and performing arts news.,https://opera.ge/rss,2,Cultura,69,Georgia,en,False,30 +2743,Ministry of Culture Georgia,Official cultural policy arts and heritage updates.,https://culture.gov.ge/rss,2,Cultura,69,Georgia,en,False,30 +2742,National Museum of Georgia News,Cultural heritage museums exhibitions and history.,https://museum.ge/rss,2,Cultura,69,Georgia,en,False,30 +6186,Tbilisi International Film Festival News,Cinema culture film festivals and creative industry.,https://tbilisifilmfestival.ge/feed/,2,Cultura,69,Georgia,en,True,0 +2746,UNESCO Georgia Culture,Cultural heritage education and preservation programs.,https://www.unesco.org/en/countries/georgia/rss,2,Cultura,69,Georgia,en,False,30 +2726,AllAfrica Georgia Business,Aggregated economic and business news related to Georgia.,https://allafrica.com/tools/headlines/rdf/georgia/business.rdf,4,Economía,69,Georgia,en,False,30 +6184,Bank of Georgia News,Corporate banking finance and economic outlook.,https://bankofgeorgia.ge/rss,4,Economía,69,Georgia,en,False,30 +6185,EBRD Georgia Economy,Investment finance and economic transition news.,https://www.ebrd.com/rss.xml,4,Economía,69,Georgia,en,False,30 +6182,Enterprise Georgia News,Investment promotion export and SME development.,https://enterprisegeorgia.gov.ge/rss,4,Economía,69,Georgia,en,False,30 +6178,Forbes Georgia Business,Business economy startups and corporate news.,https://forbes.ge/feed/,4,Economía,69,Georgia,ka,True,0 +6177,Georgia Today Economy,Business economy investment and market developments.,https://georgiatoday.ge/feed/,4,Economía,69,Georgia,en,False,30 +6181,Georgian Statistical Office,Official economic statistics indicators and reports.,https://www.geostat.ge/rss,4,Economía,69,Georgia,en,False,30 +6180,Ministry of Economy Georgia,Trade industry energy and economic policy announcements.,https://www.economy.ge/rss,4,Economía,69,Georgia,en,False,30 +6179,National Bank of Georgia News,Monetary policy banking and financial stability updates.,https://www.nbg.gov.ge/rss,4,Economía,69,Georgia,en,False,30 +6183,TBC Bank Research,Economic research banking and financial sector insights.,https://www.tbcbank.com/rss,4,Economía,69,Georgia,en,False,30 +2724,World Bank Georgia Economy,Development projects economic reports and analysis.,https://www.worldbank.org/en/country/georgia/rss.xml,4,Economía,69,Georgia,en,False,30 +6196,AllAfrica Georgia International,Aggregated international news related to Georgia.,https://allafrica.com/tools/headlines/rdf/georgia/headlines.rdf,7,Internacional,69,Georgia,en,False,30 +6193,European Union Delegation to Georgia,EU Georgia relations cooperation and diplomacy.,https://www.eeas.europa.eu/delegations/georgia/rss_en,7,Internacional,69,Georgia,en,False,30 +6192,Georgian Institute of Politics Foreign Affairs,International security democracy and foreign policy analysis.,https://gip.ge/feed/,7,Internacional,69,Georgia,en,True,0 +6189,InterpressNews International,International politics diplomacy and regional affairs.,https://www.interpressnews.ge/en/rss,7,Internacional,69,Georgia,en,False,30 +6190,Ministry of Foreign Affairs Georgia,Official diplomacy statements treaties and foreign relations.,https://mfa.gov.ge/rss,7,Internacional,69,Georgia,en,False,30 +6194,NATO Georgia News,Security cooperation and international defense relations.,https://www.nato.int/rss/news_georgia.xml,7,Internacional,69,Georgia,en,False,30 +6195,OSCE Georgia Updates,International monitoring security and cooperation.,https://www.osce.org/rss,7,Internacional,69,Georgia,en,False,30 +6191,President of Georgia International,Foreign visits diplomacy and international statements.,https://president.gov.ge/rss,7,Internacional,69,Georgia,en,False,30 +2768,AllAfrica Georgia Opinion,Aggregated opinion editorials related to Georgia.,https://allafrica.com/tools/headlines/rdf/georgia/opinion.rdf,10,Opinión,69,Georgia,en,False,30 +2766,Chatham House Georgia Analysis,Policy opinion and analysis on Georgia and region.,https://www.chathamhouse.org/rss,10,Opinión,69,Georgia,en,False,30 +2767,ECFR Georgia Commentary,European foreign policy opinion and analysis on Georgia.,https://ecfr.eu/rss/,10,Opinión,69,Georgia,en,True,0 +6198,Netgazeti Opinion,Investigative opinion columns and civic debate.,https://netgazeti.ge/feed/,10,Opinión,69,Georgia,en,True,0 +2761,Tabula Opinion,Opinion essays political debate and social commentary.,https://tabula.ge/en/rss,10,Opinión,69,Georgia,en,False,30 +2764,Transparency International Georgia Opinion,Governance anti corruption and policy opinion analysis.,https://transparency.ge/en/rss.xml,10,Opinión,69,Georgia,en,True,0 +2781,AllAfrica Georgia ICT,Aggregated technology and ICT news related to Georgia.,https://allafrica.com/tools/headlines/rdf/georgia/ict.rdf,14,Tecnología,69,Georgia,en,False,30 +6200,Georgian Research and Education Network,ICT infrastructure research and digital services.,https://www.grena.ge/rss,14,Tecnología,69,Georgia,en,False,30 +2774,Innovation and Technology Agency of Georgia,Gov supported innovation startups and technology news.,https://gita.gov.ge/rss,14,Tecnología,69,Georgia,en,False,30 +6203,MagtiCom Technology,Mobile networks telecom technology and innovation.,https://www.magticom.ge/rss,14,Tecnología,69,Georgia,en,False,30 +6201,National Communications Commission Georgia,Telecom regulation internet and digital services.,https://www.comcom.ge/rss,14,Tecnología,69,Georgia,en,False,30 +6202,Silknet Technology News,Telecommunications broadband and digital services.,https://www.silknet.com/rss,14,Tecnología,69,Georgia,en,False,30 +2776,Startup Georgia News,Startups technology venture capital and innovation.,https://startupgeorgia.gov.ge/rss,14,Tecnología,69,Georgia,en,False,30 +6199,Tech Park Georgia,Startup ecosystem technology innovation and entrepreneurship.,https://techpark.ge/feed/,14,Tecnología,69,Georgia,en,False,30 +6246,AllAfrica Ghana Science,Aggregated science health and environment news.,https://allafrica.com/tools/headlines/rdf/ghana/health.rdf,1,Ciencia,70,Ghana,en,False,30 +2936,CSIR Ghana Research,Scientific research agriculture health and environment.,https://csir.org.gh/rss,1,Ciencia,70,Ghana,en,False,30 +6243,Ghana Health Service Research,Public health science epidemiology and studies.,https://www.ghanahealthservice.org/rss,1,Ciencia,70,Ghana,en,False,30 +2931,Graphic Science and Health,Science health research and environment reporting.,https://www.graphic.com.gh/rss/health.xml,1,Ciencia,70,Ghana,en,False,30 +6242,Kwame Nkrumah University of Science and Technology,Research science engineering and technology news.,https://www.knust.edu.gh/rss,1,Ciencia,70,Ghana,en,False,30 +2937,Ministry of Environment Science Technology Innovation,Science policy environment and innovation updates.,https://mesti.gov.gh/rss,1,Ciencia,70,Ghana,en,False,30 +6245,UNICEF Ghana Research,Health education and social research insights.,https://www.unicef.org/ghana/rss.xml,1,Ciencia,70,Ghana,en,False,30 +6241,University of Ghana Research News,Academic research science innovation and health.,https://www.ug.edu.gh/rss,1,Ciencia,70,Ghana,en,False,30 +6244,World Health Organization Ghana,Public health science research and reports.,https://www.who.int/feeds/entity/countries/gha/en/rss.xml,1,Ciencia,70,Ghana,en,False,30 +6240,AllAfrica Ghana Business,Aggregated economic and business news related to Ghana.,https://allafrica.com/tools/headlines/rdf/ghana/business.rdf,4,Economía,70,Ghana,en,False,30 +6236,Bank of Ghana News,Monetary policy banking regulation and financial stability.,https://www.bog.gov.gh/rss,4,Economía,70,Ghana,en,False,30 +6231,Citi Business News,Economic analysis markets banking and fiscal policy.,https://citibusinessnews.com/feed/,4,Economía,70,Ghana,en,True,0 +6238,Ghana Investment Promotion Centre,Investment climate trade and economic development.,https://gipc.gov.gh/rss,4,Economía,70,Ghana,en,True,0 +6237,Ghana Statistical Service,Economic statistics inflation GDP and employment data.,https://statsghana.gov.gh/rss,4,Economía,70,Ghana,en,False,30 +6232,GhanaWeb Business,Economic indicators markets and business reporting.,https://www.ghanaweb.com/rss/business.xml,4,Economía,70,Ghana,en,False,30 +2916,Graphic Business,Business economy finance and corporate news.,https://www.graphic.com.gh/rss/business.xml,4,Economía,70,Ghana,en,False,30 +2928,IMF Ghana News,Macroeconomic policy finance and reform programs.,https://www.imf.org/en/Countries/GHA/rss,4,Economía,70,Ghana,en,False,30 +6235,Ministry of Finance Ghana,Public finance budget fiscal policy and debt management.,https://mofep.gov.gh/rss,4,Economía,70,Ghana,en,False,30 +2919,Peace FM Business,Business trade economy and entrepreneurship news.,https://www.peacefmonline.com/rss/business.xml,4,Economía,70,Ghana,en,False,30 +6239,World Bank Ghana Economy,Development projects economic reports and analysis.,https://www.worldbank.org/en/country/ghana/rss.xml,4,Economía,70,Ghana,en,False,30 +2962,GhanaWeb World,International news global economy and diplomacy.,https://www.ghanaweb.com/rss/world.xml,7,Internacional,70,Ghana,en,False,30 +6248,Graphic Online International,World news diplomacy and global affairs coverage.,https://www.graphic.com.gh/rss/world.xml,7,Internacional,70,Ghana,en,False,30 +2961,Peace FM World News,International politics diplomacy and world events.,https://www.peacefmonline.com/rss/world.xml,7,Internacional,70,Ghana,en,False,30 +6230,AllAfrica Ghana Politics,Aggregated political news and regional context.,https://allafrica.com/tools/headlines/rdf/ghana/headlines.rdf,11,Política,70,Ghana,en,True,0 +6222,Citi Newsroom Politics,Political analysis governance and accountability.,https://citinewsroom.com/feed/,11,Política,70,Ghana,en,False,30 +6225,Daily Guide Politics,Political reporting government and party politics.,https://dailyguidenetwork.com/feed/,11,Política,70,Ghana,en,True,0 +6229,Electoral Commission of Ghana,Elections voter registration and political process.,https://ec.gov.gh/rss,11,Política,70,Ghana,en,True,0 +6219,Ghana News Agency Politics,Official political news government and public affairs in Ghana.,https://gna.org.gh/feed/,11,Política,70,Ghana,en,False,30 +6224,GhanaWeb Politics,Political news elections and government actions.,https://www.ghanaweb.com/rss/politics.xml,11,Política,70,Ghana,en,False,30 +6227,Government of Ghana News,Official government announcements and policy updates.,https://www.ghana.gov.gh/rss,11,Política,70,Ghana,en,False,30 +6220,Graphic Online Politics,Political news governance elections and public policy.,https://www.graphic.com.gh/rss/politics.xml,11,Política,70,Ghana,en,False,30 +6228,Ministry of Foreign Affairs Ghana,Foreign policy diplomacy and international relations.,https://mfa.gov.gh/rss,11,Política,70,Ghana,en,False,30 +6221,MyJoyOnline Politics,Political developments parliament and elections.,https://www.myjoyonline.com/feed/,11,Política,70,Ghana,en,True,0 +2909,Office of the President Ghana,Official presidential statements and political agenda.,https://www.presidency.gov.gh/rss,11,Política,70,Ghana,en,True,0 +2911,Parliament of Ghana News,Legislative activity bills and parliamentary sessions.,https://www.parliament.gh/rss,11,Política,70,Ghana,en,False,30 +6223,Peace FM Politics,Political commentary interviews and election coverage.,https://www.peacefmonline.com/rss/politics.xml,11,Política,70,Ghana,en,False,30 +6226,The Chronicle Politics,Political analysis governance and institutions.,https://thechronicle.com.gh/feed/,11,Política,70,Ghana,en,True,0 +6258,AllAfrica Ghana ICT,Aggregated technology and ICT news related to Ghana.,https://allafrica.com/tools/headlines/rdf/ghana/ict.rdf,14,Tecnología,70,Ghana,en,False,30 +6253,Ghana Tech Lab,Startup ecosystem innovation technology and skills.,https://ghanatechlab.com/feed/,14,Tecnología,70,Ghana,en,False,30 +6249,GhanaWeb Technology,Technology ICT startups and digital services reporting.,https://www.ghanaweb.com/rss/technology.xml,14,Tecnología,70,Ghana,en,False,30 +2996,Graphic Online Technology,Technology innovation startups and digital economy news.,https://www.graphic.com.gh/rss/technology.xml,14,Tecnología,70,Ghana,en,False,30 +6256,MTN Ghana Technology,Telecommunications networks digital services and innovation.,https://mtn.com.gh/rss,14,Tecnología,70,Ghana,en,True,0 +6251,Ministry of Communications and Digitalisation Ghana,Digital policy ICT transformation and technology programs.,https://moc.gov.gh/rss,14,Tecnología,70,Ghana,en,True,0 +6254,Mobile Web Ghana,Digital inclusion technology training and innovation.,https://mobilewebghana.org/feed/,14,Tecnología,70,Ghana,en,False,30 +6252,National Information Technology Agency Ghana,E government digital services and ICT infrastructure.,https://nita.gov.gh/rss,14,Tecnología,70,Ghana,en,True,0 +6250,TechNovaGH,Technology startups innovation and entrepreneurship in Ghana.,https://technovagh.com/feed/,14,Tecnología,70,Ghana,en,True,0 +6257,Vodafone Ghana Technology,Telecom technology networks and digital services.,https://vodafone.com.gh/rss,14,Tecnología,70,Ghana,en,False,30 +6289,AllAfrica Grenada Science,Aggregated science health and environment news.,https://allafrica.com/tools/headlines/rdf/grenada/health.rdf,1,Ciencia,71,Granada,en,False,30 +6286,Caribbean Public Health Agency Grenada,Regional public health science and research.,https://carpha.org/rss,1,Ciencia,71,Granada,en,False,30 +6285,Grenada Bureau of Standards and Science,Scientific standards research and quality control.,https://gdbs.gd/rss,1,Ciencia,71,Granada,en,False,30 +6284,Ministry of Climate Resilience Grenada,Climate science environment and sustainability policy.,https://climate.gov.gd/rss,1,Ciencia,71,Granada,en,False,30 +6282,Ministry of Health Grenada,Public health science research and epidemiology updates.,https://health.gov.gd/rss,1,Ciencia,71,Granada,en,False,30 +6283,St Georges University Research,Medical science research education and innovation.,https://www.sgu.edu/feed/,1,Ciencia,71,Granada,en,True,0 +6287,World Health Organization Grenada,Public health science research and reports.,https://www.who.int/feeds/entity/countries/grd/en/rss.xml,1,Ciencia,71,Granada,en,False,30 +6278,AllAfrica Grenada Business,Aggregated economic and business news related to Grenada.,https://allafrica.com/tools/headlines/rdf/grenada/business.rdf,4,Economía,71,Granada,en,False,30 +6275,Caribbean Development Bank Grenada,Development finance economic projects and reports.,https://www.caribank.org/rss,4,Economía,71,Granada,en,False,30 +6273,Eastern Caribbean Central Bank Grenada,Monetary policy banking and financial stability.,https://www.eccb-centralbank.org/rss,4,Economía,71,Granada,en,False,30 +6274,Grenada Bureau of Statistics,Official economic statistics GDP inflation and labor.,https://stats.gov.gd/rss,4,Economía,71,Granada,en,True,0 +6271,Grenada Investment Development Corporation,Investment promotion trade and economic development.,https://www.gidc.gd/feed/,4,Economía,71,Granada,en,True,0 +6272,Grenada Tourism Authority Economy,Tourism industry economic impact and investment.,https://www.puregrenada.com/feed/,4,Economía,71,Granada,en,True,0 +6277,IMF Grenada Updates,Macroeconomic policy finance and reform programs.,https://www.imf.org/en/Countries/GRD/rss,4,Economía,71,Granada,en,False,30 +6270,Ministry of Finance Grenada,Public finance budget fiscal policy and debt management.,https://finance.gov.gd/rss,4,Economía,71,Granada,en,False,30 +6276,World Bank Grenada Economy,Development projects economic reports and analysis.,https://www.worldbank.org/en/country/grenada/rss.xml,4,Economía,71,Granada,en,False,30 +6269,AllAfrica Grenada Politics,Aggregated political news related to Grenada.,https://allafrica.com/tools/headlines/rdf/grenada/headlines.rdf,11,Política,71,Granada,en,False,30 +6267,Caribbean Community CARICOM Grenada,Regional politics and Caribbean integration news.,https://caricom.org/rss,11,Política,71,Granada,en,True,0 +6259,Grenada Broadcasting Network Politics,Political news government and public affairs in Grenada.,https://gbn.gd/feed/,11,Política,71,Granada,en,True,0 +6262,Grenada Chronicle Politics,Political reporting government parliament and parties.,https://grenadachronicle.com/feed/,11,Política,71,Granada,en,True,0 +6264,Grenada Government News,Official government announcements and political decisions.,https://www.gov.gd/rss,11,Política,71,Granada,en,False,30 +6261,Grenada Informer Politics,Political commentary public policy and national affairs.,https://grenadainformer.com/feed/,11,Política,71,Granada,en,False,30 +6266,Ministry of Foreign Affairs Grenada,Foreign policy diplomacy and international relations.,https://foreign.gov.gd/rss,11,Política,71,Granada,en,False,30 +6268,OECS Grenada,Regional political cooperation eastern caribbean.,https://www.oecs.org/rss,11,Política,71,Granada,en,False,30 +6263,Office of the Prime Minister Grenada,Official statements political agenda and policy updates.,https://opm.gov.gd/rss,11,Política,71,Granada,en,False,30 +6265,Parliament of Grenada News,Legislative activity bills and parliamentary sessions.,https://www.parliament.gd/rss,11,Política,71,Granada,en,False,30 +6260,The New Today Politics,Political developments governance and elections in Grenada.,https://thenewtoday.gd/feed/,11,Política,71,Granada,en,True,0 +3242,AllAfrica Greece Science,Aggregated science health and environment news related to Greece.,https://allafrica.com/tools/headlines/rdf/greece/health.rdf,1,Ciencia,72,Grecia,en,False,30 +3239,Hellenic Center for Disease Control,Public health science epidemiology and research.,https://eody.gov.gr/rss,1,Ciencia,72,Grecia,en,False,30 +6314,Kathimerini Greek Science,Actualite science sante environnement et recherche en Grece.,https://www.kathimerini.gr/rss/epistimi,1,Ciencia,72,Grecia,el,False,30 +6313,Kathimerini Science,Science research health environment and innovation news in Greece.,https://www.ekathimerini.com/feed/?cat=science,1,Ciencia,72,Grecia,en,False,30 +6319,National Observatory of Athens,Scientific research climate astronomy and environment.,https://www.noa.gr/rss,1,Ciencia,72,Grecia,en,False,30 +3237,National Technical University of Athens Research,Engineering science research and innovation.,https://www.ntua.gr/rss,1,Ciencia,72,Grecia,en,False,30 +6317,Proto Thema Science,Science health environment and technology news.,https://www.protothema.gr/rss/epistimi,1,Ciencia,72,Grecia,el,False,30 +6316,Ta Nea Science,Science health environment and public research news.,https://www.tanea.gr/feed/?cat=science,1,Ciencia,72,Grecia,el,True,0 +6315,To Vima Science,Science health environment and research reporting.,https://www.tovima.gr/feed/?cat=science,1,Ciencia,72,Grecia,el,True,0 +3241,UNESCO Greece Science,Science education research and heritage programs.,https://www.unesco.org/en/countries/greece/rss,1,Ciencia,72,Grecia,en,False,30 +3238,University of Athens Research,Academic research science medicine and health.,https://www.uoa.gr/rss,1,Ciencia,72,Grecia,en,False,30 +3240,World Health Organization Greece,Public health science research and reports.,https://www.who.int/feeds/entity/countries/grc/en/rss.xml,1,Ciencia,72,Grecia,en,False,30 +6312,AllAfrica Greece Business,Aggregated economic and business news related to Greece.,https://allafrica.com/tools/headlines/rdf/greece/business.rdf,4,Economía,72,Grecia,en,False,30 +6307,Bank of Greece News,Monetary policy banking and financial stability.,https://www.bankofgreece.gr/rss,4,Economía,72,Grecia,en,False,30 +3219,Capital.gr Economy,Business economy finance and markets news.,https://www.capital.gr/rss,4,Economía,72,Grecia,el,False,30 +6309,Enterprise Greece,Investment promotion trade and economic development.,https://www.enterprisegreece.gov.gr/rss,4,Economía,72,Grecia,en,False,30 +6308,Hellenic Statistical Authority,Official economic statistics GDP inflation employment.,https://www.statistics.gr/rss,4,Economía,72,Grecia,en,False,30 +6311,IMF Greece Updates,Macroeconomic policy finance and reform programs.,https://www.imf.org/en/Countries/GRC/rss,4,Economía,72,Grecia,en,False,30 +6300,Kathimerini Economy,Economic news markets fiscal policy and business in Greece.,https://www.ekathimerini.com/feed/?cat=economy,4,Economía,72,Grecia,en,False,30 +6301,Kathimerini Greek Economy,Actualite economique finances et entreprises en Grece.,https://www.kathimerini.gr/rss/oikonomia,4,Economía,72,Grecia,el,False,30 +6306,Ministry of Finance Greece,Public finance budget taxation and fiscal policy.,https://www.mof.gov.gr/rss,4,Economía,72,Grecia,en,False,30 +3220,Naftemporiki Economy,Economy shipping finance and markets analysis.,https://www.naftemporiki.gr/rss,4,Economía,72,Grecia,el,True,0 +6304,Proto Thema Economy,Economic news business markets and fiscal policy.,https://www.protothema.gr/rss/oikonomia,4,Economía,72,Grecia,el,False,30 +6303,Ta Nea Economy,Business economy banking and market developments.,https://www.tanea.gr/feed/?cat=economy,4,Economía,72,Grecia,el,True,0 +6302,To Vima Economy,Economic policy markets and public finance reporting.,https://www.tovima.gr/feed/?cat=economy,4,Economía,72,Grecia,el,True,0 +6310,World Bank Greece Economy,Development projects economic reports and analysis.,https://www.worldbank.org/en/country/greece/rss.xml,4,Economía,72,Grecia,en,False,30 +3244,Kathimerini Greek International,Actualite internationale diplomatie et affaires mondiales.,https://www.kathimerini.gr/rss/diethni,7,Internacional,72,Grecia,el,False,30 +3243,Kathimerini International,International relations diplomacy and global affairs from Greece.,https://www.ekathimerini.com/feed/?cat=international,7,Internacional,72,Grecia,en,False,30 +3253,NATO Greece News,Security cooperation and international defense relations.,https://www.nato.int/rss/news_greece.xml,7,Internacional,72,Grecia,en,False,30 +6323,Proto Thema International,International news global politics and diplomacy.,https://www.protothema.gr/rss/diethni,7,Internacional,72,Grecia,el,False,30 +6322,Ta Nea International,World affairs diplomacy and international politics.,https://www.tanea.gr/feed/?cat=international,7,Internacional,72,Grecia,el,True,0 +6321,To Vima International,International politics diplomacy and global events.,https://www.tovima.gr/feed/?cat=international,7,Internacional,72,Grecia,el,True,0 +3213,AllAfrica Greece Politics,Aggregated political news related to Greece.,https://allafrica.com/tools/headlines/rdf/greece/headlines.rdf,11,Política,72,Grecia,en,False,30 +3212,European Commission Greece,EU policy and Greece related political news.,https://greece.representation.ec.europa.eu/rss_en,11,Política,72,Grecia,en,False,30 +6296,Hellenic Parliament News,Legislative activity bills and parliamentary sessions.,https://www.hellenicparliament.gr/rss,11,Política,72,Grecia,en,False,30 +6290,Kathimerini Greek Politics,Actualite politique gouvernement et parlement en Grece.,https://www.kathimerini.gr/rss,11,Política,72,Grecia,el,False,30 +3268,Kathimerini Greek Politics,Actualite politique gouvernement et parlement en Grece.,https://www.kathimerini.gr/rss/politiki,11,Política,72,Grecia,el,False,30 +3201,Kathimerini Politics,Political news government policy and analysis in Greece.,https://www.ekathimerini.com/feed/,11,Política,72,Grecia,en,False,30 +6299,Ministry of Foreign Affairs Greece,Foreign policy diplomacy and international relations.,https://www.mfa.gr/rss,11,Política,72,Grecia,en,False,30 +6297,Prime Minister of Greece News,Official statements policy and political agenda.,https://www.primeminister.gr/rss,11,Política,72,Grecia,en,False,30 +6325,Proto Thema Politics,Political news analysis and government affairs.,https://www.protothema.gr/rss/politiki,11,Política,72,Grecia,el,False,30 +6293,Proto Thema Politics,Political news analysis and government affairs.,https://www.protothema.gr/rss,11,Política,72,Grecia,el,True,0 +6324,Ta Nea Politics,Political reporting parties parliament and government.,https://www.tanea.gr/feed/?cat=politics,11,Política,72,Grecia,el,True,0 +6292,Ta Nea Politics,Political reporting parties parliament and government.,https://www.tanea.gr/feed/,11,Política,72,Grecia,el,True,0 +6291,To Vima Politics,Political developments elections and public policy.,https://www.tovima.gr/feed/,11,Política,72,Grecia,el,True,0 +3269,To Vima Politics,Political developments elections and public policy.,https://www.tovima.gr/feed/?cat=politics,11,Política,72,Grecia,el,True,0 +6333,AllAfrica Greece Society,Aggregated social affairs news related to Greece.,https://allafrica.com/tools/headlines/rdf/greece/society.rdf,13,Sociedad,72,Grecia,en,False,30 +6327,Kathimerini Greek Society,Koinonia themata metanastefsi paideia kai koinoniki zoi.,https://www.kathimerini.gr/rss/koinonia,13,Sociedad,72,Grecia,el,False,30 +6326,Kathimerini Society,Social issues migration education and public life in Greece.,https://www.ekathimerini.com/feed/?cat=society,13,Sociedad,72,Grecia,en,False,30 +3287,Ministry of Social Cohesion Greece,Social policy welfare inclusion and family support.,https://www.socialpolicy.gov.gr/rss,13,Sociedad,72,Grecia,en,False,30 +3288,National Commission for Human Rights Greece,Human rights equality and social justice updates.,https://www.nchr.gr/rss,13,Sociedad,72,Grecia,en,False,30 +6330,Proto Thema Society,Social topics justice education and daily life.,https://www.protothema.gr/rss/koinonia,13,Sociedad,72,Grecia,el,False,30 +6329,Ta Nea Society,Social news welfare education and community issues.,https://www.tanea.gr/feed/?cat=society,13,Sociedad,72,Grecia,el,True,0 +6328,To Vima Society,Social affairs education health and public debate.,https://www.tovima.gr/feed/?cat=society,13,Sociedad,72,Grecia,el,True,0 +3289,UNHCR Greece,Refugees migration and social integration news.,https://www.unhcr.org/gr/rss.xml,13,Sociedad,72,Grecia,en,False,30 +3290,UNICEF Greece,Child welfare education and social development.,https://www.unicef.org/greece/rss.xml,13,Sociedad,72,Grecia,en,False,30 +3304,AllAfrica Greece ICT,Aggregated technology and ICT news related to Greece.,https://allafrica.com/tools/headlines/rdf/greece/ict.rdf,14,Tecnología,72,Grecia,en,False,30 +6344,Demokritos National Centre for Scientific Research,Research technology science and innovation.,https://www.demokritos.gr/feed/,14,Tecnología,72,Grecia,en,False,30 +6295,ERT News Technology,Public broadcaster technology science and innovation.,https://www.ertnews.gr/feed/,14,Tecnología,72,Grecia,el,True,0 +6320,Foundation for Research and Technology Hellas,Advanced research technology and innovation news.,https://www.forth.gr/feed/,14,Tecnología,72,Grecia,en,False,30 +6294,Greek Reporter Technology,Technology innovation startups and digital economy.,https://greekreporter.com/feed/,14,Tecnología,72,Grecia,en,True,0 +6345,Hellenic Telecommunications Organization,Telecom networks digital services and innovation.,https://www.cosmote.gr/rss,14,Tecnología,72,Grecia,en,False,30 +6335,Kathimerini Greek Technology,Technologia kainotomia kai psifiaki oikonomia stin Ellada.,https://www.kathimerini.gr/rss/technologia,14,Tecnología,72,Grecia,el,False,30 +6334,Kathimerini Technology,Technology innovation digital policy and startups in Greece.,https://www.ekathimerini.com/feed/?cat=technology,14,Tecnología,72,Grecia,en,False,30 +6298,Ministry of Digital Governance Greece,Digital government policy e services and innovation.,https://www.gov.gr/rss,14,Tecnología,72,Grecia,en,False,30 +6342,National Documentation Centre Greece,Research technology innovation and open data.,https://www.ekt.gr/rss,14,Tecnología,72,Grecia,en,False,30 +6338,Proto Thema Technology,Technology gadgets innovation and digital trends.,https://www.protothema.gr/rss/technology,14,Tecnología,72,Grecia,el,False,30 +6337,Ta Nea Technology,Technology startups internet and digital transformation.,https://www.tanea.gr/feed/?cat=technology,14,Tecnología,72,Grecia,el,True,0 +6336,To Vima Technology,Technology innovation research and digital society.,https://www.tovima.gr/feed/?cat=technology,14,Tecnología,72,Grecia,el,True,0 +6362,Accion Ciudadana Politica,Analisis politico anticorrupcion y democracia.,https://accionciudadana.org/feed/,11,Política,73,Guatemala,es,False,30 +6366,AllAfrica Guatemala Politics Extra,Agregador de noticias politicas de Guatemala.,https://allafrica.com/tools/headlines/rdf/guatemala/politics.rdf,11,Política,73,Guatemala,es,False,30 +6359,CRN Noticias Politica,Noticias politicas justicia y gobierno.,https://crnnoticias.com/feed/,11,Política,73,Guatemala,es,True,0 +6358,Canal Antigua Politica,Cobertura politica analisis y opinion nacional.,https://canalantigua.tv/feed/,11,Política,73,Guatemala,es,False,30 +6355,Diario de Centro America Politica,Noticias politicas oficiales y gestion del Estado.,https://dca.gob.gt/feed/,11,Política,73,Guatemala,es,False,30 +6364,Fundacion Libertad y Desarrollo Politica,Analisis politico institucional y democracia.,https://www.fundesa.org.gt/feed/,11,Política,73,Guatemala,es,False,30 +6361,Guatemala Visible Politica,Transparencia democracia y control politico.,https://guatemalavisible.com/feed/,11,Política,73,Guatemala,es,False,30 +6357,Guatevision Politica,Noticias politicas nacionales entrevistas y analisis.,https://www.guatevision.com/feed/,11,Política,73,Guatemala,es,True,0 +6363,ICEFI Guatemala Politica,Analisis politico fiscal y politica publica.,https://icefi.org/feed/,11,Política,73,Guatemala,es,False,30 +6365,InSight Crime Guatemala,Analisis politico crimen organizado y seguridad.,https://insightcrime.org/feed/,11,Política,73,Guatemala,es,True,0 +6360,Nuestro Diario Politica,Actualidad politica nacional y social.,https://www.nuestrodiario.com/feed/,11,Política,73,Guatemala,es,False,30 +6356,Soy502 Politica,Actualidad politica gobierno justicia y coyuntura.,https://www.soy502.com/rss,11,Política,73,Guatemala,es,False,30 +6371,ConakryInfos Internacional,Actualidad internacional regional y africana.,https://conakryinfos.com/feed,7,Internacional,74,Guinea,fr,True,0 +6373,GuineeNews Opinion,Analisis opinion politica y social.,https://guineenews.org/feed,10,Opinión,74,Guinea,fr,True,0 +6370,AfricaGuinee Politica,Actualidad politica gobierno y administracion publica.,https://www.africaguinee.com/feed,11,Política,74,Guinea,fr,True,0 +6369,Aminata Salud,Informacion sanitaria salud publica y bienestar.,https://aminata.com/feed,12,Salud,74,Guinea,fr,True,0 +6367,Guinee360 Sociedad,Noticias sociales justicia poblacion y comunidad.,https://www.guinee360.com/feed,13,Sociedad,74,Guinea,fr,True,0 +6368,Guinee7 Tecnologia,Innovacion tecnologia digital y telecomunicaciones.,https://www.guinee7.com/feed,14,Tecnología,74,Guinea,fr,True,0 +6372,LeJourGuinee Viajes,Turismo viajes y descubrimiento nacional.,https://lejourguinee.com/feed,15,Viajes,74,Guinea,fr,True,0 +6383,AllAfrica Guinea Ecuatorial,Agregador de noticias nacionales e internacionales.,https://allafrica.com/tools/headlines/rdf/equatorial_guinea/headlines.rdf,7,Internacional,76,Guinea Ecuatorial,en,False,30 +6381,Asodegue Internacional,Actualidad internacional derechos humanos y diplomacia.,https://asodegue.org/feed/,7,Internacional,76,Guinea Ecuatorial,es,False,30 +6382,Radio Bata Sociedad,Actualidad social cultura y vida cotidiana.,https://www.radiobata.org/feed/,13,Sociedad,76,Guinea Ecuatorial,es,False,30 +6380,Ahora EG Tecnologia,Actualidad tecnologica digitalizacion y comunicaciones.,https://ahoraeg.com/feed/,14,Tecnología,76,Guinea Ecuatorial,es,True,0 +6374,ANG Politica,Agencia nacional con informacion politica y gubernamental.,https://www.ang.gw/feed,11,Política,75,Guinea-Bisáu,pt,True,0 +6376,AllAfrica Guinea Bisau Politica,Agregador de noticias politicas nacionales.,https://allafrica.com/tools/headlines/rdf/guinea_bissau/headlines.rdf,11,Política,75,Guinea-Bisáu,en,False,30 +6375,GuineBissau News Politica,Noticias politicas gobierno y estabilidad institucional.,https://www.guineabissau.com/feed,11,Política,75,Guinea-Bisáu,en,False,30 +6379,O Democrata Politica,Analisis politico partidos y gobierno.,https://odemocratagb.com/feed,11,Política,75,Guinea-Bisáu,pt,False,30 +6378,RTGB Politica,Noticias politicas del ente publico de radiodifusion.,https://www.rtgb.gw/feed,11,Política,75,Guinea-Bisáu,pt,False,30 +6377,Radio Jovem Politica,Actualidad politica entrevistas y debate publico.,https://radiojovem.gw/feed,11,Política,75,Guinea-Bisáu,pt,False,30 +6400,University of Guyana News,Academic research education and science.,https://uog.edu.gy/feed/,1,Ciencia,77,Guyana,en,False,30 +6391,Guyana Times Deportes,Sports cricket football and athletics.,https://guyanatimesgy.com/feed/,3,Deportes,77,Guyana,en,True,0 +6399,Guyana Lands and Surveys Commission,Land management planning and development.,https://glsc.gov.gy/feed/,4,Economía,77,Guyana,en,True,0 +6385,Kaieteur News Economia,Economy business energy and finance news.,https://www.kaieteurnewsonline.com/feed/,4,Economía,77,Guyana,en,True,0 +6390,AllAfrica Guyana,Aggregated national and international news.,https://allafrica.com/tools/headlines/rdf/guyana/headlines.rdf,7,Internacional,77,Guyana,en,False,30 +6392,DPI Guyana Internacional,Foreign affairs diplomacy and regional relations.,https://dpi.gov.gy/feed/,7,Internacional,77,Guyana,en,True,0 +6397,Climate Tracker Guyana MedioAmbiente,Climate change environment and sustainability.,https://climatetracker.org/feed/,8,Medio Ambiente,77,Guyana,en,False,30 +6398,Guyana Forestry Commission,Forestry environment and conservation updates.,https://www.forestry.gov.gy/feed/,8,Medio Ambiente,77,Guyana,en,False,30 +6384,Stabroek News MedioAmbiente,Environment climate biodiversity and conservation.,https://www.stabroeknews.com/feed/,8,Medio Ambiente,77,Guyana,en,True,0 +6389,Guyana Chronicle Opinion,Opinion analysis society and governance.,https://guyanachronicle.com/feed/,10,Opinión,77,Guyana,en,True,0 +6393,Guyana Standard Opinion,Opinion columns and political debate.,https://www.guyanastandard.com/feed/,10,Opinión,77,Guyana,en,False,30 +6387,Demerara Waves Politica,Political analysis government and public affairs.,https://demerarawaves.com/feed/,11,Política,77,Guyana,en,True,0 +6386,News Room Guyana Salud,Health public health and medical updates.,https://newsroom.gy/feed/,12,Salud,77,Guyana,en,True,0 +6394,Loop Guyana Noticias,General news society entertainment and trends.,https://loopnews.com/rss?country=guyana,13,Sociedad,77,Guyana,en,False,30 +6401,Guyana Energy Agency,Energy policy renewables and technology.,https://gea.gov.gy/feed/,14,Tecnología,77,Guyana,en,False,30 +6388,OilNOW Guyana Energia,Energy transition oil gas and technology.,https://oilnow.gy/feed/,14,Tecnología,77,Guyana,en,False,30 +6402,Guyana Tourism Authority,Travel tourism culture and destinations.,https://guyanatourism.com/feed/,15,Viajes,77,Guyana,en,True,0 +6411,Radio Caraibes Haiti Cultura,Arts culture music and traditions.,https://www.radiocaraibesfm.com/feed/,2,Cultura,78,Haití,fr,False,30 +6409,AllAfrica Haiti,Aggregated national and regional news.,https://allafrica.com/tools/headlines/rdf/haiti/headlines.rdf,7,Internacional,78,Haití,en,False,30 +6395,Caribbean National Weekly Haiti,Caribbean regional news including Haiti.,https://www.cnwnetwork.com/feed/,7,Internacional,78,Haití,en,False,30 +6396,Caribbean News Service Haiti,Regional politics economy and society.,https://caribbeannewsservice.com/feed/,7,Internacional,78,Haití,en,False,30 +6405,HaaitiLibre Internacional,Relations internationales et cooperation.,https://www.haitilibre.com/rss.xml,7,Internacional,78,Haití,fr,False,30 +6413,IFRC Haiti,Red cross humanitarian and disaster response.,https://www.ifrc.org/rss.xml,7,Internacional,78,Haití,en,False,30 +6408,ReliefWeb Haiti,Humanitarian reports emergencies and aid.,https://reliefweb.int/rss.xml?country=HTI,7,Internacional,78,Haití,en,False,30 +6407,UN Haiti Noticias,United Nations updates and humanitarian response.,https://www.un.org/en/rss/haiti.xml,7,Internacional,78,Haití,en,False,30 +1759,UNDP Haiti,Development reports governance and economy.,https://www.undp.org/rss.xml,7,Internacional,78,Haití,en,False,30 +6403,Le Nouvelliste MedioAmbiente,Environnement climat risques naturels.,https://lenouvelliste.com/feed/,8,Medio Ambiente,78,Haití,fr,False,30 +6412,AyiboPost Opinion,Opinion analysis politics and society.,https://ayibopost.com/feed/,10,Opinión,78,Haití,fr,True,0 +6414,Haiti Press Network Opinion,Opinion columns commentary and debate.,https://www.hpnhaiti.com/feed/,10,Opinión,78,Haití,fr,True,0 +6415,MSPP Haiti Salud,Official health ministry updates and alerts.,https://www.mspp.gouv.ht/feed/,12,Salud,78,Haití,fr,True,0 +6410,Radio Metropole Haiti Salud,Public health epidemics and medical news.,https://metropolehaiti.com/feed/,12,Salud,78,Haití,fr,False,30 +6404,AlterPresse Sociedad,Societe droits humains et mouvements sociaux.,https://www.alterpresse.org/spip.php?page=backend,13,Sociedad,78,Haití,fr,True,0 +6406,Loop Haiti Sociedad,General news society entertainment and trends.,https://loopnews.com/rss?country=haiti,13,Sociedad,78,Haití,en,False,30 +6434,AllAfrica Honduras Economia,Agregador de noticias economicas nacionales.,https://allafrica.com/tools/headlines/rdf/honduras/economy.rdf,4,Economía,79,Honduras,en,False,30 +6430,Banco Central de Honduras,Indicadores macroeconomicos politica monetaria.,https://www.bch.hn/rss,4,Economía,79,Honduras,es,False,30 +6429,Consejo Hondureno de la Empresa Privada,Actividad empresarial economia y competitividad.,https://www.cohep.com/rss,4,Economía,79,Honduras,es,False,30 +6419,Criterio HN Economia,Investigacion economica corrupcion y desarrollo.,https://criterio.hn/feed/,4,Economía,79,Honduras,es,True,0 +6422,La Tribuna Honduras Economia,Economia nacional comercio e inversion.,https://www.latribuna.hn/feed/,4,Economía,79,Honduras,es,False,30 +6431,Secretaria de Desarrollo Economico,Politicas publicas comercio e industria.,https://www.sde.gob.hn/rss,4,Economía,79,Honduras,es,True,0 +6423,Tiempo Digital Economia,Noticias economicas finanzas y negocios.,https://tiempo.hn/feed/,4,Economía,79,Honduras,es,False,30 +6432,World Bank Honduras,Analisis economico desarrollo y datos financieros.,https://www.worldbank.org/en/country/honduras/rss,4,Economía,79,Honduras,en,False,30 +6428,AllAfrica Honduras Politica,Agregador de noticias politicas nacionales.,https://allafrica.com/tools/headlines/rdf/honduras/headlines.rdf,11,Política,79,Honduras,en,False,30 +6420,Confidencial Honduras Politica,Investigacion politica y corrupcion.,https://confidencialhn.com/feed/,11,Política,79,Honduras,es,True,0 +6425,Congreso Nacional Honduras,Actividad legislativa leyes y sesiones.,https://www.congreso.gob.hn/rss,11,Política,79,Honduras,es,False,30 +6424,Honduprensa Politica,Agencia nacional con informacion politica.,https://www.honduprensa.hn/feed/,11,Política,79,Honduras,es,False,30 +6426,Presidencia de Honduras,Comunicados oficiales agenda presidencial.,https://www.presidencia.gob.hn/rss,11,Política,79,Honduras,es,False,30 +6427,Secretaria de Relaciones Exteriores Honduras,Politica exterior y diplomacia.,https://www.cancilleria.gob.hn/rss,11,Política,79,Honduras,es,False,30 +6439,AllAfrica Honduras Tecnologia,Agregador de noticias tecnologia y telecomunicaciones.,https://allafrica.com/tools/headlines/rdf/honduras/ict.rdf,14,Tecnología,79,Honduras,en,False,30 +6435,Conatel Honduras Tecnologia,Regulacion telecomunicaciones y espectro radioelectrico.,https://www.conatel.gob.hn/rss,14,Tecnología,79,Honduras,es,True,0 +6417,El Heraldo Honduras Tecnologia,Noticias de tecnologia innovacion y transformacion digital.,https://www.elheraldo.hn/rss,14,Tecnología,79,Honduras,es,False,30 +6438,Honduras Digital Noticias,Transformacion digital startups y tecnologia.,https://hondurasdigital.gob.hn/rss,14,Tecnología,79,Honduras,es,False,30 +6416,La Prensa Honduras Tecnologia,Actualidad tecnologica telecomunicaciones y negocios digitales.,https://www.laprensa.hn/rss,14,Tecnología,79,Honduras,es,False,30 +6418,Proceso Digital Tecnologia,Analisis tecnologia gobierno digital y ciberseguridad.,https://proceso.hn/feed/,14,Tecnología,79,Honduras,es,True,0 +6421,Radio America Honduras Tecnologia,Innovacion tecnologia y economia digital.,https://www.radioamerica.hn/feed/,14,Tecnología,79,Honduras,es,True,0 +6436,Senacyt Honduras Ciencia Tecnologia,Investigacion ciencia tecnologia e innovacion.,https://www.senacyt.gob.hn/rss,14,Tecnología,79,Honduras,es,False,30 +6437,Universidad Nacional Honduras Tecnologia,Investigacion tecnologia y desarrollo academico.,https://www.unah.edu.hn/rss,14,Tecnología,79,Honduras,es,False,30 +6464,Budapest University of Technology Ciencia,Engineering science technology and research.,https://www.bme.hu/rss,1,Ciencia,80,Hungría,en,False,30 +6460,ELKH Research Network Ciencia,National research network science and technology.,https://elkh.org/rss,1,Ciencia,80,Hungría,en,True,0 +6467,EU Horizon Hungary Ciencia,European research projects and innovation.,https://research-and-innovation.ec.europa.eu/rss,1,Ciencia,80,Hungría,en,False,30 +6463,Eotvos Lorand University Ciencia,University research science and education.,https://www.elte.hu/rss,1,Ciencia,80,Hungría,en,False,30 +6442,HVG Ciencia,Science research innovation and academic news.,https://hvg.hu/rss,1,Ciencia,80,Hungría,hu,True,0 +6459,Hungarian Academy of Sciences Ciencia,Scientific research basic science and innovation.,https://mta.hu/rss,1,Ciencia,80,Hungría,en,False,30 +6443,Index Hu Ciencia,Science discoveries research and academia.,https://index.hu/24ora/rss/,1,Ciencia,80,Hungría,hu,True,0 +6461,National Research Development Innovation Office Ciencia,Research funding innovation and scientific policy.,https://nkfih.gov.hu/rss,1,Ciencia,80,Hungría,en,False,30 +6466,Nature Index Hungary,Country level research output and science metrics.,https://www.nature.com/subjects/rss.xml,1,Ciencia,80,Hungría,en,False,30 +6465,Semmelweis University Ciencia,Medical research biomedical science and health.,https://semmelweis.hu/rss,1,Ciencia,80,Hungría,en,True,0 +6444,Telex Ciencia,Science journalism research and innovation.,https://telex.hu/rss,1,Ciencia,80,Hungría,hu,True,0 +6462,University of Szeged Ciencia,Academic research science publications and projects.,https://u-szeged.hu/rss,1,Ciencia,80,Hungría,en,False,30 +6458,AllAfrica Hungary Economia,Aggregated economic news related to Hungary.,https://allafrica.com/tools/headlines/rdf/hungary/economy.rdf,4,Economía,80,Hungría,en,False,30 +6452,Budapest Business Journal Economia,Business investment economy and companies.,https://bbj.hu/feed/,4,Economía,80,Hungría,en,True,0 +6453,Central Bank of Hungary Economia,Monetary policy inflation and indicators.,https://www.mnb.hu/rss,4,Economía,80,Hungría,en,False,30 +6440,Hungary Today Economia,Economic policy investment and EU funds.,https://hungarytoday.hu/feed/,4,Economía,80,Hungría,en,True,0 +6445,Magyar Nemzet Economia,Economic news government policy and markets.,https://magyarnemzet.hu/feed/,4,Economía,80,Hungría,hu,True,0 +6454,Ministry of Finance Hungary Economia,Fiscal policy budget and taxation.,https://www.kormany.hu/en/ministry-of-finance/rss,4,Economía,80,Hungría,en,False,30 +6456,OECD Hungary Economia,Economic outlook and policy analysis.,https://www.oecd.org/rss.xml,4,Economía,80,Hungría,en,False,30 +6447,Portfolio Hu Economia,Economic news markets finance and business.,https://www.portfolio.hu/rss,4,Economía,80,Hungría,hu,False,30 +6455,Statistical Office Hungary Economia,Official economic statistics and data.,https://www.ksh.hu/rss,4,Economía,80,Hungría,en,False,30 +6457,World Bank Hungary Economia,Development finance and economic indicators.,https://www.worldbank.org/en/country/hungary/rss,4,Economía,80,Hungría,en,False,30 +6451,AllAfrica Hungary Politica,Aggregated political news related to Hungary.,https://allafrica.com/tools/headlines/rdf/hungary/headlines.rdf,11,Política,80,Hungría,en,False,30 +6441,Budapest Times Politica,Political analysis government and opposition.,https://budapesttimes.hu/feed/,11,Política,80,Hungría,en,True,0 +6449,Government of Hungary News,Official government announcements and policy.,https://kormany.hu/rss,11,Política,80,Hungría,en,False,30 +6448,MTI Hungary Politica,National news agency political coverage.,https://mti.hu/rss,11,Política,80,Hungría,hu,False,30 +6450,Ministry of Foreign Affairs Hungary,Foreign policy diplomacy and statements.,https://abouthungary.hu/rss,11,Política,80,Hungría,en,False,30 +6446,Nepszava Politika,Opposition perspective politics and democracy.,https://nepszava.hu/rss,11,Política,80,Hungría,hu,True,0 +6505,Bloomberg Quint India Economia,Business economy policy and markets.,https://www.bqprime.com/feed,4,Economía,81,India,en,True,9 +6497,Business Standard India Economia,Economic policy markets industry and finance.,https://www.business-standard.com/rss/home_page_top_stories.rss,4,Economía,81,India,en,False,30 +6502,CNBC TV18 India Economia,Business markets stocks and economy.,https://www.cnbctv18.com/rssfeed/rssfeed.xml,4,Economía,81,India,en,False,30 +6498,Financial Express India Economia,Economy finance infrastructure and policy.,https://www.financialexpress.com/feed/,4,Economía,81,India,en,False,30 +6499,LiveMint India Economia,Business economy startups and finance.,https://www.livemint.com/rss/economy,4,Economía,81,India,en,True,0 +6507,Ministry of Finance India Economia,Fiscal policy budget and taxation.,https://www.indiabudget.gov.in/rss.xml,4,Economía,81,India,en,False,30 +6503,Moneycontrol India Economia,Markets stocks economy and personal finance.,https://www.moneycontrol.com/rss/buzzingstocks.xml,4,Economía,81,India,en,False,30 +6501,NDTV Profit Economia,Business economy finance and markets.,https://feeds.feedburner.com/ndtvprofit-latest,4,Economía,81,India,en,True,0 +6508,NITI Aayog Economia,Economic planning development and policy.,https://www.niti.gov.in/rss.xml,4,Economía,81,India,en,False,30 +6506,Reserve Bank of India Economia,Monetary policy inflation and banking.,https://www.rbi.org.in/rss.xml,4,Economía,81,India,en,False,30 +6504,Reuters India Economia,India economy macro policy and markets.,https://www.reuters.com/rssFeed/indiaNews,4,Economía,81,India,en,False,30 +6496,The Economic Times India Economia,Business economy markets finance and companies.,https://economictimes.indiatimes.com/rssfeedsdefault.cms,4,Economía,81,India,en,True,0 +6500,The Hindu BusinessLine Economia,Economic news markets trade and industry.,https://www.thehindubusinessline.com/feeder/default.rss,4,Economía,81,India,en,True,0 +6509,World Bank India Economia,Development finance and economic indicators.,https://www.worldbank.org/en/country/india/rss,4,Economía,81,India,en,False,30 +6481,AllAfrica India Internacional,Aggregated international news related to India.,https://allafrica.com/tools/headlines/rdf/india/headlines.rdf,7,Internacional,81,India,en,False,30 +6477,Deccan Herald Internacional,World news international relations and geopolitics.,https://www.deccanherald.com/rss/rss.xml,7,Internacional,81,India,en,False,30 +6544,Firstpost Internacional,International politics security and global economy.,https://www.firstpost.com/rss/world.xml,7,Internacional,81,India,en,False,30 +6542,Hindustan Times Internacional,International news foreign policy and world events.,https://www.hindustantimes.com/rss/world/rssfeed.xml,7,Internacional,81,India,en,False,30 +6545,India Today Internacional,World affairs diplomacy and international politics.,https://www.indiatoday.in/rss/1206577,7,Internacional,81,India,en,True,0 +6541,Indian Express Internacional,World news geopolitics and foreign relations.,https://indianexpress.com/section/world/feed/,7,Internacional,81,India,en,False,30 +6479,Ministry of External Affairs India Internacional,Official foreign policy statements and diplomacy.,https://www.mea.gov.in/rss.htm,7,Internacional,81,India,en,False,30 +6543,NDTV World Internacional,World news diplomacy conflicts and global affairs.,https://feeds.feedburner.com/ndtvnews-world-news,7,Internacional,81,India,en,True,0 +6495,Observer Research Foundation Internacional,Foreign policy global security and strategy.,https://www.orfonline.org/rss,7,Internacional,81,India,en,False,30 +6547,ReliefWeb India Internacional,Humanitarian crises emergencies and aid.,https://reliefweb.int/rss.xml?country=IND,7,Internacional,81,India,en,False,30 +6473,Scroll India Internacional,International reporting conflicts and diplomacy.,https://scroll.in/feed,7,Internacional,81,India,en,False,30 +6540,The Hindu Internacional,International affairs diplomacy and global policy.,https://www.thehindu.com/news/international/?service=rss,7,Internacional,81,India,en,True,0 +6474,The Wire Internacional,Global affairs foreign policy and analysis.,https://thewire.in/rss,7,Internacional,81,India,en,False,30 +6470,Times of India Internacional,Global news diplomacy conflicts and geopolitics.,https://timesofindia.indiatimes.com/rssfeeds/296589292.cms,7,Internacional,81,India,en,True,0 +6546,United Nations India Internacional,UN activities peace and development updates.,https://www.un.org/en/rss/india.xml,7,Internacional,81,India,en,False,30 +6486,ANI News Politica,Political news national and regional coverage.,https://aninews.in/rss/politics.xml,11,Política,81,India,en,False,30 +6480,Election Commission of India,Electoral process democracy and voting.,https://eci.gov.in/rss,11,Política,81,India,en,False,30 +6475,Firstpost Politica,Politics governance and national debate.,https://www.firstpost.com/rss/politics.xml,11,Política,81,India,en,False,30 +6471,Hindustan Times Politica,Political news analysis and public affairs.,https://www.hindustantimes.com/rss/india/rssfeed.xml,11,Política,81,India,en,False,30 +6476,India Today Politica,Political developments government and elections.,https://www.indiatoday.in/rss/1206514,11,Política,81,India,en,True,0 +6469,Indian Express Politica,Political reporting elections and institutions.,https://indianexpress.com/section/political-pulse/feed/,11,Política,81,India,en,False,30 +6492,Lok Sabha India,Parliamentary activity bills and debates.,https://loksabha.nic.in/rss,11,Política,81,India,en,False,30 +6472,NDTV Politica,Political news government and parliament.,https://feeds.feedburner.com/ndtvnews-india-news,11,Política,81,India,en,True,0 +6488,News18 India Politica,Political news elections and governance.,https://www.news18.com/rss/politics.xml,11,Política,81,India,en,False,30 +6484,Outlook India Politica,Political analysis elections and institutions.,https://www.outlookindia.com/rss/politics,11,Política,81,India,en,False,30 +6494,PRS Legislative Research India,Legislative analysis bills and public policy.,https://prsindia.org/rss.xml,11,Política,81,India,en,False,30 +6487,PTI India Politica,Press Trust of India political news service.,https://www.ptinews.com/rssfeed/rssfeed.aspx?section=politics,11,Política,81,India,en,False,30 +6478,Press Information Bureau India,Official government press releases and policy.,https://pib.gov.in/rssfeed.aspx,11,Política,81,India,en,False,30 +6493,Rajya Sabha India,Upper house debates and legislative activity.,https://rajyasabha.nic.in/rss,11,Política,81,India,en,True,0 +6485,Rediff India Politica,Politics government and national debate.,https://www.rediff.com/rss/newsrss.xml,11,Política,81,India,en,True,0 +6489,Republic World Politica,Political debate government and policy.,https://www.republicworld.com/rss/politics.xml,11,Política,81,India,en,True,0 +6468,The Hindu Politica,Political news government parliament and policy.,https://www.thehindu.com/news/national/?service=rss,11,Política,81,India,en,True,0 +6491,The Print Politica,Policy analysis politics and governance.,https://theprint.in/feed/,11,Política,81,India,en,False,30 +6490,The Quint Politica,Independent political journalism and analysis.,https://www.thequint.com/rss/politics,11,Política,81,India,en,False,30 +6482,The Statesman Politica,Political news government policy and democracy.,https://www.thestatesman.com/feed,11,Política,81,India,en,False,30 +6483,The Telegraph India Politica,Political reporting governance and public affairs.,https://www.telegraphindia.com/rss,11,Política,81,India,en,False,30 +6527,91mobiles Tecnologia,Mobile technology smartphones and devices.,https://www.91mobiles.com/feed/,14,Tecnología,81,India,en,False,30 +6524,AllAfrica India Tecnologia,Aggregated technology and ICT news.,https://allafrica.com/tools/headlines/rdf/india/ict.rdf,14,Tecnología,81,India,en,False,30 +6519,Analytics India Magazine Tecnologia,Artificial intelligence data science and analytics.,https://analyticsindiamag.com/feed/,14,Tecnología,81,India,en,True,0 +6535,BS Tech India Tecnologia,Technology business digital economy and innovation.,https://www.business-standard.com/rss/technology.rss,14,Tecnología,81,India,en,False,30 +6530,Dataquest India Tecnologia,Enterprise IT cloud and cybersecurity.,https://www.dqindia.com/feed/,14,Tecnología,81,India,en,False,30 +6526,Digit India Tecnologia,Consumer technology reviews and innovation.,https://www.digit.in/rss,14,Tecnología,81,India,en,True,0 +6518,ET Telecom Tecnologia,Telecom policy networks and digital infrastructure.,https://telecom.economictimes.indiatimes.com/rss/topstories,14,Tecnología,81,India,en,True,0 +6536,Economic Times CIO Tecnologia,Enterprise IT CIO cloud and digital strategy.,https://cio.economictimes.indiatimes.com/rss/topstories,14,Tecnología,81,India,en,True,0 +6531,Express Computer India Tecnologia,IT enterprise cloud and infrastructure.,https://www.expresscomputer.in/feed/,14,Tecnología,81,India,en,True,0 +6525,Gizbot India Tecnologia,Gadgets mobiles reviews and consumer technology.,https://www.gizbot.com/rss/,14,Tecnología,81,India,en,False,30 +6521,Government of India Digital,Digital government policy and technology.,https://www.digitalindia.gov.in/rss,14,Tecnología,81,India,en,True,0 +6539,IEEE India Tecnologia,Engineering technology research and standards.,https://ieee.org/rss,14,Tecnología,81,India,en,False,30 +6523,ISRO India Tecnologia,Space technology satellites and research.,https://www.isro.gov.in/rss.xml,14,Tecnología,81,India,en,False,30 +6516,Inc42 Tecnologia,Startup technology funding and innovation.,https://inc42.com/feed/,14,Tecnología,81,India,en,True,0 +6537,India AI Portal Tecnologia,Artificial intelligence policy research and news.,https://indiaai.gov.in/rss,14,Tecnología,81,India,en,False,30 +6511,Indian Express Tecnologia,Technology startups digital policy and innovation.,https://indianexpress.com/section/technology/feed/,14,Tecnología,81,India,en,False,30 +6517,Medianama Tecnologia,Technology policy telecom and digital rights.,https://www.medianama.com/feed/,14,Tecnología,81,India,en,True,0 +6522,MeitY India Tecnologia,IT policy electronics and digital governance.,https://www.meity.gov.in/rss.xml,14,Tecnología,81,India,en,False,30 +6538,NASSCOM Tecnologia,IT industry policy outsourcing and innovation.,https://nasscom.in/rss.xml,14,Tecnología,81,India,en,True,0 +6512,NDTV Gadgets Tecnologia,Technology gadgets internet and digital trends.,https://feeds.feedburner.com/gadgets360-latest,14,Tecnología,81,India,en,True,0 +6533,OpenGov Asia India Tecnologia,Digital government smart cities and policy.,https://opengovasia.com/feed/,14,Tecnología,81,India,en,True,0 +6532,People Matters Tech HR,HR technology analytics and digital work.,https://www.peoplematters.in/rss,14,Tecnología,81,India,en,False,30 +6534,SmartCities India Tecnologia,Smart cities urban technology and infrastructure.,https://smartcities.gov.in/rss,14,Tecnología,81,India,en,False,30 +6528,TechCircle India Tecnologia,Enterprise technology startups and policy.,https://techcircle.vccircle.com/feed/,14,Tecnología,81,India,en,False,30 +6514,TechCrunch India Tecnologia,Startup technology venture capital and innovation.,https://techcrunch.com/tag/india/feed/,14,Tecnología,81,India,en,True,0 +6510,The Hindu Tecnologia,Technology policy digital innovation and science.,https://www.thehindu.com/sci-tech/technology/?service=rss,14,Tecnología,81,India,en,True,0 +6520,The Ken India Tecnologia,Technology business startups and deep analysis.,https://the-ken.com/feed/,14,Tecnología,81,India,en,True,0 +6513,Times of India Tecnologia,Technology internet apps and digital economy.,https://timesofindia.indiatimes.com/rssfeeds/66949542.cms,14,Tecnología,81,India,en,True,0 +6529,VC Circle India Tecnologia,Venture capital startups and technology business.,https://www.vccircle.com/feed/,14,Tecnología,81,India,en,False,30 +6515,YourStory Tecnologia,Startup ecosystem innovation and entrepreneurship.,https://yourstory.com/feed,14,Tecnología,81,India,en,True,0 +6566,Antara News Ekonomi,Agencia nacional economia y desarrollo.,https://www.antaranews.com/rss/ekonomi.xml,4,Economía,82,Indonesia,id,True,0 +6570,Bank Indonesia Economia,Monetary policy inflation and banking.,https://www.bi.go.id/rss.xml,4,Economía,82,Indonesia,en,False,30 +6568,Berita Satu Ekonomi,Economia empresas mercados y finanzas.,https://www.beritasatu.com/rss/ekonomi,4,Economía,82,Indonesia,id,False,30 +6567,Bisnis Indonesia Economia,Business industry markets and economy.,https://bisnis.com/rss,4,Economía,82,Indonesia,id,False,30 +6565,CNN Indonesia Ekonomi,Berita ekonomi bisnis dan keuangan.,https://www.cnnindonesia.com/ekonomi/rss,4,Economía,82,Indonesia,id,True,0 +6564,Detik Finance,Keuangan pasar investasi dan ekonomi.,https://finance.detik.com/rss,4,Economía,82,Indonesia,id,True,0 +6561,Jakarta Post Economia,Economic policy markets trade and business.,https://www.thejakartapost.com/rss/business,4,Economía,82,Indonesia,en,False,30 +6562,Kompas Ekonomi,Ekonomi nasional bisnis dan pasar keuangan.,https://www.kompas.com/rss/ekonomi,4,Economía,82,Indonesia,id,False,30 +6569,Kontan Ekonomi,Pasar modal investasi dan makro ekonomi.,https://www.kontan.co.id/rss,4,Economía,82,Indonesia,id,False,30 +6571,Ministry of Finance Indonesia Economia,Fiscal policy budget and taxation.,https://www.kemenkeu.go.id/rss,4,Economía,82,Indonesia,en,False,30 +6572,Statistics Indonesia Economia,Official economic statistics and indicators.,https://www.bps.go.id/rss.xml,4,Economía,82,Indonesia,en,False,30 +6563,Tempo Ekonomi,Analisis ekonomi bisnis dan kebijakan publik.,https://tempo.co/rss/ekonomi,4,Economía,82,Indonesia,id,False,30 +6573,World Bank Indonesia Economia,Development finance and economic indicators.,https://www.worldbank.org/en/country/indonesia/rss,4,Economía,82,Indonesia,en,False,30 +6596,ASEAN Indonesia Internasional,Regional diplomacy security and cooperation.,https://asean.org/feed/,7,Internacional,82,Indonesia,en,True,0 +6560,AllAfrica Indonesia Internasional,Aggregated international news references.,https://allafrica.com/tools/headlines/rdf/indonesia/headlines.rdf,7,Internacional,82,Indonesia,en,False,30 +6593,Antara News Internasional,National agency international coverage.,https://www.antaranews.com/rss/internasional.xml,7,Internacional,82,Indonesia,id,False,30 +6595,Berita Satu Internasional,World economy diplomacy and geopolitics.,https://www.beritasatu.com/rss/internasional,7,Internacional,82,Indonesia,id,False,30 +6592,CNN Indonesia Internasional,World news diplomacy conflicts and analysis.,https://www.cnnindonesia.com/internasional/rss,7,Internacional,82,Indonesia,id,True,0 +6591,Detik World,Global news international politics and events.,https://news.detik.com/internasional/rss,7,Internacional,82,Indonesia,id,True,0 +6588,Jakarta Post Internacional,World news diplomacy and foreign affairs.,https://www.thejakartapost.com/rss/world,7,Internacional,82,Indonesia,en,False,30 +6589,Kompas Global,International news diplomacy and geopolitics.,https://www.kompas.com/rss/global,7,Internacional,82,Indonesia,id,False,30 +6556,Kumparan Internasional,Global affairs international reporting.,https://kumparan.com/rss,7,Internacional,82,Indonesia,id,False,30 +6558,Ministry of Foreign Affairs Indonesia Internasional,Official foreign policy statements and diplomacy.,https://kemlu.go.id/rss.xml,7,Internacional,82,Indonesia,en,False,30 +6598,ReliefWeb Indonesia Internasional,Humanitarian crises emergencies and aid.,https://reliefweb.int/rss.xml?country=IDN,7,Internacional,82,Indonesia,en,False,30 +6594,Republika Internasional,International politics global affairs.,https://www.republika.co.id/rss/internasional,7,Internacional,82,Indonesia,id,True,0 +6599,Reuters Indonesia Internasional,International news related to Indonesia.,https://www.reuters.com/rssFeed/indonesiaNews,7,Internacional,82,Indonesia,en,False,30 +6590,Tempo Dunia,World affairs diplomacy and conflicts.,https://tempo.co/rss/dunia,7,Internacional,82,Indonesia,id,False,30 +6597,United Nations Indonesia Internasional,UN activities peace development and aid.,https://www.un.org/en/rss/indonesia.xml,7,Internacional,82,Indonesia,en,False,30 +6553,Antara News Politika,National news agency political coverage.,https://www.antaranews.com/rss/politik.xml,11,Política,82,Indonesia,id,True,0 +6557,Berita Satu Politika,Political analysis elections and governance.,https://www.beritasatu.com/rss/politik,11,Política,82,Indonesia,id,False,30 +6552,CNN Indonesia Politika,Politics governance and national debate.,https://www.cnnindonesia.com/politik/rss,11,Política,82,Indonesia,id,False,30 +6551,Detik News Politika,Political reporting government and public affairs.,https://news.detik.com/rss,11,Política,82,Indonesia,id,True,0 +6548,Jakarta Post Politica,Political news government policy and parliament.,https://www.thejakartapost.com/rss/politics,11,Política,82,Indonesia,en,False,30 +6549,Kompas Politika,Actualidad politica gobierno elecciones y parlamento.,https://www.kompas.com/rss,11,Política,82,Indonesia,id,False,30 +6555,Media Indonesia Politika,Government policy politics and institutions.,https://mediaindonesia.com/rss,11,Política,82,Indonesia,id,True,0 +6559,Presidential Secretariat Indonesia,Official presidential communications.,https://www.presidenri.go.id/feed/,11,Política,82,Indonesia,id,False,30 +6554,Republika Politika,Political commentary policy and governance.,https://www.republika.co.id/rss/politik,11,Política,82,Indonesia,id,True,0 +6550,Tempo Politika,Investigative political journalism and analysis.,https://tempo.co/rss/politik,11,Política,82,Indonesia,id,False,30 +6587,AllAfrica Indonesia Tecnologia,Aggregated technology and ICT news.,https://allafrica.com/tools/headlines/rdf/indonesia/ict.rdf,14,Tecnología,82,Indonesia,en,False,30 +6579,Antara News Teknologi,National technology science and innovation news.,https://www.antaranews.com/rss/tekno.xml,14,Tecnología,82,Indonesia,id,True,0 +6586,BPPT Indonesia Tecnologia,Applied technology engineering and innovation.,https://www.bppt.go.id/rss.xml,14,Tecnología,82,Indonesia,en,False,30 +6585,BRIN Indonesia Tecnologia,Research innovation science and technology.,https://brin.go.id/rss.xml,14,Tecnología,82,Indonesia,en,False,30 +6578,CNN Indonesia Teknologi,Technology innovation startups and digital economy.,https://www.cnnindonesia.com/teknologi/rss,14,Tecnología,82,Indonesia,id,True,0 +6580,DailySocial Tecnologia,Startup ecosystem venture capital and tech.,https://dailysocial.id/rss,14,Tecnología,82,Indonesia,en,False,30 +6577,Detik Inet Teknologi,Internet technology gadgets and digital life.,https://inet.detik.com/rss,14,Tecnología,82,Indonesia,id,True,0 +6574,Jakarta Post Tecnologia,Technology digital policy startups and innovation.,https://www.thejakartapost.com/rss/tech,14,Tecnología,82,Indonesia,en,False,30 +6582,Katadata Teknologi,Technology policy data economy and innovation.,https://katadata.co.id/rss,14,Tecnología,82,Indonesia,id,True,0 +6575,Kompas Tekno,Technology gadgets internet and digital trends.,https://tekno.kompas.com/rss,14,Tecnología,82,Indonesia,id,False,30 +6584,Ministry of ICT Indonesia,Digital policy telecom and information technology.,https://www.kominfo.go.id/rss,14,Tecnología,82,Indonesia,en,False,30 +6583,Startupbisnis Tecnologia,Startup technology entrepreneurship and digital.,https://startupbisnis.com/feed/,14,Tecnología,82,Indonesia,id,False,30 +6581,Tech in Asia Indonesia,Startup technology funding and innovation.,https://www.techinasia.com/feed,14,Tecnología,82,Indonesia,en,False,30 +6576,Tempo Tekno,Technology innovation startups and research.,https://tempo.co/rss/tekno,14,Tecnología,82,Indonesia,id,False,30 +6628,AllAfrica Iraq Ciencia,Aggregated science and research references.,https://allafrica.com/tools/headlines/rdf/iraq/science.rdf,1,Ciencia,83,Irak,en,False,30 +6626,Iraqi Red Crescent Science,Health research disasters and response.,https://www.ircs.org.iq/rss,1,Ciencia,83,Irak,en,True,0 +6625,Ministry of Health Iraq Ciencia,Medical research public health and science.,https://moh.gov.iq/rss,1,Ciencia,83,Irak,en,False,30 +6617,Ministry of Higher Education Iraq Ciencia,Science policy research and universities.,https://mohesr.gov.iq/rss,1,Ciencia,83,Irak,en,False,30 +6624,Mustansiriyah University Ciencia,Academic science research and studies.,https://uomustansiriyah.edu.iq/rss,1,Ciencia,83,Irak,en,False,30 +6618,University of Baghdad Ciencia,Academic research science and innovation.,https://uobaghdad.edu.iq/rss,1,Ciencia,83,Irak,en,False,30 +6623,University of Basrah Ciencia,Scientific research environment and biology.,https://www.uobasrah.edu.iq/rss,1,Ciencia,83,Irak,en,False,30 +6615,World Bank Iraq Ciencia,Development research science and data.,https://www.worldbank.org/en/country/iraq/rss,1,Ciencia,83,Irak,en,False,30 +6627,World Health Organization Iraq Ciencia,Public health research and science.,https://www.emro.who.int/rss/irq.xml,1,Ciencia,83,Irak,en,False,30 +6612,Central Bank of Iraq Economia,Monetary policy banking and currency.,https://cbi.iq/rss,4,Economía,83,Irak,en,False,30 +6611,Ministry of Finance Iraq Economia,Fiscal policy budget and public spending.,https://mof.gov.iq/rss,4,Economía,83,Irak,en,False,30 +6604,NINA Iraq Economia,National news economy oil and infrastructure.,https://ninanews.com/rss,4,Economía,83,Irak,ar,False,30 +6614,OPEC Iraq Economia,Oil market data and production updates.,https://www.opec.org/rss.xml,4,Economía,83,Irak,en,False,30 +6613,Oil Ministry Iraq Economia,Oil production exports and energy policy.,https://oil.gov.iq/rss,4,Economía,83,Irak,en,False,30 +1574,Al Jazeera Iraq Internacional,International news conflicts diplomacy and region.,https://www.aljazeera.com/xml/rss/all.xml,7,Internacional,83,Irak,en,True,0 +6633,Al Monitor Iraq Internacional,Middle East diplomacy and regional affairs.,https://www.al-monitor.com/rss,7,Internacional,83,Irak,en,True,0 +6603,Al Sumaria Internacional,World news regional conflicts and diplomacy.,https://www.alsumaria.tv/rss,7,Internacional,83,Irak,ar,False,30 +6610,AllAfrica Iraq Internacional,Aggregated international references.,https://allafrica.com/tools/headlines/rdf/iraq/headlines.rdf,7,Internacional,83,Irak,en,False,30 +6629,BBC News Iraq Internacional,Global coverage conflicts and diplomacy.,https://feeds.bbci.co.uk/news/world/middle_east/rss.xml,7,Internacional,83,Irak,en,True,0 +6606,Baghdad Today Internacional,International affairs security and geopolitics.,https://baghdadtoday.news/rss,7,Internacional,83,Irak,ar,True,0 +6632,Foreign Policy Iraq Internacional,Global analysis policy and security.,https://foreignpolicy.com/feed/,7,Internacional,83,Irak,en,True,0 +6602,INA Iraq Internacional,Official international relations and statements.,https://ina.iq/eng/rss,7,Internacional,83,Irak,en,False,30 +6605,Kurdistan24 Internacional,World news regional security and diplomacy.,https://www.kurdistan24.net/en/rss,7,Internacional,83,Irak,en,False,30 +6608,ReliefWeb Iraq Internacional,Humanitarian crises emergencies and response.,https://reliefweb.int/rss.xml?country=IRQ,7,Internacional,83,Irak,en,False,30 +6609,Reuters Iraq Internacional,International news related to Iraq.,https://www.reuters.com/rssFeed/iraqNews,7,Internacional,83,Irak,en,False,30 +6600,Rudaw Internacional,International affairs Kurdish region and Iraq.,https://www.rudaw.net/rss,7,Internacional,83,Irak,en,True,0 +6601,Shafaq News Internacional,International developments regional diplomacy.,https://shafaq.com/rss,7,Internacional,83,Irak,en,False,30 +6630,United Nations Iraq Internacional,UN activities peace security and aid.,https://www.un.org/en/rss/iraq.xml,7,Internacional,83,Irak,en,False,30 +6607,Iraqi Government News Politica,Official government statements and policy.,https://gds.gov.iq/rss,11,Política,83,Irak,en,False,30 +6351,crisisgroup.org Irak,,https://www.crisisgroup.org/rss/87,11,Política,83,Irak,,True,0 +6622,AllAfrica Iraq Tecnologia,Aggregated technology and ICT references.,https://allafrica.com/tools/headlines/rdf/iraq/ict.rdf,14,Tecnología,83,Irak,en,False,30 +6620,Asiacell Tecnologia,Telecom networks mobile and digital services.,https://www.asiacell.com/rss,14,Tecnología,83,Irak,en,False,30 +6616,Ministry of Communications Iraq Tecnologia,Telecom policy networks and digital services.,https://moc.gov.iq/rss,14,Tecnología,83,Irak,en,False,30 +6619,University of Technology Iraq,Engineering technology research and innovation.,https://uotechnology.edu.iq/rss,14,Tecnología,83,Irak,en,False,30 +6621,Zain Iraq Tecnologia,Mobile technology telecom and connectivity.,https://www.zain.com/en/rss,14,Tecnología,83,Irak,en,False,30 +6746,AllAfrica Ireland Ciencia,Aggregated science research references.,https://allafrica.com/tools/headlines/rdf/ireland/science.rdf,1,Ciencia,85,Irlanda,en,False,30 +6739,Environmental Protection Agency Ireland Ciencia,Environment climate science and research.,https://www.epa.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6738,Health Research Board Ireland Ciencia,Medical research public health and science.,https://www.hrb.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6737,Irish Examiner Ciencia,Science research innovation and education.,https://www.irishexaminer.com/rss/science.xml,1,Ciencia,85,Irlanda,en,False,30 +6734,Irish Times Ciencia,Science research health environment and innovation.,https://www.irishtimes.com/rss/science,1,Ciencia,85,Irlanda,en,False,30 +6740,Marine Institute Ireland Ciencia,Marine science fisheries and ocean research.,https://www.marine.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6741,Met Eireann Ciencia,Meteorology climate weather and research.,https://www.met.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6745,National University of Ireland Ciencia,University research science and education.,https://www.nui.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6735,RTE News Ciencia,Science research environment health and technology.,https://www.rte.ie/news/rss/science.xml,1,Ciencia,85,Irlanda,en,False,30 +6714,Science Foundation Ireland Ciencia,Research funding science and innovation.,https://www.sfi.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6710,Silicon Republic Ciencia,Science research innovation and technology.,https://www.siliconrepublic.com/feed,1,Ciencia,85,Irlanda,en,True,0 +6742,Teagasc Ciencia,Agricultural science food research and innovation.,https://www.teagasc.ie/rss,1,Ciencia,85,Irlanda,en,True,0 +6736,The Journal Ciencia,Science reporting research health and environment.,https://www.thejournal.ie/science/feed/,1,Ciencia,85,Irlanda,en,False,30 +6743,Trinity College Dublin Ciencia,Academic research science and innovation.,https://www.tcd.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6744,University College Dublin Ciencia,Academic research science engineering and health.,https://www.ucd.ie/rss,1,Ciencia,85,Irlanda,en,False,30 +6700,BreakingNews Ie Economia,Economic headlines business and finance.,https://www.breakingnews.ie/business/feed/,4,Economía,85,Irlanda,en,False,30 +6703,CSO Ireland Economia,Official economic statistics and indicators.,https://www.cso.ie/en/rssfeeds/,4,Economía,85,Irlanda,en,False,30 +6702,Central Bank of Ireland Economia,Monetary policy banking and financial stability.,https://www.centralbank.ie/rss,4,Economía,85,Irlanda,en,False,30 +6701,Department of Finance Ireland Economia,Fiscal policy budget and public finance.,https://www.gov.ie/en/organisation/department-of-finance/rss/,4,Economía,85,Irlanda,en,False,30 +6698,Irish Examiner Economia,Business economy companies and markets.,https://www.irishexaminer.com/rss/business.xml,4,Economía,85,Irlanda,en,False,30 +6695,Irish Independent Economia,Economy business companies and markets.,https://www.independent.ie/rss/section/business,4,Economía,85,Irlanda,en,True,0 +6694,Irish Times Economia,Economic policy markets business and trade.,https://www.irishtimes.com/rss/business,4,Economía,85,Irlanda,en,False,30 +6699,Newstalk Economia,Business economy interviews and analysis.,https://www.newstalk.com/business/rss,4,Economía,85,Irlanda,en,False,30 +6696,RTE News Economia,Public broadcaster economy business and finance.,https://www.rte.ie/news/rss/business.xml,4,Economía,85,Irlanda,en,False,30 +6697,The Journal Economia,Economic reporting business and public finance.,https://www.thejournal.ie/business/feed/,4,Economía,85,Irlanda,en,False,30 +6706,World Bank Ireland Economia,Development indicators and economic analysis.,https://www.worldbank.org/en/country/ireland/rss,4,Economía,85,Irlanda,en,False,30 +6678,AllAfrica Ireland Internacional,Aggregated international references.,https://allafrica.com/tools/headlines/rdf/ireland/headlines.rdf,7,Internacional,85,Irlanda,en,False,30 +6677,BBC News Ireland Internacional,UK Ireland global affairs and diplomacy.,https://feeds.bbci.co.uk/news/northern_ireland/rss.xml,7,Internacional,85,Irlanda,en,True,0 +6752,BreakingNews Ie Internacional,World headlines global affairs and conflicts.,https://www.breakingnews.ie/world/feed/,7,Internacional,85,Irlanda,en,False,30 +6674,Department of Foreign Affairs Ireland Internacional,Official foreign policy diplomacy and statements.,https://www.gov.ie/en/organisation/department-of-foreign-affairs/rss/,7,Internacional,85,Irlanda,en,False,30 +6753,European Commission Ireland Internacional,EU policy diplomacy and international affairs.,https://commission.europa.eu/rss_en,7,Internacional,85,Irlanda,en,False,30 +6675,European Parliament Ireland Internacional,EU diplomacy foreign affairs and policy.,https://www.europarl.europa.eu/rss/en/press-room.xml,7,Internacional,85,Irlanda,en,False,30 +6751,Irish Examiner Internacional,International politics diplomacy and security.,https://www.irishexaminer.com/rss/world.xml,7,Internacional,85,Irlanda,en,False,30 +6748,Irish Independent Internacional,World news diplomacy conflicts and analysis.,https://www.independent.ie/rss/section/world-news,7,Internacional,85,Irlanda,en,True,0 +6747,Irish Times Internacional,International affairs diplomacy and world news.,https://www.irishtimes.com/rss/world,7,Internacional,85,Irlanda,en,False,30 +6670,Newstalk Internacional,International affairs interviews and analysis.,https://www.newstalk.com/news/rss,7,Internacional,85,Irlanda,en,False,30 +6749,RTE News Internacional,International news global affairs and diplomacy.,https://www.rte.ie/news/rss/world.xml,7,Internacional,85,Irlanda,en,False,30 +6755,ReliefWeb Ireland Internacional,Humanitarian crises emergencies and response.,https://reliefweb.int/rss.xml?country=IRL,7,Internacional,85,Irlanda,en,False,30 +6676,Reuters Ireland Internacional,International news related to Ireland.,https://www.reuters.com/rssFeed/irelandNews,7,Internacional,85,Irlanda,en,False,30 +6750,The Journal Internacional,World news diplomacy conflicts and global affairs.,https://www.thejournal.ie/world/feed/,7,Internacional,85,Irlanda,en,False,30 +6754,United Nations Ireland Internacional,UN activities peace development and aid.,https://www.un.org/en/rss/ireland.xml,7,Internacional,85,Irlanda,en,False,30 +6668,BreakingNews Ie Politica,Political headlines government and elections.,https://www.breakingnews.ie/politics/feed/,11,Política,85,Irlanda,en,False,30 +6684,Connacht Tribune Politica,Regional politics western Ireland affairs.,https://connachttribune.ie/feed/,11,Política,85,Irlanda,en,True,0 +6672,Dail Eireann Politica,Parliament activity bills and debates.,https://www.oireachtas.ie/en/rss/,11,Política,85,Irlanda,en,False,30 +6673,Department of Taoiseach Politica,Official government statements and policy.,https://www.gov.ie/en/organisation/department-of-the-taoiseach/rss/,11,Política,85,Irlanda,en,False,30 +6687,Donegal Daily Politica,Political reporting Donegal councils and policy.,https://www.donegaldaily.com/feed/,11,Política,85,Irlanda,en,True,0 +6681,FM104 Politica,Political news Dublin and national coverage.,https://www.fm104.ie/rss/news/,11,Política,85,Irlanda,en,False,30 +6692,Galway Bay FM Politica,Political interviews and regional affairs.,https://www.galwaybayfm.ie/feed/,11,Política,85,Irlanda,en,False,30 +6671,Irish Examiner Politica,Politics government policy and society.,https://www.irishexaminer.com/rss/politics.xml,11,Política,85,Irlanda,en,False,30 +6665,Irish Independent Politica,Politics government elections and analysis.,https://www.independent.ie/rss/section/politics,11,Política,85,Irlanda,en,True,0 +6679,Irish Sun Politica,Political headlines government and elections.,https://www.thesun.ie/news/irish-news/feed/,11,Política,85,Irlanda,en,True,0 +6664,Irish Times Politica,Political news government parliament and policy.,https://www.irishtimes.com/rss/politics,11,Política,85,Irlanda,en,False,30 +6685,Kerry Live Politica,Local political reporting and public affairs.,https://www.kerrylive.ie/rss,11,Política,85,Irlanda,en,False,30 +6686,Leinster Leader Politica,County politics councils and governance.,https://www.leinsterleader.ie/rss,11,Política,85,Irlanda,en,False,30 +6683,Limerick Leader Politica,Local politics councils and elections.,https://www.limerickleader.ie/rss,11,Política,85,Irlanda,en,False,30 +6690,Longford Leader Politica,Local politics councils and representatives.,https://www.longfordleader.ie/rss,11,Política,85,Irlanda,en,False,30 +6691,Meath Chronicle Politica,County politics governance and elections.,https://www.meathchronicle.ie/rss,11,Política,85,Irlanda,en,True,0 +6693,Offaly Express Politica,Local political coverage and councils.,https://www.offalyexpress.ie/rss,11,Política,85,Irlanda,en,False,30 +6666,RTE News Politica,Public broadcaster political coverage.,https://www.rte.ie/news/rss/politics.xml,11,Política,85,Irlanda,en,False,30 +6667,The Journal Politica,Political reporting policy and public affairs.,https://www.thejournal.ie/politics/feed/,11,Política,85,Irlanda,en,False,30 +6689,Tipperary Live Politica,Regional political developments and councils.,https://www.tipperarylive.ie/rss,11,Política,85,Irlanda,en,False,30 +6680,Today FM Politica,Political interviews debates and current affairs.,https://www.todayfm.com/rss/news,11,Política,85,Irlanda,en,False,30 +6682,Waterford News Politica,Regional political news and local government.,https://www.waterford-news.ie/rss,11,Política,85,Irlanda,en,False,30 +6688,Westmeath Independent Politica,Local government elections and policy.,https://www.westmeathindependent.ie/rss,11,Política,85,Irlanda,en,True,0 +6725,AI Ireland Tecnologia,Artificial intelligence research and events.,https://www.aiireland.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6718,AllAfrica Ireland Tecnologia,Aggregated technology references.,https://allafrica.com/tools/headlines/rdf/ireland/ict.rdf,14,Tecnología,85,Irlanda,en,False,30 +6727,Blockchain Ireland Tecnologia,Blockchain technology innovation and policy.,https://blockchainireland.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6669,Business Post Tecnologia,Technology business digital economy.,https://www.businesspost.ie/feed/,14,Tecnología,85,Irlanda,en,False,30 +6732,CONNECT Research Ireland Tecnologia,Telecom networks IoT and research.,https://connectcentre.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6728,Cloud Industry Forum Ireland Tecnologia,Cloud computing industry and standards.,https://www.cloudindustryforum.org/feed/,14,Tecnología,85,Irlanda,en,True,0 +6724,Cyber Ireland Tecnologia,Cybersecurity innovation startups and policy.,https://www.cyberireland.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6716,Data Protection Commission Ireland Tecnologia,Data protection technology and regulation.,https://www.dataprotection.ie/rss,14,Tecnología,85,Irlanda,en,False,30 +6726,Data Science Institute Ireland Tecnologia,Data science analytics and research.,https://www.datascienceinstitute.ie/feed/,14,Tecnología,85,Irlanda,en,False,30 +6705,Enterprise Ireland Tecnologia,Innovation technology exports and startups.,https://www.enterprise-ireland.com/rss,14,Tecnología,85,Irlanda,en,False,30 +6717,European Tech Ireland Tecnologia,EU digital policy and innovation.,https://digital-strategy.ec.europa.eu/en/rss,14,Tecnología,85,Irlanda,en,False,30 +6715,HEA Ireland Tecnologia,Higher education research and technology.,https://hea.ie/rss,14,Tecnología,85,Irlanda,en,False,30 +6722,ICT Ireland Tecnologia,Information technology industry and policy.,https://www.ictireland.ie/rss,14,Tecnología,85,Irlanda,en,False,30 +6704,IDA Ireland Tecnologia,Foreign tech investment and industry.,https://www.idaireland.com/rss,14,Tecnología,85,Irlanda,en,False,30 +6721,ITAG Ireland Tecnologia,Technology industry policy and innovation.,https://itag.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6733,Insight Centre Ireland Tecnologia,Data analytics AI and research.,https://www.insight-centre.org/feed/,14,Tecnología,85,Irlanda,en,True,0 +6723,Irish Computer Society Tecnologia,IT professionals cybersecurity and research.,https://www.ics.ie/rss,14,Tecnología,85,Irlanda,en,False,30 +6708,Irish Independent Tecnologia,Technology business startups and innovation.,https://www.independent.ie/rss/section/technology,14,Tecnología,85,Irlanda,en,True,0 +6712,Irish Tech News Tecnologia,Technology startups AI and innovation.,https://irishtechnews.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6707,Irish Times Tecnologia,Technology innovation digital policy and science.,https://www.irishtimes.com/rss/technology,14,Tecnología,85,Irlanda,en,False,30 +6729,National Digital Strategy Ireland Tecnologia,Digital policy government and technology.,https://www.gov.ie/en/policy-information/rss/,14,Tecnología,85,Irlanda,en,False,30 +6709,RTE News Tecnologia,Technology digital economy and innovation.,https://www.rte.ie/news/rss/technology.xml,14,Tecnología,85,Irlanda,en,False,30 +6730,Smart Dublin Tecnologia,Smart city technology and innovation.,https://smartdublin.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6720,StartupBlink Ireland Tecnologia,Startup data innovation and technology.,https://www.startupblink.com/blog/feed/,14,Tecnología,85,Irlanda,en,True,0 +6719,Tech Ireland Tecnologia,Startup ecosystem venture capital and technology.,https://www.techireland.org/feed,14,Tecnología,85,Irlanda,en,False,30 +6711,TechCentral Ireland Tecnologia,IT news cybersecurity and digital trends.,https://www.techcentral.ie/feed/,14,Tecnología,85,Irlanda,en,False,30 +6713,The Journal Tecnologia,Technology innovation and digital society.,https://www.thejournal.ie/technology/feed/,14,Tecnología,85,Irlanda,en,False,30 +6731,Tyndall National Institute Tecnologia,Microelectronics photonics and research.,https://www.tyndall.ie/feed/,14,Tecnología,85,Irlanda,en,True,0 +6663,AllAfrica Iran Ciencia,Aggregated science and research references.,https://allafrica.com/tools/headlines/rdf/iran/science.rdf,1,Ciencia,84,Irán,en,False,30 +6657,Amirkabir University Ciencia,Scientific research engineering and innovation.,https://aut.ac.ir/rss,1,Ciencia,84,Irán,en,False,30 +6650,IRNA Ciencia,Official science research and development news.,https://www.irna.ir/rss,1,Ciencia,84,Irán,en,True,0 +6651,ISNA Ciencia,Science research universities and innovation.,https://www.isna.ir/rss,1,Ciencia,84,Irán,en,True,0 +6660,Iran National Science Foundation,Ciencia funding research grants and projects.,https://insf.org/rss,1,Ciencia,84,Irán,en,False,30 +6662,Iranian Space Agency Ciencia,Space science satellites and research.,https://isa.ir/rss,1,Ciencia,84,Irán,en,False,30 +6658,Isfahan University Ciencia,Academic science research and studies.,https://ui.ac.ir/rss,1,Ciencia,84,Irán,en,False,30 +6652,Mehr News Ciencia,Scientific research environment and technology.,https://www.mehrnews.com/rss,1,Ciencia,84,Irán,en,True,0 +6659,Ministry of Science Iran Ciencia,Science policy research and universities.,https://www.msrt.ir/rss,1,Ciencia,84,Irán,en,False,30 +6661,Pasteur Institute Iran Ciencia,Medical research public health and science.,https://www.pasteur.ac.ir/rss,1,Ciencia,84,Irán,en,False,30 +6654,Press TV Ciencia,Science innovation research and education.,https://www.presstv.ir/rss,1,Ciencia,84,Irán,en,False,30 +6656,Sharif University Ciencia,Engineering science research and technology.,https://www.sharif.edu/rss,1,Ciencia,84,Irán,en,False,30 +6653,Tasnim News Ciencia,Science policy research and innovation.,https://www.tasnimnews.com/en/rss,1,Ciencia,84,Irán,en,False,30 +6649,Tehran Times Ciencia,Science research innovation and education.,https://www.tehrantimes.com/rss,1,Ciencia,84,Irán,en,True,0 +6655,University of Tehran Ciencia,Academic research science and innovation.,https://ut.ac.ir/rss,1,Ciencia,84,Irán,en,False,30 +6641,Aftab News Politica,Political developments public policy and debates.,https://aftabnews.ir/rss,11,Política,84,Irán,en,False,30 +6644,Assembly of Experts Politica,Clerical assembly decisions and statements.,https://khobregan.ir/rss,11,Política,84,Irán,en,False,30 +6647,Atlantic Council Iran Politica,Policy analysis security and diplomacy.,https://www.atlanticcouncil.org/tag/iran/feed/,11,Política,84,Irán,en,True,0 +6648,Carnegie Endowment Iran Politica,Political analysis governance and foreign policy.,https://carnegieendowment.org/rss/region/iran,11,Política,84,Irán,en,False,30 +6643,Dolat IR Politica,Official government communications and policy.,https://dolat.ir/rss,11,Política,84,Irán,en,False,30 +6639,Entekhab Politica,Political headlines government parliament and policy.,https://www.entekhab.ir/rss,11,Política,84,Irán,en,False,30 +6635,Etemad Politica,Political analysis reformist perspectives.,https://www.etemadonline.com/rss,11,Política,84,Irán,en,False,30 +6645,Expediency Council Iran Politica,Policy arbitration decisions and governance.,https://www.maslahat.ir/rss,11,Política,84,Irán,en,False,30 +6642,Fararu Politica,Political commentary governance and elections.,https://fararu.com/rss,11,Política,84,Irán,en,False,30 +6646,Iran Human Rights Documentation Politica,Political rights governance and accountability.,https://www.iranhrdc.org/feed/,11,Política,84,Irán,en,True,0 +6637,Jomhouri Eslami Politica,Political viewpoints government and clerical affairs.,https://www.jomhourieslami.com/rss,11,Política,84,Irán,en,False,30 +6634,Kayhan Politica,Political commentary government policy and ideology.,https://kayhan.ir/rss,11,Política,84,Irán,en,False,30 +6638,Roozaneh Politica,Political news domestic affairs and governance.,https://roozanehonline.com/rss,11,Política,84,Irán,en,False,30 +6636,Shargh Daily Politica,Political reporting policy and society.,https://www.sharghdaily.com/rss,11,Política,84,Irán,en,False,30 +6640,Tabnak Politica,Political analysis security and institutions.,https://www.tabnak.ir/rss,11,Política,84,Irán,en,False,30 +6350,crisisgroup.org Irán,,https://www.crisisgroup.org/rss/85,11,Política,84,Irán,,True,0 +6795,AllAfrica Iceland Ciencia,Aggregated science research references.,https://allafrica.com/tools/headlines/rdf/iceland/science.rdf,1,Ciencia,86,Islandia,en,False,30 +6792,Health Directorate Iceland Ciencia,Public health science and research.,https://www.landlaeknir.is/rss,1,Ciencia,86,Islandia,en,False,30 +6790,Icelandic Institute of Natural History Ciencia,Biodiversity ecology and conservation research.,https://www.ni.is/rss,1,Ciencia,86,Islandia,en,False,30 +6788,Icelandic Meteorological Office Ciencia,Weather climate volcanology and research.,https://en.vedur.is/rss,1,Ciencia,86,Islandia,en,False,30 +6794,International Arctic Science Committee Ciencia,Polar science research and cooperation.,https://iasc.info/rss,1,Ciencia,86,Islandia,en,False,30 +6791,Landspitali University Hospital Ciencia,Medical research health and science.,https://www.landspitali.is/rss,1,Ciencia,86,Islandia,en,False,30 +6789,Marine and Freshwater Research Institute Ciencia,Marine science fisheries and ecosystem research.,https://www.hafogvatn.is/rss,1,Ciencia,86,Islandia,en,False,30 +6782,Reykjavik University Ciencia,Engineering science research and technology.,https://en.ru.is/rss,1,Ciencia,86,Islandia,en,False,30 +6781,University of Iceland Ciencia,Academic research science and innovation.,https://www.hi.is/rss,1,Ciencia,86,Islandia,en,False,30 +6767,AllAfrica Iceland Internacional,Aggregated international references.,https://allafrica.com/tools/headlines/rdf/iceland/headlines.rdf,7,Internacional,86,Islandia,en,False,30 +6793,Arctic Council Iceland Internacional,Arctic cooperation diplomacy and policy.,https://arctic-council.org/rss/,7,Internacional,86,Islandia,en,False,30 +6766,BBC News Iceland Internacional,World coverage Nordic region and Iceland.,https://feeds.bbci.co.uk/news/world/europe/rss.xml,7,Internacional,86,Islandia,en,True,0 +6796,European Economic Area Iceland Internacional,EEA relations trade and diplomacy.,https://www.efta.int/rss,7,Internacional,86,Islandia,en,False,30 +6762,Government of Iceland Internacional,International policy agreements and relations.,https://www.government.is/rss/,7,Internacional,86,Islandia,en,False,30 +6756,Iceland Review Internacional,International affairs diplomacy and world news.,https://www.icelandreview.com/rss/,7,Internacional,86,Islandia,en,True,0 +6763,Ministry for Foreign Affairs Iceland Internacional,Official foreign policy diplomacy and statements.,https://www.government.is/ministries/ministry-for-foreign-affairs/rss/,7,Internacional,86,Islandia,en,False,30 +6758,Morgunbladid Internacional,World news diplomacy conflicts and analysis.,https://www.mbl.is/rss/,7,Internacional,86,Islandia,is,False,30 +6797,Nordic Council Iceland Internacional,Nordic cooperation diplomacy and policy.,https://www.norden.org/en/rss,7,Internacional,86,Islandia,en,False,30 +6757,RUV Internacional,International news diplomacy and global affairs.,https://www.ruv.is/rss/frettir,7,Internacional,86,Islandia,is,True,0 +6799,ReliefWeb Iceland Internacional,Humanitarian crises emergencies and response.,https://reliefweb.int/rss.xml?country=ISL,7,Internacional,86,Islandia,en,False,30 +6765,Reuters Iceland Internacional,International news related to Iceland.,https://www.reuters.com/rssFeed/icelandNews,7,Internacional,86,Islandia,en,False,30 +6760,Stundin Internacional,Investigative international reporting and policy.,https://stundin.is/rss,7,Internacional,86,Islandia,is,True,0 +6798,United Nations Iceland Internacional,UN activities peace development and aid.,https://www.un.org/en/rss/iceland.xml,7,Internacional,86,Islandia,en,False,30 +6759,Visir Internacional,International affairs security and diplomacy.,https://www.visir.is/rss,7,Internacional,86,Islandia,is,False,30 +6770,Akureyri Municipality Politica,Local government policy north Iceland.,https://www.akureyri.is/rss,11,Política,86,Islandia,is,False,30 +6761,Althingi Parlament Politica,Parliament activity bills and debates.,https://www.althingi.is/rss/,11,Política,86,Islandia,en,True,0 +6777,Association of Icelandic Municipalities Politica,Local government coordination and policy.,https://www.samband.is/rss,11,Política,86,Islandia,is,False,30 +6776,Egilsstadir Municipality Politica,Local government eastern Iceland affairs.,https://www.fljotsdalsherad.is/rss,11,Política,86,Islandia,is,False,30 +6771,Hafnarfjordur Municipality Politica,City council local governance and policy.,https://www.hafnarfjordur.is/rss,11,Política,86,Islandia,is,True,0 +6778,Icelandic Local Elections Politica,Local elections councils and democracy.,https://www.kjorsysla.is/rss,11,Política,86,Islandia,is,False,30 +6775,Isafjordur Municipality Politica,Local council policy Westfjords region.,https://www.isafjordur.is/rss,11,Política,86,Islandia,is,False,30 +6772,Kopavogur Municipality Politica,Local government council decisions and policy.,https://www.kopavogur.is/rss,11,Política,86,Islandia,is,False,30 +6764,Prime Minister Office Iceland,Executive policy announcements and governance.,https://www.government.is/ministries/prime-ministers-office/rss/,11,Política,86,Islandia,en,False,30 +6773,Reykjanesbaer Municipality Politica,Local council governance and development.,https://www.reykjanesbaer.is/rss,11,Política,86,Islandia,is,False,30 +6769,Reykjavik City Hall Politica,City council decisions budgets and local policy.,https://reykjavik.is/rss,11,Política,86,Islandia,is,False,30 +6774,Selfoss Municipality Politica,Local governance councils and policy.,https://www.arborg.is/rss,11,Política,86,Islandia,is,False,30 +6787,AllAfrica Iceland Tecnologia,Aggregated technology references.,https://allafrica.com/tools/headlines/rdf/iceland/ict.rdf,14,Tecnología,86,Islandia,en,False,30 +6783,Arctic Green Energy Tecnologia,Renewable energy geothermal technology.,https://www.arcticgreenenergy.com/feed/,14,Tecnología,86,Islandia,en,True,0 +6780,Digital Iceland Tecnologia,Digital government services and technology.,https://island.is/rss,14,Tecnología,86,Islandia,en,False,30 +6785,Iceland Space Agency Tecnologia,Space research satellites and technology.,https://spaceiceland.is/rss,14,Tecnología,86,Islandia,en,False,30 +6779,Icelandic Innovation Center Tecnologia,Innovation startups technology and research.,https://www.innovationcenter.is/rss,14,Tecnología,86,Islandia,en,False,30 +6784,Landsvirkjun Tecnologia,Energy technology power innovation and research.,https://www.landsvirkjun.com/rss,14,Tecnología,86,Islandia,en,False,30 +6768,Reykjavik Grapevine Tecnologia,Technology startups digital culture and science.,https://grapevine.is/feed/,14,Tecnología,86,Islandia,en,True,0 +6786,Statistics Iceland Tecnologia,Data technology analytics and digital services.,https://statice.is/rss,14,Tecnología,86,Islandia,en,False,30 +6814,Asian Development Bank RMI Economia,Development finance economy and infrastructure.,https://www.adb.org/countries/marshall-islands/rss,4,Economía,87,Islas Marshall,en,False,30 +6813,Central Statistics Office RMI Economia,Official economic statistics and indicators.,https://www.rmistats.gov.mh/rss,4,Economía,87,Islas Marshall,en,False,30 +6819,Compact of Free Association Economia,US RMI economic cooperation and funding.,https://www.rmiembassyus.org/compact/rss,4,Economía,87,Islas Marshall,en,False,30 +6812,Economic Policy Planning RMI Economia,Economic development planning and policy.,https://www.rmiembassyus.org/economy/rss,4,Economía,87,Islas Marshall,en,False,30 +6817,Fisheries Marshall Islands Economia,Marine economy fisheries and exports.,https://www.mimra.com/rss,4,Economía,87,Islas Marshall,en,False,30 +6433,IMF Marshall Islands Economia,Macroeconomic outlook fiscal and monetary.,https://www.imf.org/rss,4,Economía,87,Islas Marshall,en,False,30 +6800,Marshall Islands Journal Economia,Economy business trade and public finance.,https://marshallislandsjournal.com/feed/,4,Economía,87,Islas Marshall,en,True,0 +6811,Ministry of Finance RMI Economia,Fiscal policy budget and public finance.,https://rmi-mf.gov.mh/rss,4,Economía,87,Islas Marshall,en,False,30 +6816,Pacific Islands Trade Economia,Regional trade economy and development.,https://www.pacifictradeinvest.com/feed/,4,Economía,87,Islas Marshall,en,False,30 +6810,ReliefWeb RMI Economia,Humanitarian funding and economic recovery.,https://reliefweb.int/rss.xml?country=MHL,4,Economía,87,Islas Marshall,en,False,30 +6818,Tourism Marshall Islands Economia,Tourism economy industry and development.,https://visitmarshallislands.com/feed/,4,Economía,87,Islas Marshall,en,True,0 +6815,World Bank RMI Economia,Development indicators economic analysis.,https://www.worldbank.org/en/country/marshallislands/rss,4,Economía,87,Islas Marshall,en,False,30 +413,ABC Pacific RMI Politica,Pacific politics governance and regional affairs.,https://www.abc.net.au/news/feed/51120/rss.xml,11,Política,87,Islas Marshall,en,True,0 +6802,Government of RMI Politica,Government policy legislation and governance.,https://www.rmiembassyus.org/rss,11,Política,87,Islas Marshall,en,False,30 +6804,Ministry of Foreign Affairs RMI Politica,Foreign policy diplomacy and relations.,https://www.rmiembassyus.org/news/rss,11,Política,87,Islas Marshall,en,False,30 +6805,Ministry of Justice RMI Politica,Justice policy courts and legislation.,https://justice.gov.mh/rss,11,Política,87,Islas Marshall,en,False,30 +6803,Nitijela Parliament Politica,Parliament debates laws and resolutions.,https://nitijela.gov.mh/rss,11,Política,87,Islas Marshall,en,False,30 +6801,Office of the President RMI Politica,Official presidential statements and policy.,https://rmipresident.org/rss,11,Política,87,Islas Marshall,en,False,30 +6807,Pacific Islands News RMI Politica,Regional political news Marshall Islands.,https://www.pina.com.fj/feed/,11,Política,87,Islas Marshall,en,True,0 +6806,Public Service Commission RMI Politica,Public administration governance and reform.,https://psc.gov.mh/rss,11,Política,87,Islas Marshall,en,False,30 +6808,Radio New Zealand Pacific RMI Politica,Pacific regional politics and diplomacy.,https://www.rnz.co.nz/international/pacific/rss,11,Política,87,Islas Marshall,en,False,30 +6809,United Nations RMI Politica,UN relations development and governance.,https://www.un.org/en/rss/marshall-islands.xml,11,Política,87,Islas Marshall,en,False,30 +6917,ANU Pacific Research,Australian research on Solomon Islands.,https://www.anu.edu.au/rss/pacific/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6905,Agriculture Division Solomon,Agricultural research and science.,https://www.agriculture.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6912,CSIRO Pacific Research,Scientific research projects in Solomon.,https://www.csiro.au/rss/pacific/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6915,Coral Triangle Initiative,Coral reef science and marine research.,https://www.coraltriangleinitiative.org/rss/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6904,Fisheries Division Solomon,Marine science and fisheries research.,https://www.fisheries.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6906,Forestry Division Solomon,Forestry science and research updates.,https://www.forestry.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6902,Geological Survey Solomon,Geological research and earth sciences.,https://www.geology.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6907,Health Ministry Research,Public health research and medical science.,https://www.health.gov.sb/rss/research,1,Ciencia,88,Islas Salomón,en,False,30 +6901,Meteorology Service Solomon,Weather and climate science data.,https://www.met.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,True,0 +6903,Ministry of Environment,Environmental science and conservation.,https://www.environment.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6918,New Zealand Science Pacific,Science cooperation with Solomon.,https://www.science.org.nz/rss/pacific/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6909,Pacific Community SPC,Regional scientific research for Solomon.,https://www.spc.int/rss/solomon-islands,1,Ciencia,88,Islas Salomón,en,False,30 +6916,Pacific Islands Forum Science,Regional science policy and research.,https://www.forumsec.org/rss/science,1,Ciencia,88,Islas Salomón,en,False,30 +6910,SPREP Environmental,Environmental science for Pacific islands.,https://www.sprep.org/rss/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6900,Solomon Islands National University,Science education and research updates.,https://www.sin.edu.sb/rss/science,1,Ciencia,88,Islas Salomón,en,False,30 +6908,Solomon Islands Water Authority,Water science and hydrology research.,https://www.siwa.gov.sb/rss,1,Ciencia,88,Islas Salomón,en,False,30 +6914,The Nature Conservancy,Marine and terrestrial conservation science.,https://www.nature.org/rss/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6911,University of Queensland Pacific,Collaborative research in Solomon.,https://www.uq.edu.au/rss/pacific/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6899,University of South Pacific Science,Science programs and research at USP.,https://www.usp.ac.fj/rss/science/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6913,WWF Solomon Islands,Conservation science and research.,https://www.wwfpacific.org/rss/solomon,1,Ciencia,88,Islas Salomón,en,False,30 +6861,Bank Hapoalim,Economic research and market insights.,https://www.bankhapoalim.co.il/rss/economy,4,Economía,89,Israel,he,False,30 +6845,Bank of Israel,Monetary policy and financial stability reports.,https://www.boi.org.il/rss/economy,4,Economía,89,Israel,en,False,30 +6856,Bloomberg Israel,Financial markets and economic analysis.,https://www.bloomberg.com/rss/israel,4,Economía,89,Israel,en,False,30 +6843,Calcalist Economic News,Financial news and market analysis.,https://www.calcalist.co.il/rss/economy,4,Economía,89,Israel,he,False,30 +6847,Central Bureau of Statistics,Economic indicators and statistics.,https://www.cbs.gov.il/rss/economy,4,Economía,89,Israel,he,False,30 +6857,Financial Times Israel,Global perspective on Israeli economy.,https://www.ft.com/rss/israel-economy,4,Economía,89,Israel,en,False,30 +6844,Globes English,Israeli business and technology news.,https://www.globes.co.il/rss/business,4,Economía,89,Israel,en,False,30 +6841,Haaretz Business,Economic analysis and financial markets.,https://www.haaretz.com/rss/business,4,Economía,89,Israel,en,False,30 +6854,Histadrut Labor Union,Labor market and employment news.,https://www.histadrut.org.il/rss,4,Economía,89,Israel,he,False,30 +6850,IVC Research Center,Venture capital and high-tech reports.,https://www.ivc-online.com/rss,4,Economía,89,Israel,en,False,30 +6853,Israel Chamber of Commerce,Business news and commercial updates.,https://www.chamber.org.il/rss,4,Economía,89,Israel,he,False,30 +6864,Israel Discount Bank,Market research and economic updates.,https://www.discountbank.co.il/rss/economy,4,Economía,89,Israel,he,False,30 +6851,Israel Export Institute,Export data and international trade.,https://www.export.gov.il/rss,4,Economía,89,Israel,en,False,30 +6859,Israel Innovation Authority,Technology investment and R&D updates.,https://www.innovationisrael.org.il/rss,4,Economía,89,Israel,en,False,30 +6848,Israel Tax Authority,Tax news and regulations updates.,https://www.gov.il/rss/taxes,4,Economía,89,Israel,he,False,30 +6840,Jerusalem Post Economy,Israeli economic news and business updates.,https://www.jpost.com/rss/Business,4,Economía,89,Israel,en,False,30 +6862,Leumi Bank,Financial analysis and economic forecasts.,https://www.leumi.co.il/rss/economy,4,Economía,89,Israel,he,False,30 +6852,Manufacturers Association,Industrial production and manufacturing.,https://www.industry.org.il/rss,4,Economía,89,Israel,he,False,30 +6860,Ministry of Economy,Business regulations and economic policy.,https://www.gov.il/rss/economy,4,Economía,89,Israel,he,False,30 +6846,Ministry of Finance,Budget updates and fiscal policy.,https://www.gov.il/rss/finance,4,Economía,89,Israel,he,False,30 +6863,Mizrahi Tefahot Bank,Economic commentary and reports.,https://www.mizrahi-tefahot.co.il/rss/economy,4,Economía,89,Israel,he,False,30 +6855,Reuters Israel Economy,International economic coverage of Israel.,https://www.reuters.com/rss/israel-economy,4,Economía,89,Israel,en,False,30 +6849,Startup Nation Central,Israeli innovation and startup economy.,https://www.startupnationcentral.org/rss,4,Economía,89,Israel,en,False,30 +6858,The Marker,Hebrew economic newspaper business section.,https://www.themarker.com/rss/business,4,Economía,89,Israel,he,False,30 +6842,Times of Israel Business,Israeli economy and technology sector.,https://www.timesofisrael.com/rss/business,4,Economía,89,Israel,en,False,30 +6880,AIPAC International,US-Israel international policy updates.,https://www.aipac.org/rss,7,Internacional,89,Israel,en,False,30 +6873,Al-Monitor Middle East,Regional Middle East international coverage.,https://www.al-monitor.com/rss/middle-east,7,Internacional,89,Israel,en,False,30 +6885,All Israel News,International news covering Israel.,https://www.allisrael.com/rss,7,Internacional,89,Israel,en,False,30 +6879,BICOM International,British-Israeli international relations news.,https://www.bicom.org.uk/rss,7,Internacional,89,Israel,en,True,0 +6887,Camera International,International media accuracy monitoring.,https://www.camera.org/rss,7,Internacional,89,Israel,en,True,0 +6870,Channel 12 World News,International breaking news and events.,https://www.channel12news.com/rss/world,7,Internacional,89,Israel,he,False,30 +6871,Foreign Ministry Israel,Official international relations updates.,https://www.mfa.gov.il/rss/news,7,Internacional,89,Israel,en,False,30 +6866,Haaretz International,Global affairs and international relations.,https://www.haaretz.com/rss/world,7,Internacional,89,Israel,en,False,30 +6886,HonestReporting Intl,International media monitoring about Israel.,https://www.honestreporting.com/rss,7,Internacional,89,Israel,en,True,0 +6872,IDF International Updates,Military and security international news.,https://www.idf.il/rss/international,7,Internacional,89,Israel,en,False,30 +6877,INSS International,International security and strategic studies.,https://www.inss.org.il/rss/international,7,Internacional,89,Israel,en,False,30 +6868,Israel Hayom World,International news and foreign affairs.,https://www.israelhayom.com/rss/world,7,Internacional,89,Israel,en,False,30 +6874,Israel National News World,International news with Israeli focus.,https://www.israelnationalnews.com/rss/world,7,Internacional,89,Israel,en,False,30 +6878,Israel Project Intl,International media and public diplomacy.,https://www.theisraelproject.org/rss,7,Internacional,89,Israel,en,False,30 +6884,Israel Today Intl,International Christian news about Israel.,https://www.israeltoday.co.il/rss/international,7,Internacional,89,Israel,en,False,30 +6876,Jerusalem Center Intl,International affairs analysis and research.,https://www.jcpa.org/rss/international,7,Internacional,89,Israel,en,False,30 +6883,Jerusalem Online,International news from Jerusalem perspective.,https://www.jerusalemonline.com/rss/international,7,Internacional,89,Israel,en,False,30 +6865,Jerusalem Post International,International news from Israeli perspective.,https://www.jpost.com/rss/International,7,Internacional,89,Israel,en,False,30 +6882,Jewish Agency Intl,International Jewish and Israel news.,https://www.jewishagency.org/rss,7,Internacional,89,Israel,en,True,0 +6875,Jewish News Syndicate,International Jewish and Israel-related news.,https://www.jns.org/rss,7,Internacional,89,Israel,en,False,30 +6631,Middle East Eye,Middle East international news analysis.,https://www.middleeasteye.net/rss,7,Internacional,89,Israel,en,True,0 +6867,Times of Israel World News,International coverage and global events.,https://www.timesofisrael.com/rss/world,7,Internacional,89,Israel,en,False,30 +6888,UN Watch International,International UN and human rights news.,https://www.unwatch.org/rss,7,Internacional,89,Israel,en,True,0 +6881,World Jewish Congress,International Jewish community news.,https://www.worldjewishcongress.org/rss,7,Internacional,89,Israel,en,True,0 +6869,Ynet International,Global news coverage in Hebrew.,https://www.ynetnews.com/rss/international,7,Internacional,89,Israel,he,False,30 +6832,972 Magazine Politics,Left-leaning political analysis and commentary.,https://www.972mag.com/rss/politics,11,Política,89,Israel,en,False,30 +6836,AP News Israel,Political news from Associated Press.,https://apnews.com/rss/israel-politics,11,Política,89,Israel,en,False,30 +6829,Al-Monitor Israel Politics,Analysis of Israeli politics and regional affairs.,https://www.al-monitor.com/rss/israel-politics,11,Política,89,Israel,en,False,30 +6834,BBC Israel Politics,International coverage of Israeli politics.,https://www.bbc.com/rss/israel-politics,11,Política,89,Israel,en,False,30 +6825,Channel 12 News Politics,Breaking political news and investigations.,https://www.channel12news.com/rss/politics,11,Política,89,Israel,he,False,30 +6838,DW Israel Politics,German coverage of Israeli political affairs.,https://www.dw.com/rss/israel-politics,11,Política,89,Israel,de,False,30 +6828,Foreign Ministry Israel,Foreign policy and diplomatic relations.,https://www.mfa.gov.il/rss/politics,11,Política,89,Israel,en,False,30 +6837,France 24 Israel,French perspective on Israeli politics.,https://www.france24.com/rss/israel-politics,11,Política,89,Israel,fr,True,0 +6839,Haaretz Hebrew Politics,Haaretz political news in Hebrew.,https://www.haaretz.co.il/rss/politics,11,Política,89,Israel,he,False,30 +6821,Haaretz Politics,In-depth political coverage and commentary.,https://www.haaretz.com/rss/politics,11,Política,89,Israel,en,False,30 +6833,Israel Democracy Institute,Research and analysis on Israeli democracy.,https://www.idi.org.il/rss/politics,11,Política,89,Israel,en,False,30 +6823,Israel Hayom Politics,Right-leaning political news and updates.,https://www.israelhayom.com/rss/politics,11,Política,89,Israel,en,False,30 +6830,Israel National News,Political news from nationalist perspective.,https://www.israelnationalnews.com/rss/politics,11,Política,89,Israel,en,False,30 +6820,Jerusalem Post Politics,Israeli political news and analysis.,https://www.jpost.com/rss/Politics,11,Política,89,Israel,en,False,30 +6826,Knesset Official Updates,Official parliamentary proceedings and legislation.,https://www.knesset.gov.il/rss/politics,11,Política,89,Israel,he,False,30 +6831,Middle East Eye Israel,Regional political analysis and coverage.,https://www.middleeasteye.net/rss/israel-politics,11,Política,89,Israel,en,False,30 +6827,Prime Minister Office,Official statements and government announcements.,https://www.gov.il/rss/politics,11,Política,89,Israel,he,False,30 +6835,Reuters Israel Politics,Global news agency coverage of Israel.,https://www.reuters.com/rss/israel-politics,11,Política,89,Israel,en,False,30 +6822,Times of Israel Politics,Political developments and government news.,https://www.timesofisrael.com/rss/politics,11,Política,89,Israel,en,False,30 +6824,Ynet News Politics,Comprehensive political coverage in Hebrew.,https://www.ynetnews.com/rss/politics,11,Política,89,Israel,he,False,30 +6947,AGI Scienza,Agenzia giornalistica scientifica italiana.,https://www.agi.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6943,ANSA Scienza e Tecnologia,Notizie scientifiche e tecnologiche italiane.,https://www.ansa.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6960,ASI Agenzia Spaziale Italiana,Attivita spaziali e astronautica.,https://www.asi.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6948,Adnkronos Scienza,Notizie scientifiche e tecnologiche.,https://www.adnkronos.com/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6958,CNR Consiglio Nazionale Ricerche,Ricerca scientifica italiana.,https://www.cnr.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6944,Corriere della Sera Scienza,News e approfondimenti scientifici.,https://www.corriere.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6949,Focus Scienza,Rivista scientifica italiana.,https://www.focus.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6951,Galileo Scienza,Giornale di scienza e problemi globali.,https://www.galileonet.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6959,INFN Istituto Nazionale Fisica Nucleare,Ricerca in fisica nucleare.,https://www.infn.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6961,INGV Istituto Nazionale Geofisica,Scienze della terra e vulcanologia.,https://www.ingv.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6962,ISS Istituto Superiore di Sanita,Salute pubblica e ricerca medica.,https://www.iss.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6956,Il Messaggero Scienza,Notizie scientifiche e di ricerca.,https://www.ilmessaggero.it/rss/scienza.xml,1,Ciencia,90,Italia,it,True,0 +6946,Il Sole 24 Ore Scienza,Notizie su scienza e innovazione.,https://www.ilsole24ore.com/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6945,La Repubblica Scienza,Attualita scientifica e ricerca.,https://www.repubblica.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6965,La Sapienza Roma,Ricerca scientifica universitaria.,https://www.uniroma1.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6957,La Stampa Scienza,Notizie scientifiche e analisi.,https://www.lastampa.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6952,Le Scienze,Edizione italiana di Scientific American.,https://www.lescienze.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6953,National Geographic Italia,Scienza e esplorazione.,https://www.nationalgeographic.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6964,Politecnico di Milano,Ricerca in ingegneria e architettura.,https://www.polimi.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6954,Rai News Scienza,Notizie scientifiche dalla televisione pubblica.,https://www.rainews.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6967,Science News Italy,Notizie scientifiche in inglese.,https://www.sciencemag.org/rss/italy.xml,1,Ciencia,90,Italia,en,False,30 +6955,TGcom24 Scienza,Notizie scientifiche in tempo reale.,https://www.tgcom24.mediaset.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6963,Universita di Bologna Scienza,Ricerca scientifica universitaria.,https://www.unibo.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6966,Universita di Padova,Ricerca e innovazione scientifica.,https://www.unipd.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +6950,Wired Italia,Innovazione e tecnologia italiana.,https://www.wired.it/rss/scienza.xml,1,Ciencia,90,Italia,it,False,30 +7063,IMF - Jamaica,Macroeconomic assessments and fiscal monitoring.,https://www.imf.org/en/Countries/JAM/rss,4,Economía,91,Jamaica,en,False,30 +7061,Inter-American Development Bank - Jamaica,Development projects and economic analysis.,https://www.iadb.org/en/country/jamaica/rss,4,Economía,91,Jamaica,en,False,30 +7060,Jamaica Chamber of Commerce,Business sector news and economic advocacy.,https://www.jamaicachamber.org.jm/news/rss,4,Economía,91,Jamaica,en,False,30 +7054,Jamaica Gleaner - Business,Business and economic news from leading newspaper.,https://jamaica-gleaner.com/section/business/feed/,4,Economía,91,Jamaica,en,False,30 +7055,Jamaica Observer - Business,Economic coverage and financial markets.,https://www.jamaicaobserver.com/business/feed/,4,Economía,91,Jamaica,en,True,0 +7057,Jamaica Promotions Corporation,Investment news and business opportunities.,https://www.jamprocorp.com/news/rss,4,Economía,91,Jamaica,en,False,30 +7056,Jamaica Stock Exchange,Financial market data and listed company announcements.,https://www.jamstockex.com/rss/,4,Economía,91,Jamaica,en,False,30 +7059,Planning Institute of Jamaica,Economic statistics and development planning.,https://www.pioj.gov.jm/category/news/feed/,4,Economía,91,Jamaica,en,False,30 +7058,Tax Administration Jamaica,Tax policy updates and revenue collection.,https://www.jamaicatax.gov.jm/news/rss,4,Economía,91,Jamaica,en,False,30 +7062,World Bank - Jamaica,Development indicators and economic reports.,https://www.worldbank.org/en/country/jamaica/rss,4,Economía,91,Jamaica,en,False,30 +7044,Jamaica Gleaner - Politics,Political news and analysis from Jamaica's oldest newspaper.,https://jamaica-gleaner.com/section/news/politics/feed/,11,Política,91,Jamaica,en,False,30 +7051,Jamaica Information Service,Government's official news agency with press releases.,https://jis.gov.jm/category/news/feed/,11,Política,91,Jamaica,en,False,30 +7045,Jamaica Observer - Politics,Political coverage and current affairs.,https://www.jamaicaobserver.com/politics/feed/,11,Política,91,Jamaica,en,True,0 +7047,Loop Jamaica - News,Caribbean news network covering Jamaican politics.,https://jamaica.loopnews.com/category/politics/feed/,11,Política,91,Jamaica,en,False,30 +7052,Our Today - Politics,Online news source with political commentary.,https://ourtoday.news/category/politics/feed/,11,Política,91,Jamaica,en,False,30 +7048,RJR News,Radio Jamaica's news portal with political updates.,https://rjrnewsonline.com/category/news/politics/feed/,11,Política,91,Jamaica,en,False,30 +7053,Radio Irie FM - News,News coverage from popular Jamaican radio station.,https://www.iriefm.net/category/news/feed/,11,Política,91,Jamaica,en,True,0 +7049,Television Jamaica - News,Broadcast news coverage including politics.,https://www.televisionjamaica.com/category/news/feed/,11,Política,91,Jamaica,en,False,30 +7050,The Parliament of Jamaica,Official parliamentary proceedings and legislative updates.,https://japarliament.gov.jm/rss/,11,Política,91,Jamaica,en,False,30 +7123,Jamaica Gleaner - News,General news covering social issues and community affairs.,https://jamaica-gleaner.com/section/news/feed/,13,Sociedad,91,Jamaica,en,False,30 +7127,Jamaica Information Service - Features,Government features on social programs and national development.,https://jis.gov.jm/category/features/feed/,13,Sociedad,91,Jamaica,en,False,30 +7124,Jamaica Observer - News,National news with social and community coverage.,https://www.jamaicaobserver.com/news/feed/,13,Sociedad,91,Jamaica,en,True,0 +7129,Jamaica Social Investment Fund,Community development projects and social initiatives.,https://jsif.org/news/rss/,13,Sociedad,91,Jamaica,en,False,30 +7046,Jamaica Star - News,Local news and social developments across Jamaica.,https://jamaica-star.com/section/news/feed/,13,Sociedad,91,Jamaica,en,False,30 +7125,Loop Jamaica - Community,Community news and social interest stories.,https://jamaica.loopnews.com/category/community/feed/,13,Sociedad,91,Jamaica,en,False,30 +7128,Ministry of Education Jamaica,Education system news and social development through schools.,https://moey.gov.jm/category/news/feed/,13,Sociedad,91,Jamaica,en,False,30 +7130,Our Today - Society,Society news and lifestyle features.,https://ourtoday.news/category/society/feed/,13,Sociedad,91,Jamaica,en,False,30 +7126,RJR News - Features,In-depth features on social and cultural topics.,https://rjrnewsonline.com/category/features/feed/,13,Sociedad,91,Jamaica,en,False,30 +7131,United Nations Jamaica,UN development programs and social initiatives in Jamaica.,https://jamaica.un.org/en/rss.xml,13,Sociedad,91,Jamaica,en,True,0 +7095,Digicel Jamaica,Mobile network updates and digital services.,https://www.digicelgroup.com/jm/en/news/rss.html,14,Tecnología,91,Jamaica,en,False,30 +7094,Flow Jamaica,Telecommunications company news and services.,https://www.discoverflow.co/jamaica/news/rss,14,Tecnología,91,Jamaica,en,False,30 +7093,Jamaica Computer Society,IT professional association news and events.,https://jcs.org.jm/news/feed/,14,Tecnología,91,Jamaica,en,False,30 +7089,Jamaica Gleaner - Tech,Technology section from leading Jamaican newspaper.,https://jamaica-gleaner.com/section/tech/feed/,14,Tecnología,91,Jamaica,en,False,30 +7090,Jamaica Observer - Sci-Tech,Science and technology coverage.,https://www.jamaicaobserver.com/sci-tech/feed/,14,Tecnología,91,Jamaica,en,True,0 +7097,Liberty Latin America - Jamaica,Telecom infrastructure and connectivity news.,https://www.lla.com/news-room/rss/,14,Tecnología,91,Jamaica,en,False,30 +7096,Our Today - Tech,Technology news and digital lifestyle.,https://ourtoday.news/category/tech/feed/,14,Tecnología,91,Jamaica,en,False,30 +7092,University of Technology Jamaica - Computing,Academic research and IT education news.,https://utech.edu.jm/faculty/computing/rss/,14,Tecnología,91,Jamaica,en,False,30 +7091,eGov Jamaica,E-government initiatives and digital transformation.,https://www.egovjamaica.gov.jm/news/feed/,14,Tecnología,91,Jamaica,en,False,30 +7178,Asahi Shimbun Economy,Economic section of Asahi Shimbun.,https://www.asahi.com/rss/economy.xml,4,Economía,92,Japón,ja,False,30 +7173,Asian Development Bank Japan,Development economics and projects.,https://www.adb.org/countries/japan/rss,4,Economía,92,Japón,en,False,30 +7168,Bank of Japan,Monetary policy and economic reports.,https://www.boj.or.jp/en/rss/index.htm,4,Economía,92,Japón,en,False,30 +7164,Bloomberg Japan,Financial and economic news about Japan.,https://www.bloomberg.com/japan/rss,4,Economía,92,Japón,en,False,30 +7184,CNBC Japan Business,CNBC business news about Japan.,https://www.cnbc.com/id/19799679/rss,4,Economía,92,Japón,en,False,30 +7165,Financial Times Japan,Economic analysis and news on Japan.,https://www.ft.com/japan?format=rss,4,Economía,92,Japón,en,True,0 +7185,Forbes Japan,Business and economy news from Forbes.,https://forbesjapan.com/rss,4,Economía,92,Japón,ja,False,30 +7175,IMF Japan,IMF reports and economic outlook.,https://www.imf.org/en/Countries/JPN/rss,4,Economía,92,Japón,en,False,30 +7170,Japan Business Federation,Keidanren business and economic news.,https://www.keidanren.or.jp/rss/english.xml,4,Economía,92,Japón,en,False,30 +7172,Japan Center for Economic Research,Economic research and analysis.,https://www.jcer.or.jp/en/rss,4,Economía,92,Japón,en,True,0 +7183,Japan Economic Newswire,Economic news wire service.,https://www.kyodonews.net/rss/economy,4,Economía,92,Japón,en,False,30 +7169,Japan External Trade Organization,Trade and investment news.,https://www.jetro.go.jp/rss/news.html,4,Economía,92,Japón,en,False,30 +7162,Japan Times Business,Business and economic news from Japan.,https://www.japantimes.co.jp/business/feed/,4,Economía,92,Japón,en,False,30 +7177,Mainichi Shimbun Economy,Economic news from Mainichi.,https://mainichi.jp/rss/etc/keizai.rss,4,Economía,92,Japón,ja,False,30 +7167,Ministry of Economy Japan,Official economic policies and data.,https://www.meti.go.jp/english/rss/index.html,4,Economía,92,Japón,en,False,30 +3817,Nikkei Asian Review,Asian business and economic news.,https://asia.nikkei.com/rss,4,Economía,92,Japón,en,False,30 +7176,Nikkei Business Daily,Japanese business and economy news.,https://www.nikkei.com/rss/business/,4,Economía,92,Japón,ja,False,30 +7180,Nikkei Sangyo Shimbun,Industry and technology economy news.,https://www.nikkan.co.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7163,Reuters Japan Business,Economic news from Japan by Reuters.,https://www.reuters.com/rss/japan-business,4,Economía,92,Japón,en,False,30 +7166,The Economist Japan,Economic analysis of Japan.,https://www.economist.com/japan/rss,4,Economía,92,Japón,en,False,30 +7171,Tokyo Stock Exchange,Stock market and financial news.,https://www.jpx.co.jp/rss/english.xml,4,Economía,92,Japón,en,False,30 +7174,World Bank Japan,Economic data and reports on Japan.,https://www.worldbank.org/en/country/japan/rss,4,Economía,92,Japón,en,False,30 +7179,Yomiuri Shimbun Economy,Economic news from Yomiuri.,https://www.yomiuri.co.jp/economy/feed/,4,Economía,92,Japón,ja,False,30 +7265,ダイヤモンド 経済,ダイヤモンドの経済ニュース,https://diamond.jp/rss/economy.xml,4,Economía,92,Japón,ja,False,30 +7181,ダイヤモンド・オンライン,ダイヤモンド・オンラインのビジネスニュース,https://diamond.jp/rss/dol.xml,4,Economía,92,Japón,ja,False,30 +7271,内閣府 経済,内閣府の経済関連ニュース,https://www.cao.go.jp/rss/economy.xml,4,Economía,92,Japón,ja,False,30 +7274,帝国データバンク,帝国データバンクの経済情報,https://www.tdb.co.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7276,日本経済団体連合会,経団連の経済政策ニュース,https://www.keidanren.or.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7186,日本経済新聞,日本経済新聞の主要経済ニュース,https://www.nikkei.com/rss1/keizai.rss,4,Economía,92,Japón,ja,False,30 +7201,日本経済新聞 エネルギー,日本経済新聞のエネルギー経済ニュース,https://www.nikkei.com/rss/energy.rss,4,Economía,92,Japón,ja,False,30 +7199,日本経済新聞 テクノロジー,日本経済新聞のテクノロジー経済ニュース,https://www.nikkei.com/rss/technology.rss,4,Economía,92,Japón,ja,False,30 +7193,日本経済新聞 マーケット,日本経済新聞のマーケットニュース,https://www.nikkei.com/rss/markets.rss,4,Economía,92,Japón,ja,False,30 +7204,日本経済新聞 不動産,日本経済新聞の不動産経済ニュース,https://www.nikkei.com/rss/realestate.rss,4,Economía,92,Japón,ja,False,30 +7194,日本経済新聞 企業,日本経済新聞の企業ニュース,https://www.nikkei.com/rss/company.rss,4,Economía,92,Japón,ja,False,30 +7207,日本経済新聞 商品,日本経済新聞の商品ニュース,https://www.nikkei.com/rss/commodity.rss,4,Economía,92,Japón,ja,False,30 +7196,日本経済新聞 国際,日本経済新聞の国際経済ニュース,https://www.nikkei.com/rss/world.rss,4,Economía,92,Japón,ja,False,30 +7195,日本経済新聞 政治・経済,日本経済新聞の政治・経済ニュース,https://www.nikkei.com/rss/politics.rss,4,Economía,92,Japón,ja,False,30 +7200,日本経済新聞 消費,日本経済新聞の消費経済ニュース,https://www.nikkei.com/rss/consumption.rss,4,Economía,92,Japón,ja,False,30 +7206,日本経済新聞 為替,日本経済新聞の為替ニュース,https://www.nikkei.com/rss/forex.rss,4,Economía,92,Japón,ja,False,30 +7202,日本経済新聞 環境,日本経済新聞の環境経済ニュース,https://www.nikkei.com/rss/environment.rss,4,Economía,92,Japón,ja,False,30 +7198,日本経済新聞 産業,日本経済新聞の産業ニュース,https://www.nikkei.com/rss/industry.rss,4,Economía,92,Japón,ja,False,30 +7208,日本経済新聞 経済指標,日本経済新聞の経済指標ニュース,https://www.nikkei.com/rss/economic-indicators.rss,4,Economía,92,Japón,ja,False,30 +7252,日本経済新聞 総合,日本経済新聞の総合経済ニュース,https://www.nikkei.com/rss/flash.rss,4,Economía,92,Japón,ja,False,30 +7205,日本経済新聞 貿易,日本経済新聞の貿易ニュース,https://www.nikkei.com/rss/trade.rss,4,Economía,92,Japón,ja,False,30 +7197,日本経済新聞 金融,日本経済新聞の金融ニュース,https://www.nikkei.com/rss/finance.rss,4,Economía,92,Japón,ja,False,30 +7203,日本経済新聞 雇用,日本経済新聞の雇用経済ニュース,https://www.nikkei.com/rss/employment.rss,4,Economía,92,Japón,ja,False,30 +7192,日本経済新聞 電子版,日本経済新聞電子版の経済ニュース,https://www.nikkei.com/rss/electronics.rss,4,Economía,92,Japón,ja,False,30 +7268,日本経済研究,日本経済研究センターの経済分析,https://www.jcer.or.jp/rss,4,Economía,92,Japón,ja,True,0 +7275,日本貿易振興機構,ジェトロの貿易経済ニュース,https://www.jetro.go.jp/rss/news.xml,4,Economía,92,Japón,ja,True,0 +7272,日本銀行 新着,日本銀行の新着情報,https://www.boj.or.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7254,日経MJ,日経MJのマーケティング経済ニュース,https://www.nikkei.com/rss/mj.rss,4,Economía,92,Japón,ja,False,30 +7191,日経ビジネス,日経ビジネスの経済ニュース,https://business.nikkei.com/rss/index.rdf,4,Economía,92,Japón,ja,False,30 +7253,日経ヴェリタス,日経ヴェリタスの経済分析ニュース,https://www.nikkei.com/rss/veritas.rss,4,Economía,92,Japón,ja,False,30 +7258,日経新聞 夕刊,日経新聞夕刊の経済ニュース,https://www.nikkei.com/rss/evening.rss,4,Economía,92,Japón,ja,False,30 +7257,日経新聞 朝刊,日経新聞朝刊の経済ニュース,https://www.nikkei.com/rss/morning.rss,4,Economía,92,Japón,ja,False,30 +7256,日経産業新聞,日経産業新聞の産業経済ニュース,https://www.nikkei.com/rss/san.rss,4,Economía,92,Japón,ja,False,30 +7255,日経金融新聞,日経金融新聞の金融経済ニュース,https://www.nikkei.com/rss/kin.rss,4,Economía,92,Japón,ja,False,30 +7259,日経電子版 速報,日経電子版の経済速報ニュース,https://www.nikkei.com/rss/news.rss,4,Economía,92,Japón,ja,False,30 +7187,朝日新聞 経済,朝日新聞の経済ニュース,https://www.asahi.com/rss/economy.rdf,4,Economía,92,Japón,ja,False,30 +7260,朝日新聞 経済総合,朝日新聞の経済総合ニュース,https://www.asahi.com/rss/economy_all.xml,4,Economía,92,Japón,ja,False,30 +7273,東京商工リサーチ,東京商工リサーチの経済ニュース,https://www.tsr.co.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7264,東洋経済 新着,東洋経済の新着経済記事,https://toyokeizai.net/rss/latest,4,Economía,92,Japón,ja,False,30 +7182,東洋経済オンライン,東洋経済オンラインの経済・ビジネスニュース,https://toyokeizai.net/rss,4,Economía,92,Japón,ja,False,30 +7189,毎日新聞 経済,毎日新聞の経済ニュース,https://mainichi.jp/rss/etc/keizai.rdf,4,Economía,92,Japón,ja,False,30 +7262,毎日新聞 経済総合,毎日新聞の経済総合ニュース,https://mainichi.jp/rss/etc/keizai_all.rss,4,Economía,92,Japón,ja,False,30 +7190,産経新聞 経済,産経新聞の経済ニュース,https://www.sankei.com/rss/economy.xml,4,Economía,92,Japón,ja,False,30 +7263,産経新聞 経済総合,産経新聞の経済総合ニュース,https://www.sankei.com/rss/economy_all.xml,4,Economía,92,Japón,ja,False,30 +7269,経済産業省 新着,経済産業省の新着情報,https://www.meti.go.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7188,読売新聞 経済,読売新聞の経済ニュース,https://www.yomiuri.co.jp/economy/feed/index.xml,4,Economía,92,Japón,ja,False,30 +7261,読売新聞 経済総合,読売新聞の経済総合ニュース,https://www.yomiuri.co.jp/economy/feed/economy.xml,4,Economía,92,Japón,ja,False,30 +7270,財務省 新着,財務省の新着情報,https://www.mof.go.jp/rss/news.xml,4,Economía,92,Japón,ja,False,30 +7267,週刊ダイヤモンド,週刊ダイヤモンドの経済ニュース,https://diamond.jp/rss/weekly.xml,4,Economía,92,Japón,ja,False,30 +7266,週刊東洋経済,週刊東洋経済の経済ニュース,https://toyokeizai.net/rss/weekly,4,Economía,92,Japón,ja,False,30 +7136,Kyodo News - Politics,Japan's leading news agency with political wire service.,https://english.kyodonews.net/rss/politics.xml,11,Política,92,Japón,en,False,30 +7134,NHK World Japan - News,Japan's public broadcaster with political and general news.,https://www3.nhk.or.jp/nhkworld/en/news/rss/,11,Política,92,Japón,en,False,30 +7142,NHKニュース(政治),日本放送協会の政治ニュースフィード,https://www.nhk.or.jp/rss/news/cat01.xml,11,Política,92,Japón,ja,False,30 +7141,National Diet of Japan,Official legislative news and parliamentary updates.,https://www.shugiin.go.jp/en/rss/rss.html,11,Política,92,Japón,en,False,30 +7133,Nikkei Asia - Politics,Political and diplomatic coverage from leading business news source.,https://asia.nikkei.com/feeds/rss/Politics,11,Política,92,Japón,en,False,30 +7140,Prime Minister's Office of Japan,Official announcements and policy statements.,https://japan.kantei.go.jp/rss/index_e.xml,11,Política,92,Japón,en,False,30 +7138,Reuters Japan,International news agency's Japan desk coverage.,https://www.reuters.com/world/japan/rss/,11,Política,92,Japón,en,False,30 +7150,TBS NEWS(政治),TBSテレビの政治関連報道,https://news.tbs.co.jp/rss/tbs_newsi_program_seiji.xml,11,Política,92,Japón,ja,False,30 +7137,The Asahi Shimbun - Asia & Japan,Left-leaning newspaper with political reporting.,https://www.asahi.com/ajw/rss/politics.xml,11,Política,92,Japón,en,False,30 +7139,The Diplomat - Japan,Analysis of Japan's foreign policy and regional diplomacy.,https://thediplomat.com/tag/japan/feed/,11,Política,92,Japón,en,True,0 +7132,The Japan Times - Politics,Political news and analysis from Japan's largest English newspaper.,https://www.japantimes.co.jp/news/politics/feed/,11,Política,92,Japón,en,False,30 +7135,The Mainichi - English,National newspaper covering political and social issues.,https://mainichi.jp/english/rss/,11,Política,92,Japón,en,False,30 +7153,テレビ朝日ANNニュース(政治),テレビ朝日の政治ニュース,https://news.tv-asahi.co.jp/rss/politics.xml,11,Política,92,Japón,ja,False,30 +7152,フジニュースネットワーク(政治),FNNの政治関連ニュース配信,https://www.fnn.jp/rss/politics.xml,11,Política,92,Japón,ja,False,30 +7161,公明党ニュース,与党・公明党の最新情報,https://www.komei.or.jp/news/rss/,11,Política,92,Japón,ja,True,0 +7149,共同通信(政治),共同通信社の政治ニュース配信,https://www.kyodo.co.jp/rss/news/politics.xml,11,Política,92,Japón,ja,False,30 +7157,参議院ニュース,参議院からのお知らせと情報,https://www.sangiin.go.jp/rss/news.rdf,11,Política,92,Japón,ja,False,30 +7155,外務省記者会見・発表,外交政策や会見・発表資料,https://www.mofa.go.jp/mofaj/press/danwa/index.xml,11,Política,92,Japón,ja,False,30 +7151,日本テレビニュース(政治),日テレの政治カテゴリニュース,https://www.ntv.co.jp/rss/news/politics.xml,11,Política,92,Japón,ja,False,30 +7147,日本経済新聞(政治),日経新聞の政治・行政ニュース,https://www.nikkei.com/rss/flash/politics.xml,11,Política,92,Japón,ja,False,30 +7160,日本維新の会お知らせ,政党・日本維新の会からの情報,https://o-ishin.jp/news/rss.xml,11,Política,92,Japón,ja,False,30 +7148,時事ドットコム(政治),時事通信社の政治関連速報,https://www.jiji.com/rss/politics.rss,11,Política,92,Japón,ja,False,30 +7143,朝日新聞デジタル(政治),朝日新聞の政治カテゴリRSS,https://www.asahi.com/rss/politics.xml,11,Política,92,Japón,ja,False,30 +7145,毎日新聞(政治),毎日新聞の政治ニュース配信,https://mainichi.jp/rss/etc/mainichi-flash-politics.rss,11,Política,92,Japón,ja,False,30 +7146,産経ニュース(政治),産経新聞の政治記事フィード,https://www.sankei.com/rss/news/politics.xml,11,Política,92,Japón,ja,False,30 +7159,立憲民主党ニュース,野党第一党の声明と活動報告,https://cdp-japan.jp/news/rss.xml,11,Política,92,Japón,ja,False,30 +7158,自由民主党ニュース,与党・自民党の最新ニュース,https://www.jimin.jp/news/rss/index.xml,11,Política,92,Japón,ja,False,30 +7156,衆議院トピックス,国会・衆議院の最新情報,https://www.shugiin.go.jp/rss/newsrss.rdf,11,Política,92,Japón,ja,False,30 +7144,読売新聞オンライン(政治),読売新聞の政治関連ニュース,https://www.yomiuri.co.jp/rss/news/politics.xml,11,Política,92,Japón,ja,False,30 +7154,首相官邸ホームページ,内閣総理大臣の動きや政府発表,https://www.kantei.go.jp/rss/index.xml,11,Política,92,Japón,ja,False,30 +7211,ASCII.jp,アスキーのテクノロジーニュース,https://ascii.jp/rss.xml,14,Tecnología,92,Japón,ja,True,0 +7214,CNET Japan,CNET Japanの最新記事,https://feeds.japan.cnet.com/rss/cnet/all.rdf,14,Tecnología,92,Japón,ja,True,0 +7223,Car Watch,自動車技術・カーエレクトロニクス,https://car.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7226,CodeZine,開発者向け情報サイト,https://codezine.jp/rss/new/index.xml,14,Tecnología,92,Japón,ja,True,0 +7213,Engadget Japanese,エンガジェット日本版,https://japanese.engadget.com/rss.xml,14,Tecnología,92,Japón,ja,False,30 +7212,Gigazine,ガジェット・テクノロジーニュース,https://gigazine.net/news/rss_2.0/,14,Tecnología,92,Japón,ja,True,0 +7209,ITmedia,ITメディアの最新ニュース,https://rss.itmedia.co.jp/rss/2.0/itmedia_all.xml,14,Tecnología,92,Japón,ja,True,0 +7210,Impress Watch,インプレスの技術・製品ニュース,https://www.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7220,PC Watch,パソコン・周辺機器ニュース,https://pc.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7218,Phile-web,音響機器・AV機器ニュース,https://www.phileweb.com/news/rss/index.rdf,14,Tecnología,92,Japón,ja,False,30 +7227,Publickey,テクノロジー・ビジネス情報,https://www.publickey1.jp/atom.xml,14,Tecnología,92,Japón,ja,True,0 +7216,TechCrunch Japan,テッククランチ日本版,https://jp.techcrunch.com/feed/,14,Tecnología,92,Japón,ja,False,30 +7228,Techable,スタートアップ・新技術,https://techable.jp/feed/,14,Tecnología,92,Japón,ja,False,30 +7215,ZDNet Japan,ZDNet JapanのITニュース,https://japan.zdnet.com/rss/index.rdf,14,Tecnología,92,Japón,ja,False,30 +7230,エンタープライズIT,企業ITソリューション情報,https://enterprisezine.jp/feed/,14,Tecnología,92,Japón,ja,False,30 +7231,クラウドWatch,クラウドコンピューティング,https://cloud.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7221,ケータイWatch,携帯電話・スマートフォン,https://k-tai.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7232,セキュリティWatch,セキュリティ関連ニュース,https://security.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7219,デジカメWatch,デジタルカメラ・関連機器,https://dc.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7224,デジタルライフ,デジタルライフ関連ニュース,https://news.mynavi.jp/feed/digital.rdf,14,Tecnología,92,Japón,ja,False,30 +7229,メガソフトニュース,ソフトウェア・開発ツール,https://www.megasoft.co.jp/meganews/rss/,14,Tecnología,92,Japón,ja,False,30 +7222,家電Watch,家電製品・生活家電ニュース,https://kaden.watch.impress.co.jp/headline.rdf,14,Tecnología,92,Japón,ja,False,30 +7217,週刊アスキー,週刊アスキーの最新号,https://weekly.ascii.jp/rss.xml,14,Tecnología,92,Japón,ja,True,0 +7233,電波新聞デジタル,電子部品・半導体ニュース,https://denkashimbun.jp/feed/,14,Tecnología,92,Japón,ja,False,30 +7225,@IT,ITエンジニア向け情報,https://atmarkit.itmedia.co.jp/rss/ait/index.xml,14,Tecnología,92,Japón,ja,False,30 +7335,Al Arabiya Economy Jordan,Al Arabiya economic news on Jordan.,http://www.alarabiya.net/rss/jordan-economy,4,Economía,93,Jordania,ar,True,0 +7319,Al Dustour Economy,Economic coverage from Al Dustour.,http://www.addustour.com/rss/economy,4,Economía,93,Jordania,ar,False,30 +7318,Al Ghad Economy,Economic news from Al Ghad newspaper.,http://www.alghad.com/rss/economy,4,Economía,93,Jordania,ar,False,30 +7334,Al Jazeera Economy Jordan,Al Jazeera economic news on Jordan.,http://www.aljazeera.net/rss/jordan-economy,4,Economía,93,Jordania,ar,False,30 +7315,Al Rai Economy,Economic news from Al Rai newspaper.,http://www.alrai.com/rss/economy,4,Economía,93,Jordania,ar,False,30 +7324,Amman Stock Exchange,Stock market and financial news.,http://www.ase.com.jo/rss,4,Economía,93,Jordania,en,False,30 +7338,Arabian Business Jordan,Arabian Business Jordan coverage.,http://www.arabianbusiness.com/rss/jordan,4,Economía,93,Jordania,en,False,30 +7336,Asharq Al Awsat Economy Jordan,Asharq Al Awsat Jordan economy.,http://www.aawsat.com/rss/jordan-economy,4,Economía,93,Jordania,ar,False,30 +7331,Bloomberg Jordan,Bloomberg financial news on Jordan.,http://www.bloomberg.com/rss/jordan,4,Economía,93,Jordania,en,False,30 +7320,Central Bank of Jordan,Jordan Central Bank news and reports.,http://www.cbj.gov.jo/rss,4,Economía,93,Jordania,en,False,30 +7326,Department of Statistics Jordan,Jordan economic statistics.,http://www.dos.gov.jo/rss,4,Economía,93,Jordania,en,False,30 +7332,Financial Times Jordan,FT economic analysis on Jordan.,http://www.ft.com/rss/jordan-economy,4,Economía,93,Jordania,en,False,30 +7329,IMF Jordan,IMF economic reports on Jordan.,http://www.imf.org/rss/jordan,4,Economía,93,Jordania,en,False,30 +7323,Jordan Chamber of Commerce,Jordan Chamber of Commerce news.,http://www.jocc.org.jo/rss,4,Economía,93,Jordania,ar,False,30 +7325,Jordan Investment Commission,Investment news and opportunities.,http://www.jic.gov.jo/rss,4,Economía,93,Jordania,en,False,30 +7327,Jordan Strategy Forum,Economic research and analysis.,http://www.jsf.org/rss,4,Economía,93,Jordania,en,False,30 +7316,Jordan Times Economy,Jordanian economic news in English.,http://www.jordantimes.com/rss/economy,4,Economía,93,Jordania,en,False,30 +7337,Middle East Monitor Economy,Middle East Monitor Jordan economy.,http://www.middleeastmonitor.com/rss/jordan-economy,4,Economía,93,Jordania,en,False,30 +7321,Ministry of Finance Jordan,Jordan Ministry of Finance updates.,http://www.mof.gov.jo/rss,4,Economía,93,Jordania,ar,False,30 +7322,Ministry of Industry and Trade,Jordan trade and industry news.,http://www.mit.gov.jo/rss,4,Economía,93,Jordania,ar,False,30 +7339,Oxford Business Group Jordan,Oxford Business Group Jordan reports.,http://www.oxfordbusinessgroup.com/rss/jordan,4,Economía,93,Jordania,en,False,30 +7317,Petra News Agency Economy,Jordanian official economic news.,http://www.petra.gov.jo/rss/economy,4,Economía,93,Jordania,ar,False,30 +7330,Reuters Jordan Economy,Reuters economic news on Jordan.,http://www.reuters.com/rss/jordan-economy,4,Economía,93,Jordania,en,False,30 +7333,The Economist Jordan,The Economist on Jordan economy.,http://www.economist.com/rss/jordan,4,Economía,93,Jordania,en,False,30 +7328,World Bank Jordan,World Bank reports on Jordan.,http://www.worldbank.org/rss/jordan,4,Economía,93,Jordania,en,False,30 +7290,7iber,Jordanian media platform politics.,http://www.7iber.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7295,AP News Jordan,Associated Press Jordan coverage.,http://apnews.com/rss/jordan,11,Política,93,Jordania,en,False,30 +7292,Al Arabiya Jordan,Al Arabiya coverage of Jordan.,http://www.alarabiya.net/rss/jordan,11,Política,93,Jordania,ar,True,0 +7281,Al Dustour,Jordanian newspaper political coverage.,http://www.addustour.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7280,Al Ghad,Jordanian independent newspaper politics.,http://www.alghad.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7299,Al Hayat Jordan,Al Hayat coverage of Jordan.,http://www.alhayat.com/rss/jordan,11,Política,93,Jordania,ar,False,30 +7291,Al Jazeera Jordan,Al Jazeera coverage of Jordan.,http://www.aljazeera.net/rss/jordan,11,Política,93,Jordania,ar,False,30 +7277,Al Rai,Jordanian daily newspaper political news.,http://www.alrai.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7287,Ammon News,Jordanian news network political.,http://www.ammonnews.net/rss/politics,11,Política,93,Jordania,ar,False,30 +7298,Asharq Al Awsat Jordan,Asharq Al Awsat Jordan coverage.,http://www.aawsat.com/rss/jordan,11,Política,93,Jordania,ar,False,30 +7293,BBC Arabic Jordan,BBC Arabic coverage of Jordan.,http://www.bbc.com/arabic/rss/jordan,11,Política,93,Jordania,ar,False,30 +7301,DW Arabic Jordan,DW Arabic coverage of Jordan.,http://www.dw.com/ar/rss/jordan,11,Política,93,Jordania,ar,False,30 +7300,France 24 Arabic Jordan,France 24 Arabic Jordan coverage.,http://www.france24.com/ar/rss/jordan,11,Política,93,Jordania,ar,False,30 +7282,Jordan News Agency,PETRA political news feed.,http://www.petra.gov.jo/rss/political,11,Política,93,Jordania,en,False,30 +7284,Jordan TV,Jordan Television political coverage.,http://www.jrtv.gov.jo/rss/politics,11,Política,93,Jordania,ar,False,30 +7278,Jordan Times,Jordanian English newspaper politics.,http://www.jordantimes.com/rss/politics,11,Política,93,Jordania,en,False,30 +7288,Khaberni,Jordanian news website political.,http://www.khaberni.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7296,Middle East Eye Jordan,Middle East Eye Jordan coverage.,http://www.middleeasteye.net/rss/jordan,11,Política,93,Jordania,en,False,30 +7279,Petra News Agency,Jordanian official news agency.,http://www.petra.gov.jo/rss/politics,11,Política,93,Jordania,ar,False,30 +7285,Radio Jordan,Jordan Radio political news.,http://www.radiojordan.gov.jo/rss/politics,11,Política,93,Jordania,ar,False,30 +7294,Reuters Jordan,Reuters coverage of Jordan politics.,http://www.reuters.com/rss/jordan,11,Política,93,Jordania,en,False,30 +7283,Roya News,Jordanian TV network political news.,http://www.roya.tv/rss/politics,11,Política,93,Jordania,ar,False,30 +7289,Sawaleif,Jordanian news and analysis.,http://www.sawaleif.com/rss/politics,11,Política,93,Jordania,ar,False,30 +7286,The Jordanian,Independent Jordanian news portal.,http://www.jordanian.net/rss/politics,11,Política,93,Jordania,en,False,30 +7297,The National Jordan,The National UAE coverage of Jordan.,http://www.thenationalnews.com/rss/jordan,11,Política,93,Jordania,en,False,30 +6346,crisisgroup.org Jordan,,"https://www.crisisgroup.org/rss-0#:~:text=Israel/Palestine-,Jordan,-Lebanon",11,Política,93,Jordania,en,False,30 +7383,ADB Kazakhstan,Banco Asiatico de Desarrollo Kazajistan.,https://www.adb.org/rss/kazakhstan,4,Economía,94,Kazajistán,en,False,30 +7374,Atameken National Chamber,Camara Nacional de Emprendedores.,https://atameken.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7379,Bloomberg Kazakhstan,Bloomberg noticias sobre Kazajistan.,https://www.bloomberg.com/rss/kazakhstan,4,Economía,94,Kazajistán,en,False,30 +7366,Caravan Economy,Periodico en ruso seccion economia.,https://caravan.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7371,Committee on Statistics,Agencia de estadisticas de Kazajistan.,https://stat.gov.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7380,Financial Times Kazakhstan,FT analisis economico.,https://www.ft.com/rss/kazakhstan-economy,4,Economía,94,Kazajistán,en,False,30 +7376,Forbes Kazakhstan Economy,Forbes Kazajistan seccion economia.,https://forbes.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7382,IMF Kazakhstan,FMI informes sobre Kazajistan.,https://www.imf.org/rss/kazakhstan,4,Economía,94,Kazajistán,en,False,30 +7363,Informburo Economy,Noticias economicas en ruso y kazajo.,https://informburo.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7372,Kazakhstan Stock Exchange,Bolsa de valores de Kazajistan.,https://kase.kz/rss,4,Economía,94,Kazajistán,en,False,30 +7365,Kazakhstan Today Economy,Agencia de noticias economicas.,https://kt.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7361,Kazinform Economy,Agencia oficial de noticias economicas.,https://www.inform.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7364,Khabar 24 Economy,Canal estatal noticias economicas.,https://24.kz/rss/economy,4,Economía,94,Kazajistán,kk,False,30 +7375,Kursiv Economy,Noticias de negocios y economia.,https://kursiv.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7367,Liter Economy,Periodico en ruso noticias economicas.,https://liter.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7370,Ministry of Finance,Ministerio de Finanzas de Kazajistan.,https://minfin.gov.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7369,Ministry of National Economy,Ministerio de Economia Nacional.,https://economy.gov.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7368,National Bank of Kazakhstan,Banco Central de Kazajistan.,https://nationalbank.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7378,Reuters Kazakhstan Economy,Reuters noticias economicas.,https://www.reuters.com/rss/kazakhstan-economy,4,Economía,94,Kazajistán,en,False,30 +7373,Samruk Kazyna,Fondo de riqueza soberana de Kazajistan.,https://sk.kz/rss,4,Economía,94,Kazajistán,ru,False,30 +7362,Tengrinews Economy,Noticias economicas populares.,https://tengrinews.kz/rss/economy,4,Economía,94,Kazajistán,ru,False,30 +7377,The Astana Times Economy,Periodico en ingles seccion economia.,https://astanatimes.com/rss/economy,4,Economía,94,Kazajistán,en,False,30 +7381,World Bank Kazakhstan,Banco Mundial informes sobre Kazajistan.,https://www.worldbank.org/rss/kazakhstan,4,Economía,94,Kazajistán,en,False,30 +7354,Al Jazeera Kazakhstan,Cobertura de Al Jazeera sobre Kazajistan.,https://www.aljazeera.com/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7356,BBC News Kazakhstan,BBC noticias sobre Kazajistan.,https://www.bbc.com/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7360,CACI Analyst,Analisis del Centro Asia-Caucaso.,https://cacianalyst.org/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7346,Caravan,Periodico en ruso sobre politica.,https://caravan.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7349,Central Asia Monitor,Analisis politico de Asia Central.,https://camonitor.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7358,Eurasianet Kazakhstan,Noticias sobre Asia Central.,https://eurasianet.org/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7353,Forbes Kazakhstan,Edicion kazaja de Forbes.,https://forbes.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7342,Informburo,Medio de noticias en ruso y kazajo.,https://informburo.kz/rss,11,Política,94,Kazajistán,ru,False,30 +7351,Kazakh Telegraph Agency,Noticias oficiales en ingles.,https://kaztag.kz/rss/politics,11,Política,94,Kazajistán,en,False,30 +7345,Kazakhstan Today,Agencia de noticias en ruso.,https://kt.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7340,Kazinform,Agencia de noticias oficial de Kazajistan.,https://www.inform.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7343,Khabar 24,Canal de television estatal de noticias.,https://24.kz/rss/politics,11,Política,94,Kazajistán,kk,False,30 +7352,Kursiv,Noticias de negocios y politica.,https://kursiv.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7347,Liter,Periodico en ruso con noticias politicas.,https://liter.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7357,RFE/RL Kazakhstan,Radio Free Europe en ingles.,https://www.rferl.org/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7344,Radio Azattyk,Radio Free Europe en kazajo.,https://www.azattyq.org/rss,11,Política,94,Kazajistán,kk,False,30 +7355,Reuters Kazakhstan,Noticias de Reuters sobre Kazajistan.,https://www.reuters.com/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7341,Tengrinews,Noticias populares de Kazajistan en ruso.,https://tengrinews.kz/rss/,11,Política,94,Kazajistán,ru,False,30 +7350,The Astana Times,Periodico en ingles de Kazajistan.,https://astanatimes.com/rss/politics,11,Política,94,Kazajistán,en,False,30 +7359,The Diplomat Kazakhstan,Analisis politico de Asia Central.,https://thediplomat.com/rss/kazakhstan,11,Política,94,Kazajistán,en,False,30 +7348,Vlast,Analisis politico y noticias.,https://vlast.kz/rss/politics,11,Política,94,Kazajistán,ru,False,30 +7454,African Academy of Sciences,African science news and research.,https://www.aasciences.africa/rss,1,Ciencia,95,Kenia,en,False,30 +7442,Daily Nation Science,Daily Nation science and technology.,https://www.nation.co.ke/rss/science,1,Ciencia,95,Kenia,en,False,30 +7461,FAO Kenya,FAO agricultural science news.,https://www.fao.org/rss/kenya,1,Ciencia,95,Kenia,en,False,30 +7453,International Centre of Insect Physiology,ICIPE insect science research.,https://www.icipe.org/rss,1,Ciencia,95,Kenia,en,False,30 +7451,International Livestock Research Institute,ILRI agricultural research.,https://www.ilri.org/rss,1,Ciencia,95,Kenia,en,True,0 +7446,Jomo Kenyatta University,JKUAT science and technology.,https://www.jkuat.ac.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7465,Kenya Medical Association,Medical science and research.,https://www.kma.co.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7450,Kenya Medical Research Institute,KEMRI medical research news.,https://www.kemri.org/rss,1,Ciencia,95,Kenia,en,False,30 +7463,Kenya Wildlife Service,Wildlife and conservation science.,https://www.kws.go.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7445,Kenyatta University,Kenyatta University research.,https://www.ku.ac.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7447,Moi University,Moi University research news.,https://www.mu.ac.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7449,National Commission for Science,National Commission for Science and Technology.,https://www.nacosti.go.ke/rss,1,Ciencia,95,Kenia,en,True,0 +7464,National Museums of Kenya,Archaeology and natural history.,https://www.museums.or.ke/rss,1,Ciencia,95,Kenia,en,True,0 +7457,Nature Africa,Science news from Nature Africa.,https://www.nature.com/africa/rss,1,Ciencia,95,Kenia,en,False,30 +7458,SciDev.Net Africa,Science and development news.,https://www.scidev.net/africa/rss,1,Ciencia,95,Kenia,en,False,30 +7455,Science Africa,African science news portal.,https://www.scienceafrica.co.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7448,Strathmore University,Strathmore University research.,https://www.strathmore.edu/rss,1,Ciencia,95,Kenia,en,False,30 +7456,The Conversation Africa,Academic research news in Africa.,https://theconversation.com/africa/rss,1,Ciencia,95,Kenia,en,False,30 +7443,The Standard Science,The Standard science news.,https://www.standardmedia.co.ke/rss/science,1,Ciencia,95,Kenia,en,False,30 +7441,The Star Kenya Science,Kenyan science and research news.,https://www.the-star.co.ke/rss/science,1,Ciencia,95,Kenia,en,False,30 +7462,UNDP Kenya Science,UNDP science and technology projects.,https://www.undp.org/rss/kenya/science,1,Ciencia,95,Kenia,en,False,30 +7459,UNESCO Kenya,UNESCO science programs in Kenya.,https://www.unesco.org/rss/kenya,1,Ciencia,95,Kenia,en,False,30 +7444,University of Nairobi,University of Nairobi research news.,https://www.uonbi.ac.ke/rss,1,Ciencia,95,Kenia,en,False,30 +7452,World Agroforestry Centre,ICRAF agroforestry research.,https://www.worldagroforestry.org/rss,1,Ciencia,95,Kenia,en,False,30 +7460,World Health Organization Kenya,WHO health science news.,https://www.who.int/rss/kenya,1,Ciencia,95,Kenia,en,False,30 +7427,Africa Development Bank Kenya,AfDB projects in Kenya.,https://www.afdb.org/rss/kenya,4,Economía,95,Kenia,en,False,30 +7422,Bloomberg Kenya,Bloomberg news on Kenya economy.,https://www.bloomberg.com/rss/kenya,4,Economía,95,Kenia,en,False,30 +7411,Business Daily Africa,Kenyan business and financial news.,https://www.businessdailyafrica.com/rss,4,Economía,95,Kenia,en,False,30 +7410,Capital FM Business,Capital FM business news.,https://www.capitalfm.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7414,Central Bank of Kenya,Kenyan central bank news and reports.,https://www.centralbank.go.ke/rss,4,Economía,95,Kenia,en,True,0 +7412,Citizen TV Business,Citizen TV business news.,https://citizentv.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7408,Daily Nation Business,Daily Nation business and economy news.,https://www.nation.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7430,Export Promotion Council,Export and trade news.,https://epc.go.ke/rss,4,Economía,95,Kenia,en,False,30 +7423,Financial Times Kenya,FT economic analysis on Kenya.,https://www.ft.com/rss/kenya-economy,4,Economía,95,Kenia,en,False,30 +7426,IMF Kenya,IMF economic reports on Kenya.,https://www.imf.org/rss/kenya,4,Economía,95,Kenia,en,False,30 +7413,KBC Business,Kenya Broadcasting Corporation business.,https://www.kbc.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7418,Kenya Association of Manufacturers,Manufacturing industry news.,https://kam.co.ke/rss,4,Economía,95,Kenia,en,False,30 +7420,Kenya Bankers Association,Banking industry news.,https://kba.co.ke/rss,4,Economía,95,Kenia,en,True,0 +7429,Kenya Investment Authority,Investment opportunities in Kenya.,https://invest.go.ke/rss,4,Economía,95,Kenia,en,False,30 +7416,Kenya National Bureau of Statistics,Kenyan economic statistics.,https://www.knbs.or.ke/rss,4,Economía,95,Kenia,en,False,30 +7419,Kenya Private Sector Alliance,Private sector association news.,https://kepsa.or.ke/rss,4,Economía,95,Kenia,en,False,30 +7431,Kenya Revenue Authority,Tax and revenue news.,https://www.kra.go.ke/rss,4,Economía,95,Kenia,en,False,30 +7417,Nairobi Securities Exchange,Nairobi stock exchange news.,https://www.nse.co.ke/rss,4,Economía,95,Kenia,en,True,0 +7415,National Treasury Kenya,National Treasury of Kenya news.,https://www.treasury.go.ke/rss,4,Economía,95,Kenia,en,True,0 +7421,Reuters Kenya Business,Reuters business news on Kenya.,https://www.reuters.com/rss/kenya-business,4,Economía,95,Kenia,en,False,30 +7424,The Economist Kenya,The Economist on Kenya economy.,https://www.economist.com/rss/kenya,4,Economía,95,Kenia,en,False,30 +7409,The Standard Business,The Standard business and economy.,https://www.standardmedia.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7407,The Star Kenya Economy,Kenyan economic news and analysis.,https://www.the-star.co.ke/rss/business,4,Economía,95,Kenia,en,False,30 +7428,UNDP Kenya,UNDP economic development news.,https://www.undp.org/rss/kenya,4,Economía,95,Kenia,en,False,30 +7425,World Bank Kenya,World Bank reports on Kenya.,https://www.worldbank.org/rss/kenya,4,Economía,95,Kenia,en,False,30 +7555,24.kg Science,Noticias de ciencia independientes.,https://24.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7570,AKIpress Science,Agencia de noticias ciencia.,https://akipress.com/rss/science,1,Ciencia,96,Kirguistán,ru,True,0 +7567,American University of Central Asia,Universidad Americana de Asia Central.,https://www.auca.kg/rss,1,Ciencia,96,Kirguistán,en,False,30 +7559,Azattyk Science,Radio Free Science en kirguis.,https://www.azattyk.org/rss/science,1,Ciencia,96,Kirguistán,ky,False,30 +7562,Elgezit.kg Science,Periodico en kirguis ciencia.,https://elgezit.kg/rss/science,1,Ciencia,96,Kirguistán,ky,False,30 +7576,FAO Kyrgyzstan,FAO ciencia agricola.,https://www.fao.org/rss/kyrgyzstan,1,Ciencia,96,Kirguistán,en,False,30 +7578,International Science Foundation,Fundacion Internacional de Ciencia.,https://www.isf.org/rss/kyrgyzstan,1,Ciencia,96,Kirguistán,en,False,30 +7554,Kabar Science,Agencia estatal noticias de ciencia.,https://kabar.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7571,Kaktus Media Science,Medio independiente ciencia.,https://kaktus.media/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7558,Kloop Science,Medio independiente noticias de ciencia.,https://kloop.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7561,Kyrgyz National News Agency Science,Agencia nacional noticias de ciencia.,https://kyrgyztoday.kg/rss/science,1,Ciencia,96,Kirguistán,ky,False,30 +7566,Kyrgyz Russian Slavic University,Universidad Eslava Kirguis-Rusa.,https://www.krsu.edu.kg/rss,1,Ciencia,96,Kirguistán,ru,False,30 +7565,Kyrgyz State University,Universidad Estatal de Kirguistan.,https://www.knu.kg/rss,1,Ciencia,96,Kirguistán,ru,True,0 +7557,MSN.kg Science,Portal de noticias ciencia.,https://www.msn.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7569,Ministry of Education and Science,Ministerio de Educacion y Ciencia.,https://www.edu.gov.kg/rss,1,Ciencia,96,Kirguistán,ru,False,30 +7564,National Academy of Sciences,Academia Nacional de Ciencias de Kirguistan.,https://www.nas.kg/rss,1,Ciencia,96,Kirguistán,ru,True,0 +7560,Radio Azattyk Russian Science,Radio Free Science en ruso.,https://rus.azattyk.org/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7573,Reuters Kyrgyzstan Science,Reuters noticias de ciencia.,https://www.reuters.com/rss/kyrgyzstan-science,1,Ciencia,96,Kirguistán,en,False,30 +7572,Sputnik Kyrgyzstan Science,Sputnik noticias de ciencia.,https://kg.sputnik.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7563,Super.kg Science,Portal de noticias ciencia kirguis.,https://super.kg/rss/science,1,Ciencia,96,Kirguistán,ky,False,30 +7577,UNDP Kyrgyzstan Science,PNUD ciencia y tecnologia.,https://www.undp.org/rss/kyrgyzstan/science,1,Ciencia,96,Kirguistán,en,False,30 +7574,UNESCO Kyrgyzstan,UNESCO programas de ciencia.,https://www.unesco.org/rss/kyrgyzstan,1,Ciencia,96,Kirguistán,en,False,30 +7568,University of Central Asia,Universidad de Asia Central.,https://www.ucentralasia.org/rss,1,Ciencia,96,Kirguistán,en,False,30 +7556,Vecherniy Bishkek Science,Periodico en ruso seccion ciencia.,https://www.vb.kg/rss/science,1,Ciencia,96,Kirguistán,ru,False,30 +7575,World Health Organization Kyrgyzstan,OMS noticias de ciencia medica.,https://www.who.int/rss/kyrgyzstan,1,Ciencia,96,Kirguistán,en,False,30 +7516,24.kg Economy,Noticias economicas independientes.,https://24.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7537,ADB Kyrgyzstan,Banco Asiatico de Desarrollo.,https://www.adb.org/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7530,AKIpress Economy,Agencia de noticias economia.,https://akipress.com/rss/economy,4,Economía,96,Kirguistán,ru,True,0 +7520,Azattyk Economy,Radio Free Economy en kirguis.,https://www.azattyk.org/rss/economy,4,Economía,96,Kirguistán,ky,False,30 +7534,Bloomberg Kyrgyzstan,Bloomberg noticias sobre Kirguistan.,https://www.bloomberg.com/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7538,EBRD Kyrgyzstan,Banco Europeo de Reconstruccion.,https://www.ebrd.com/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7523,Elgezit.kg Economy,Periodico en kirguis economia.,https://elgezit.kg/rss/economy,4,Economía,96,Kirguistán,ky,False,30 +7536,IMF Kyrgyzstan,FMI informes sobre Kirguistan.,https://www.imf.org/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7515,Kabar Economy,Agencia estatal noticias economicas.,https://kabar.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7531,Kaktus Media Economy,Medio independiente economia.,https://kaktus.media/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7519,Kloop Economy,Medio independiente noticias economicas.,https://kloop.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7522,Kyrgyz National News Agency Economy,Agencia nacional noticias economicas.,https://kyrgyztoday.kg/rss/economy,4,Economía,96,Kirguistán,ky,False,30 +7529,Kyrgyz Stock Exchange,Bolsa de valores de Kirguistan.,https://www.kse.kg/rss,4,Economía,96,Kirguistán,ru,False,30 +7518,MSN.kg Economy,Portal de noticias economia.,https://www.msn.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7526,Ministry of Economy and Commerce,Ministerio de Economia y Comercio.,https://www.mek.kg/rss,4,Economía,96,Kirguistán,ru,False,30 +7527,Ministry of Finance,Ministerio de Finanzas de Kirguistan.,https://www.minfin.kg/rss,4,Economía,96,Kirguistán,ru,False,30 +7525,National Bank of Kyrgyzstan,Banco Central de Kirguistan.,https://www.nbkr.kg/rss,4,Economía,96,Kirguistán,ru,False,30 +7521,Radio Azattyk Russian Economy,Radio Free Economy en ruso.,https://rus.azattyk.org/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7533,Reuters Kyrgyzstan Economy,Reuters noticias economicas.,https://www.reuters.com/rss/kyrgyzstan-economy,4,Economía,96,Kirguistán,en,False,30 +7532,Sputnik Kyrgyzstan Economy,Sputnik noticias economicas.,https://kg.sputnik.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7528,State Committee for Industry,Comite Estatal de Industria.,https://www.industry.kg/rss,4,Economía,96,Kirguistán,ru,False,30 +7524,Super.kg Economy,Portal de noticias economia kirguis.,https://super.kg/rss/economy,4,Economía,96,Kirguistán,ky,False,30 +7539,UNDP Kyrgyzstan,PNUD desarrollo economico.,https://www.undp.org/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7517,Vecherniy Bishkek Economy,Periodico en ruso seccion economia.,https://www.vb.kg/rss/economy,4,Economía,96,Kirguistán,ru,False,30 +7535,World Bank Kyrgyzstan,Banco Mundial informes.,https://www.worldbank.org/rss/kyrgyzstan,4,Economía,96,Kirguistán,en,False,30 +7477,24.kg,Medio de noticias independiente en ruso.,https://24.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7487,AKIpress,Agencia de noticias de Asia Central.,https://akipress.com/rss/politics,11,Política,96,Kirguistán,ru,True,0 +7497,Al Jazeera Kyrgyzstan,Al Jazeera cobertura de Kirguistan.,https://www.aljazeera.com/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7481,Azattyk,Radio Free Europe en kirguis.,https://www.azattyk.org/rss,11,Política,96,Kirguistán,ky,False,30 +7496,BBC News Kyrgyzstan,BBC noticias sobre Kirguistan.,https://www.bbc.com/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7491,Central Asian Analytical Network,Analisis politico de Asia Central.,https://caa-network.org/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7484,Elgezit.kg,Periodico en kirguis.,https://elgezit.kg/rss/politics,11,Política,96,Kirguistán,ky,False,30 +7493,Eurasianet Kyrgyzstan,Noticias sobre Asia Central.,https://eurasianet.org/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7500,Government of Kyrgyzstan,Gobierno de Kirguistan.,https://www.gov.kg/rss,11,Política,96,Kirguistán,ru,False,30 +7476,Kabar News Agency,Agencia estatal de noticias de Kirguistan.,https://kabar.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7488,Kaktus Media,Medio independiente en ruso.,https://kaktus.media/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7480,Kloop,Medio independiente en kirguis y ruso.,https://kloop.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7483,Kyrgyz National News Agency,Agencia nacional de noticias.,https://kyrgyztoday.kg/rss/politics,11,Política,96,Kirguistán,ky,False,30 +7486,Kyrgyzpress,Medio de noticias en kirguis.,https://kyrgyzpress.kg/rss/politics,11,Política,96,Kirguistán,ky,False,30 +7479,MSN.kg,Portal de noticias en ruso.,https://www.msn.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7499,Parliament of Kyrgyzstan,Parlamento de Kirguistan.,https://www.kenesh.kg/rss,11,Política,96,Kirguistán,ru,False,30 +7498,President of Kyrgyzstan,Oficina del Presidente de Kirguistan.,https://www.president.kg/rss,11,Política,96,Kirguistán,ru,True,0 +7495,RFE/RL Kyrgyzstan,Radio Free Europe en ingles.,https://www.rferl.org/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7482,Radio Azattyk Russian,Radio Free Europe en ruso.,https://rus.azattyk.org/rss,11,Política,96,Kirguistán,ru,False,30 +7490,Regnum Kyrgyzstan,Agencia de noticias en ruso.,https://regnum.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7494,Reuters Kyrgyzstan,Reuters cobertura de Kirguistan.,https://www.reuters.com/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7489,Sputnik Kyrgyzstan,Sputnik noticias en ruso.,https://kg.sputnik.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7485,Super.kg,Portal de noticias en kirguis.,https://super.kg/rss/politics,11,Política,96,Kirguistán,ky,False,30 +7492,The Diplomat Kyrgyzstan,Analisis politico en ingles.,https://thediplomat.com/rss/kyrgyzstan,11,Política,96,Kirguistán,en,False,30 +7478,Vecherniy Bishkek,Periodico en ruso de Kirguistan.,https://www.vb.kg/rss/politics,11,Política,96,Kirguistán,ru,False,30 +7614,24.kg Society,Noticias de sociedad independientes.,https://24.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7623,AKIpress Society,Agencia de noticias sociedad.,https://akipress.com/rss/society,13,Sociedad,96,Kirguistán,ru,True,0 +7628,Al Jazeera Kyrgyzstan Society,Al Jazeera noticias de sociedad.,https://www.aljazeera.com/rss/kyrgyzstan-society,13,Sociedad,96,Kirguistán,en,False,30 +7618,Azattyk Society,Radio Free Society en kirguis.,https://www.azattyk.org/rss/society,13,Sociedad,96,Kirguistán,ky,False,30 +7627,BBC News Kyrgyzstan Society,BBC noticias de sociedad.,https://www.bbc.com/rss/kyrgyzstan/society,13,Sociedad,96,Kirguistán,en,False,30 +7621,Elgezit.kg Society,Periodico en kirguis sociedad.,https://elgezit.kg/rss/society,13,Sociedad,96,Kirguistán,ky,False,30 +7613,Kabar Society,Agencia estatal noticias de sociedad.,https://kabar.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7624,Kaktus Media Society,Medio independiente sociedad.,https://kaktus.media/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7617,Kloop Society,Medio independiente noticias de sociedad.,https://kloop.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7620,Kyrgyz National News Agency Society,Agencia nacional noticias de sociedad.,https://kyrgyztoday.kg/rss/society,13,Sociedad,96,Kirguistán,ky,False,30 +7616,MSN.kg Society,Portal de noticias sociedad.,https://www.msn.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7631,Ministry of Education,Ministerio de Educacion de Kirguistan.,https://www.edu.gov.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7630,Ministry of Health,Ministerio de Salud de Kirguistan.,https://www.med.kg/rss,13,Sociedad,96,Kirguistán,ru,False,30 +7629,Ministry of Social Development,Ministerio de Desarrollo Social de Kirguistan.,https://www.social.gov.kg/rss,13,Sociedad,96,Kirguistán,ru,False,30 +7632,National Statistical Committee,Comite Nacional de Estadisticas.,https://www.stat.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7636,OSCE Kyrgyzstan,OSCE noticias sobre sociedad y seguridad.,https://www.osce.org/rss/kyrgyzstan,13,Sociedad,96,Kirguistán,en,False,30 +7619,Radio Azattyk Russian Society,Radio Free Society en ruso.,https://rus.azattyk.org/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7637,Red Cross Kyrgyzstan,Cruz Roja noticias de sociedad.,https://www.redcross.kg/rss,13,Sociedad,96,Kirguistán,ru,False,30 +7626,Reuters Kyrgyzstan Society,Reuters noticias de sociedad.,https://www.reuters.com/rss/kyrgyzstan-society,13,Sociedad,96,Kirguistán,en,False,30 +7625,Sputnik Kyrgyzstan Society,Sputnik noticias de sociedad.,https://kg.sputnik.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7622,Super.kg Society,Portal de noticias sociedad kirguis.,https://super.kg/rss/society,13,Sociedad,96,Kirguistán,ky,False,30 +7634,UNDP Kyrgyzstan Society,PNUD desarrollo social.,https://www.undp.org/rss/kyrgyzstan/society,13,Sociedad,96,Kirguistán,en,False,30 +7633,UNICEF Kyrgyzstan,UNICEF noticias sobre infancia.,https://www.unicef.org/rss/kyrgyzstan,13,Sociedad,96,Kirguistán,en,False,30 +7615,Vecherniy Bishkek Society,Periodico en ruso seccion sociedad.,https://www.vb.kg/rss/society,13,Sociedad,96,Kirguistán,ru,False,30 +7635,World Bank Kyrgyzstan Society,Banco Mundial desarrollo social.,https://www.worldbank.org/rss/kyrgyzstan/society,13,Sociedad,96,Kirguistán,en,False,30 +7580,24.kg Technology,Noticias de tecnologia independientes.,https://24.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7595,AKIpress Technology,Agencia de noticias tecnologia.,https://akipress.com/rss/technology,14,Tecnología,96,Kirguistán,ru,True,0 +7584,Azattyk Technology,Radio Free Technology en kirguis.,https://www.azattyk.org/rss/technology,14,Tecnología,96,Kirguistán,ky,False,30 +7593,Beeline Kyrgyzstan,Operador de telecomunicaciones.,https://www.beeline.kg/rss,14,Tecnología,96,Kirguistán,ru,False,30 +7602,CNET Kyrgyzstan,CNET noticias de tecnologia.,https://cnet.com/rss/kyrgyzstan,14,Tecnología,96,Kirguistán,en,False,30 +7587,Elgezit.kg Technology,Periodico en kirguis tecnologia.,https://elgezit.kg/rss/technology,14,Tecnología,96,Kirguistán,ky,False,30 +7579,Kabar Technology,Agencia estatal noticias de tecnologia.,https://kabar.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7596,Kaktus Media Technology,Medio independiente tecnologia.,https://kaktus.media/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7583,Kloop Technology,Medio independiente noticias de tecnologia.,https://kloop.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7586,Kyrgyz National News Agency Technology,Agencia nacional noticias de tecnologia.,https://kyrgyztoday.kg/rss/technology,14,Tecnología,96,Kirguistán,ky,False,30 +7582,MSN.kg Technology,Portal de noticias tecnologia.,https://www.msn.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7592,MegaCom,Operador de telecomunicaciones de Kirguistan.,https://www.megacom.kg/rss,14,Tecnología,96,Kirguistán,ru,False,30 +7589,Ministry of Digital Development,Ministerio de Desarrollo Digital de Kirguistan.,https://www.digital.gov.kg/rss,14,Tecnología,96,Kirguistán,ru,True,0 +7591,National Communications Agency,Agencia Nacional de Comunicaciones.,https://www.nca.kg/rss,14,Tecnología,96,Kirguistán,ru,False,30 +7594,O! Mobile,Operador movil de Kirguistan.,https://www.o.kg/rss,14,Tecnología,96,Kirguistán,ru,False,30 +7585,Radio Azattyk Russian Technology,Radio Free Technology en ruso.,https://rus.azattyk.org/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7598,Reuters Kyrgyzstan Technology,Reuters noticias de tecnologia.,https://www.reuters.com/rss/kyrgyzstan-technology,14,Tecnología,96,Kirguistán,en,False,30 +7597,Sputnik Kyrgyzstan Technology,Sputnik noticias de tecnologia.,https://kg.sputnik.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7590,State Committee for Information Technology,Comite Estatal de Tecnologias de la Informacion.,https://www.it.gov.kg/rss,14,Tecnología,96,Kirguistán,ru,False,30 +7588,Super.kg Technology,Portal de noticias tecnologia kirguis.,https://super.kg/rss/technology,14,Tecnología,96,Kirguistán,ky,False,30 +7599,TechCrunch Kyrgyzstan,TechCrunch noticias sobre Kirguistan.,https://techcrunch.com/rss/kyrgyzstan,14,Tecnología,96,Kirguistán,en,False,30 +7581,Vecherniy Bishkek Technology,Periodico en ruso seccion tecnologia.,https://www.vb.kg/rss/technology,14,Tecnología,96,Kirguistán,ru,False,30 +7600,VentureBeat Kyrgyzstan,VentureBeat noticias sobre Kirguistan.,https://venturebeat.com/rss/kyrgyzstan,14,Tecnología,96,Kirguistán,en,False,30 +7603,Wired Kyrgyzstan,Wired noticias sobre Kirguistan.,https://wired.com/rss/kyrgyzstan,14,Tecnología,96,Kirguistán,en,False,30 +7601,ZDNet Kyrgyzstan,ZDNet noticias de tecnologia.,https://zdnet.com/rss/kyrgyzstan,14,Tecnología,96,Kirguistán,en,False,30 +7727,ABC Pacific Kiribati Science,Australian Broadcasting Corporation science.,https://www.abc.net.au/pacific/rss/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7741,ANU Pacific Research,Australian research on Kiribati.,https://www.anu.edu.au/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7731,CSIRO Pacific Research,CSIRO research projects in Kiribati.,https://www.csiro.au/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7739,Climate Change Kiribati,Climate change research and science.,https://www.climate.gov.ki/rss,1,Ciencia,97,Kiribati,en,False,30 +7734,Coral Reef Alliance,Coral reef science and research.,https://www.coral.org/rss/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7737,FAO Kiribati Science,FAO agricultural and fisheries science.,https://www.fao.org/rss/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7721,Government of Kiribati Science,Official government science news.,http://www.gov.ki/rss/science,1,Ciencia,97,Kiribati,en,False,30 +7724,Kiribati Meteorological Service,Meteorology and climate science.,http://www.met.gov.ki/rss,1,Ciencia,97,Kiribati,en,False,30 +7722,Ministry of Environment Kiribati,Environment and climate science.,http://www.environment.gov.ki/rss,1,Ciencia,97,Kiribati,en,False,30 +7723,Ministry of Fisheries Science,Fisheries and marine science.,http://www.fisheries.gov.ki/rss/science,1,Ciencia,97,Kiribati,en,False,30 +7744,NOAA Pacific,NOAA ocean and atmospheric science.,https://www.noaa.gov/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7742,New Zealand Science Pacific,New Zealand science cooperation.,https://www.science.org.nz/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7740,Pacific Islands Forum Science,Regional science policy and research.,https://www.forumsec.org/rss/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7745,Pacific Marine Science,Marine science research in Pacific.,https://www.pacificmarine.org/rss/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7726,RNZ Pacific Kiribati Science,Radio New Zealand Pacific science coverage.,https://www.rnz.co.nz/rss/pacific/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7728,SPC Pacific Community Science,SPC scientific research in Kiribati.,https://www.spc.int/rss/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7729,SPREP Environment Science,SPREP environmental science for Kiribati.,https://www.sprep.org/rss/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7733,The Nature Conservancy Pacific,Marine conservation science.,https://www.nature.org/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7738,UNDP Kiribati Science,UNDP science and technology projects.,https://www.undp.org/rss/kiribati/science,1,Ciencia,97,Kiribati,en,False,30 +7735,UNESCO Pacific Science,UNESCO science programs in Pacific.,https://www.unesco.org/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7743,US Geological Survey Pacific,Geological and ocean science.,https://www.usgs.gov/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7730,University of Queensland Pacific,Collaborative research in Pacific.,https://www.uq.edu.au/rss/pacific/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7725,University of South Pacific Kiribati,USP Kiribati campus science programs.,https://www.usp.ac.fj/rss/kiribati-science,1,Ciencia,97,Kiribati,en,False,30 +7732,WWF Pacific Kiribati,WWF conservation science in Kiribati.,https://www.wwfpacific.org/rss/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7736,World Health Organization Kiribati,WHO health science news.,https://www.who.int/rss/kiribati,1,Ciencia,97,Kiribati,en,False,30 +7690,ABC Pacific Kiribati Economy,Australian Broadcasting Corporation economic news.,https://www.abc.net.au/pacific/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7698,ADB Kiribati,Asian Development Bank projects in Kiribati.,https://www.adb.org/rss/kiribati,4,Economía,97,Kiribati,en,False,30 +7694,BBC News Kiribati Economy,BBC economic coverage of Kiribati.,https://www.bbc.com/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7704,Climate Finance Kiribati,Climate change financing and economy.,https://www.climatefinance.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7700,FAO Kiribati,Food and Agriculture Organization news.,https://www.fao.org/rss/kiribati,4,Economía,97,Kiribati,en,False,30 +7703,Forum Fisheries Agency Economy,Fisheries economy and trade news.,https://www.ffa.int/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7683,Government of Kiribati Economy,Official government economic news.,http://www.gov.ki/rss/economy,4,Economía,97,Kiribati,en,False,30 +7697,IMF Kiribati Economy,IMF economic reports and analysis.,https://www.imf.org/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7706,Kiribati Chamber of Commerce,Chamber of Commerce business news.,https://www.chamber.ki/rss,4,Economía,97,Kiribati,en,False,30 +7705,Kiribati Customs Economy,Customs and international trade news.,https://www.customs.gov.ki/rss/economy,4,Economía,97,Kiribati,en,False,30 +7688,Kiribati Development Bank,Development Bank economic news.,http://www.kdb.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7685,Kiribati Ministry of Finance,Ministry of Finance news and updates.,http://www.finance.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7686,Kiribati Ministry of Fisheries,Ministry of Fisheries and marine resources economy.,http://www.fisheries.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7687,Kiribati Ministry of Tourism,Tourism and hospitality economy news.,http://www.tourism.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7707,Kiribati Statistics Office,National statistics and economic data.,https://www.stats.gov.ki/rss,4,Economía,97,Kiribati,en,False,30 +7702,Pacific Islands Forum Economy,Forum economic news including Kiribati.,https://www.forumsec.org/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7691,Pacific Islands News Association Economy,PINA economic news on Kiribati.,https://www.pina.com.fj/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7692,Pacific News Service Economy,Regional economic news service.,https://www.pacnews.com/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7684,President of Kiribati Economy,Presidential office economic announcements.,http://www.president.gov.ki/rss/economy,4,Economía,97,Kiribati,en,False,30 +7689,RNZ Pacific Kiribati Economy,Radio New Zealand Pacific economic coverage.,https://www.rnz.co.nz/rss/pacific/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7695,Reuters Kiribati Economy,Reuters economic news on Kiribati.,https://www.reuters.com/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7701,SPC Pacific Community Economy,SPC economic development news.,https://www.spc.int/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7693,The Guardian Pacific Kiribati Economy,Guardian economic coverage of Kiribati.,https://www.theguardian.com/rss/kiribati-economy,4,Economía,97,Kiribati,en,True,0 +7699,UNDP Kiribati Economy,UNDP economic development news.,https://www.undp.org/rss/kiribati/economy,4,Economía,97,Kiribati,en,False,30 +7696,World Bank Kiribati Economy,World Bank economic reports and projects.,https://www.worldbank.org/rss/kiribati-economy,4,Economía,97,Kiribati,en,False,30 +7664,ABC Pacific Kiribati,Australian Broadcasting Corporation coverage.,https://www.abc.net.au/pacific/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7670,Al Jazeera Kiribati,Al Jazeera coverage of Kiribati.,https://www.aljazeera.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7668,BBC News Kiribati,BBC coverage of Kiribati.,https://www.bbc.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7680,Climate Home News Kiribati,Climate change and politics in Kiribati.,https://www.climatechangenews.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7674,Commonwealth News Kiribati,Commonwealth coverage of Kiribati.,https://thecommonwealth.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7672,Eurasianet Kiribati,News and analysis on Kiribati.,https://eurasianet.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7675,Forum Fisheries Agency,Pacific fisheries politics and Kiribati.,https://www.ffa.int/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7660,Government of Kiribati,Official government news and announcements.,http://www.gov.ki/rss/politics,11,Política,97,Kiribati,en,False,30 +7678,IMF Kiribati,IMF news and reports on Kiribati.,https://www.imf.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7682,Kiribati Customs Service,Customs and trade related news.,https://www.customs.gov.ki/rss,11,Política,97,Kiribati,en,False,30 +7658,Kiribati Independent,Independent news and political coverage.,http://www.kiribatiindependent.com/rss/politics,11,Política,97,Kiribati,en,False,30 +7659,Kiribati Newstar,Local news and political updates.,http://www.kiribatinewstar.com/rss/politics,11,Política,97,Kiribati,en,False,30 +7662,Kiribati Parliament,Parliamentary proceedings and news.,http://www.parliament.gov.ki/rss,11,Política,97,Kiribati,en,False,30 +7681,Pacific Islands Forum,Regional forum news including Kiribati.,https://www.forumsec.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7665,Pacific Islands News Association,PINA news on Kiribati.,https://www.pina.com.fj/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7666,Pacific News Service,Regional news service covering Kiribati.,https://www.pacnews.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7661,President of Kiribati,Office of the President news.,http://www.president.gov.ki/rss,11,Política,97,Kiribati,en,False,30 +7663,RNZ Pacific Kiribati,Radio New Zealand Pacific coverage of Kiribati.,https://www.rnz.co.nz/rss/pacific/kiribati,11,Política,97,Kiribati,en,False,30 +7669,Reuters Kiribati,Reuters coverage of Kiribati.,https://www.reuters.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7676,SPC Pacific Community,SPC development news on Kiribati.,https://www.spc.int/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7671,The Diplomat Kiribati,Analysis of Kiribati politics and regional issues.,https://thediplomat.com/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7667,The Guardian Pacific Kiribati,Guardian coverage of Kiribati.,https://www.theguardian.com/rss/kiribati,11,Política,97,Kiribati,en,True,0 +7673,UN News Kiribati,United Nations news on Kiribati.,https://news.un.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7679,UNDP Kiribati,UNDP development news on Kiribati.,https://www.undp.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7677,World Bank Kiribati,World Bank projects and news on Kiribati.,https://www.worldbank.org/rss/kiribati,11,Política,97,Kiribati,en,False,30 +7769,ABC Pacific Kiribati Society,Australian Broadcasting Corporation society news.,https://www.abc.net.au/pacific/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7773,BBC News Kiribati Society,BBC society coverage of Kiribati.,https://www.bbc.com/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7763,Government of Kiribati Society,Official government society news.,http://www.gov.ki/rss/society,13,Sociedad,97,Kiribati,en,False,30 +7785,Kiribati Culture and Arts,Cultural and arts society news.,https://www.culture.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7787,Kiribati Disability Services,Disability and inclusion society news.,https://www.disability.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7767,Kiribati Independent Society,Independent society news coverage.,http://www.kiribatiindependent.com/rss/society,13,Sociedad,97,Kiribati,en,False,30 +7783,Kiribati National Council of Churches,Religious and community society news.,https://www.churches.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7784,Kiribati Sports Federation,Sports and recreation society news.,https://www.sports.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7786,Kiribati Youth Development,Youth and community development news.,https://www.youth.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7765,Ministry of Education Kiribati,Education and social development.,http://www.education.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7764,Ministry of Health Kiribati,Health and social welfare news.,http://www.health.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7766,Ministry of Women and Social Affairs,Women and social affairs news.,http://www.women.gov.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7781,Pacific Islands Forum Society,Forum society news including Kiribati.,https://www.forumsec.org/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7770,Pacific Islands News Association Society,PINA society news on Kiribati.,https://www.pina.com.fj/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7771,Pacific News Service Society,Regional society news service.,https://www.pacnews.com/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7768,RNZ Pacific Kiribati Society,Radio New Zealand Pacific society coverage.,https://www.rnz.co.nz/rss/pacific/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7782,Red Cross Kiribati,Red Cross humanitarian and society news.,https://www.redcross.org.ki/rss,13,Sociedad,97,Kiribati,en,False,30 +7774,Reuters Kiribati Society,Reuters society news on Kiribati.,https://www.reuters.com/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7780,SPC Pacific Community Society,SPC social development in Kiribati.,https://www.spc.int/rss/kiribati-society,13,Sociedad,97,Kiribati,en,False,30 +7772,The Guardian Pacific Kiribati Society,Guardian society coverage of Kiribati.,https://www.theguardian.com/rss/kiribati-society,13,Sociedad,97,Kiribati,en,True,0 +7779,UN Women Kiribati,UN Women gender and society news.,https://www.unwomen.org/rss/kiribati,13,Sociedad,97,Kiribati,en,False,30 +7776,UNDP Kiribati Society,UNDP social development news.,https://www.undp.org/rss/kiribati/society,13,Sociedad,97,Kiribati,en,False,30 +7775,UNICEF Kiribati,UNICEF news on children and society.,https://www.unicef.org/rss/kiribati,13,Sociedad,97,Kiribati,en,False,30 +7778,WHO Kiribati Society,WHO public health and society news.,https://www.who.int/rss/kiribati/society,13,Sociedad,97,Kiribati,en,False,30 +7777,World Bank Kiribati Society,World Bank social development projects.,https://www.worldbank.org/rss/kiribati/society,13,Sociedad,97,Kiribati,en,False,30 +7855,Al Arabiya Science Kuwait,Al Arabiya science news on Kuwait.,https://www.alarabiya.net/rss/kuwait-science,1,Ciencia,98,Kuwait,ar,False,30 +7854,Al Jazeera Science Kuwait,Al Jazeera science news on Kuwait.,https://www.aljazeera.net/rss/kuwait-science,1,Ciencia,98,Kuwait,ar,False,30 +7841,Al Qabas Kuwait Science,Arabic independent newspaper science.,https://www.alqabas.com/rss/science,1,Ciencia,98,Kuwait,ar,False,30 +7840,Al Rai Kuwait Science,Arabic newspaper science news.,https://www.alraimedia.com/rss/science,1,Ciencia,98,Kuwait,ar,False,30 +7839,Arab Times Science,English newspaper science news.,https://www.arabtimesonline.com/rss/science,1,Ciencia,98,Kuwait,en,False,30 +7850,Dasman Diabetes Institute,Diabetes and medical research.,https://www.dasmaninstitute.org/rss,1,Ciencia,98,Kuwait,en,True,0 +7861,FAO Kuwait,FAO agricultural science news.,https://www.fao.org/rss/kuwait,1,Ciencia,98,Kuwait,en,False,30 +7842,KUNA Science,Kuwait News Agency science news.,https://www.kuna.net.kw/rss/science,1,Ciencia,98,Kuwait,ar,False,30 +7851,Kuwait Cancer Control Center,Cancer research and treatment.,https://www.kccc.org.kw/rss,1,Ciencia,98,Kuwait,ar,False,30 +7847,Kuwait Environment Public Authority,Environmental science news.,https://www.epa.org.kw/rss,1,Ciencia,98,Kuwait,ar,False,30 +7845,Kuwait Foundation for Advancement of Sciences,KFAS science advancement news.,https://www.kfas.org/rss,1,Ciencia,98,Kuwait,ar,False,30 +7844,Kuwait Institute for Scientific Research,KISR scientific research news.,https://www.kisr.edu.kw/rss,1,Ciencia,98,Kuwait,en,False,30 +7848,Kuwait Meteorological Department,Meteorology and climate science.,https://www.met.gov.kw/rss,1,Ciencia,98,Kuwait,ar,False,30 +7846,Kuwait Ministry of Health Science,Health science and research.,https://www.moh.gov.kw/rss/science,1,Ciencia,98,Kuwait,ar,False,30 +7849,Kuwait Petroleum Corporation Research,Oil and energy research.,https://www.kpc.com.kw/research/rss,1,Ciencia,98,Kuwait,en,False,30 +7852,Kuwait Science Club,Science education and outreach.,https://www.ksc.org.kw/rss,1,Ciencia,98,Kuwait,ar,False,30 +7838,Kuwait Times Science,English newspaper science coverage.,https://www.kuwaittimes.com/rss/science,1,Ciencia,98,Kuwait,en,False,30 +7843,Kuwait University,Kuwait University research and science.,https://www.ku.edu.kw/rss,1,Ciencia,98,Kuwait,ar,False,30 +7857,Nature Middle East,Nature journal Middle East news.,https://www.natureasia.com/middle-east/rss,1,Ciencia,98,Kuwait,en,False,30 +7853,Reuters Kuwait Science,Reuters science news on Kuwait.,https://www.reuters.com/rss/kuwait-science,1,Ciencia,98,Kuwait,en,False,30 +7858,Science and Development Network,SciDev.Net Middle East news.,https://www.scidev.net/middle-east/rss,1,Ciencia,98,Kuwait,en,False,30 +7856,Scientific American Arabic,Arabic science magazine.,https://www.scientificamerican.com/arabic/rss,1,Ciencia,98,Kuwait,ar,False,30 +7862,UNDP Kuwait Science,UNDP science and technology projects.,https://www.undp.org/rss/kuwait/science,1,Ciencia,98,Kuwait,en,False,30 +7859,UNESCO Kuwait,UNESCO science programs in Kuwait.,https://www.unesco.org/rss/kuwait,1,Ciencia,98,Kuwait,en,False,30 +7860,WHO Kuwait,World Health Organization Kuwait news.,https://www.who.int/rss/kuwait,1,Ciencia,98,Kuwait,en,False,30 +7820,AFP Kuwait,AFP news coverage of Kuwait.,https://www.afp.com/rss/kuwait,11,Política,98,Kuwait,en,False,30 +7821,AP News Kuwait,Associated Press coverage.,https://apnews.com/rss/kuwait,11,Política,98,Kuwait,en,False,30 +7807,Al Anbaa Kuwait,Arabic newspaper political focus.,https://www.alanba.com.kw/rss/politics,11,Política,98,Kuwait,ar,False,30 +7818,Al Arabiya Kuwait,Al Arabiya coverage of Kuwait.,https://www.alarabiya.net/rss/kuwait,11,Política,98,Kuwait,ar,False,30 +7825,Al Hayat Kuwait,Al Hayat coverage of Kuwait.,https://www.alhayat.com/rss/kuwait,11,Política,98,Kuwait,ar,False,30 +7805,Al Jarida Kuwait,Arabic newspaper political coverage.,https://www.aljarida.com/rss/politics,11,Política,98,Kuwait,ar,False,30 +7817,Al Jazeera Kuwait,Al Jazeera coverage of Kuwait.,https://www.aljazeera.net/rss/kuwait,11,Política,98,Kuwait,ar,False,30 +7804,Al Qabas Kuwait,Arabic independent newspaper politics.,https://www.alqabas.com/rss/politics,11,Política,98,Kuwait,ar,False,30 +7803,Al Rai Kuwait,Arabic newspaper political news.,https://www.alraimedia.com/rss/politics,11,Política,98,Kuwait,ar,False,30 +7811,Al Seyassah Kuwait,Arabic newspaper political coverage.,https://www.alseyassah.com/rss/politics,11,Política,98,Kuwait,ar,False,30 +7810,Al Watan Kuwait,Arabic newspaper political news.,https://www.alwatan.com.kw/rss/politics,11,Política,98,Kuwait,ar,False,30 +7814,Amiri Diwan Kuwait,Office of the Emir of Kuwait.,https://www.da.gov.kw/rss,11,Política,98,Kuwait,ar,False,30 +7802,Arab Times Politics,English newspaper political news.,https://www.arabtimesonline.com/rss/politics,11,Política,98,Kuwait,en,False,30 +7824,Asharq Al Awsat Kuwait,Asharq Al Awsat coverage.,https://www.aawsat.com/rss/kuwait,11,Política,98,Kuwait,ar,False,30 +7819,BBC News Kuwait,BBC coverage of Kuwait.,https://www.bbc.com/rss/kuwait,11,Política,98,Kuwait,en,False,30 +7809,KUNA Arabic,Arabic version of Kuwait News Agency.,https://www.kuna.net.kw/ar/rss/politics,11,Política,98,Kuwait,ar,False,30 +7806,KUNA News Agency,Kuwait News Agency official news.,https://www.kuna.net.kw/rss/politics,11,Política,98,Kuwait,ar,False,30 +7815,Kuwait Government Online,Official government portal.,https://www.e.gov.kw/rss,11,Política,98,Kuwait,ar,False,30 +7813,Kuwait National Assembly,National Assembly proceedings.,https://www.kna.kw/rss,11,Política,98,Kuwait,ar,False,30 +7808,Kuwait TV,Kuwait Television political coverage.,https://www.kuna.net.kw/tv/rss/politics,11,Política,98,Kuwait,ar,False,30 +7801,Kuwait Times Politics,English newspaper political coverage.,https://www.kuwaittimes.com/rss/politics,11,Política,98,Kuwait,en,False,30 +7822,Middle East Eye Kuwait,Middle East Eye coverage.,https://www.middleeasteye.net/rss/kuwait,11,Política,98,Kuwait,en,False,30 +7812,Ministry of Foreign Affairs Kuwait,Kuwaiti foreign policy news.,https://www.mofa.gov.kw/rss,11,Política,98,Kuwait,ar,False,30 +7816,Reuters Kuwait,Reuters coverage of Kuwait.,https://www.reuters.com/rss/kuwait,11,Política,98,Kuwait,en,False,30 +7823,The National UAE Kuwait,The National UAE coverage.,https://www.thenationalnews.com/rss/kuwait,11,Política,98,Kuwait,en,False,30 +6352,crisisgroup.org Kuwait,,https://www.crisisgroup.org/rss/163,11,Política,98,Kuwait,,True,0 +7933,AFP Kuwait Society,AFP society news on Kuwait.,https://www.afp.com/rss/kuwait-society,13,Sociedad,98,Kuwait,en,False,30 +7920,Al Anbaa Kuwait Society,Arabic newspaper society focus.,https://www.alanba.com.kw/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7931,Al Arabiya Society Kuwait,Al Arabiya society news on Kuwait.,https://www.alarabiya.net/rss/kuwait-society,13,Sociedad,98,Kuwait,ar,False,30 +7918,Al Jarida Kuwait Society,Arabic newspaper society coverage.,https://www.aljarida.com/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7930,Al Jazeera Society Kuwait,Al Jazeera society news on Kuwait.,https://www.aljazeera.net/rss/kuwait-society,13,Sociedad,98,Kuwait,ar,False,30 +7917,Al Qabas Kuwait Society,Arabic independent newspaper society.,https://www.alqabas.com/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7916,Al Rai Kuwait Society,Arabic newspaper society news.,https://www.alraimedia.com/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7915,Arab Times Society,English newspaper society news.,https://www.arabtimesonline.com/rss/society,13,Sociedad,98,Kuwait,en,False,30 +7932,BBC News Kuwait Society,BBC society coverage of Kuwait.,https://www.bbc.com/rss/kuwait-society,13,Sociedad,98,Kuwait,en,False,30 +7919,KUNA Society,Kuwait News Agency society news.,https://www.kuna.net.kw/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7927,Kuwait Central Statistical Bureau,Social statistics and data.,https://www.csb.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7938,Kuwait Cultural Office,Cultural and artistic society news.,https://www.culture.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7926,Kuwait Environment Public Authority Society,Environmental and community news.,https://www.epa.org.kw/rss/society,13,Sociedad,98,Kuwait,ar,False,30 +7924,Kuwait Ministry of Awqaf and Islamic Affairs,Religious and endowment affairs.,https://www.awqaf.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7922,Kuwait Ministry of Education,Education and academic news.,https://www.moe.edu.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7921,Kuwait Ministry of Health,Health and public welfare news.,https://www.moh.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7923,Kuwait Ministry of Social Affairs,Social affairs and community news.,https://www.mosa.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7925,Kuwait Public Authority for Youth and Sports,Youth and sports development news.,https://www.pays.gov.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7928,Kuwait Red Crescent Society,Humanitarian and relief news.,https://www.redcrescent.org.kw/rss,13,Sociedad,98,Kuwait,ar,False,30 +7914,Kuwait Times Society,English newspaper society coverage.,https://www.kuwaittimes.com/rss/society,13,Sociedad,98,Kuwait,en,False,30 +7929,Reuters Kuwait Society,Reuters society news on Kuwait.,https://www.reuters.com/rss/kuwait-society,13,Sociedad,98,Kuwait,en,False,30 +7937,UN Women Kuwait,UN Women gender and society news.,https://www.unwomen.org/rss/kuwait,13,Sociedad,98,Kuwait,en,False,30 +7935,UNDP Kuwait Society,UNDP social development news.,https://www.undp.org/rss/kuwait/society,13,Sociedad,98,Kuwait,en,False,30 +7934,UNICEF Kuwait,UNICEF news on children and society.,https://www.unicef.org/rss/kuwait,13,Sociedad,98,Kuwait,en,False,30 +7936,WHO Kuwait Society,WHO public health and society news.,https://www.who.int/rss/kuwait/society,13,Sociedad,98,Kuwait,en,False,30 +7895,Al Anbaa Kuwait Technology,Arabic newspaper technology focus.,https://www.alanba.com.kw/rss/technology,14,Tecnología,98,Kuwait,ar,True,0 +7893,Al Jarida Kuwait Technology,Arabic newspaper technology coverage.,https://www.aljarida.com/rss/technology,14,Tecnología,98,Kuwait,ar,False,30 +7892,Al Qabas Kuwait Technology,Arabic independent newspaper tech.,https://www.alqabas.com/rss/technology,14,Tecnología,98,Kuwait,ar,False,30 +7891,Al Rai Kuwait Technology,Arabic newspaper technology news.,https://www.alraimedia.com/rss/technology,14,Tecnología,98,Kuwait,ar,False,30 +7890,Arab Times Technology,English newspaper technology news.,https://www.arabtimesonline.com/rss/technology,14,Tecnología,98,Kuwait,en,False,30 +7911,CNET Kuwait,CNET technology news on Kuwait.,https://cnet.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7897,Communications and Information Technology Regulatory Authority,CITRA regulatory news.,https://www.citra.gov.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7894,KUNA Technology,Kuwait News Agency technology news.,https://www.kuna.net.kw/rss/technology,14,Tecnología,98,Kuwait,ar,False,30 +7906,Kuwait Digital Transformation,Digital transformation initiatives.,https://www.digital.gov.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7905,Kuwait Information Technology Society,IT society and community.,https://www.kits.org.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7900,Kuwait Institute for Scientific Research Technology,KISR technology research.,https://www.kisr.edu.kw/technology/rss,14,Tecnología,98,Kuwait,en,False,30 +7898,Kuwait National Guard Technology,National Guard tech developments.,https://www.kng.gov.kw/rss/technology,14,Tecnología,98,Kuwait,ar,False,30 +7904,Kuwait Telecom Company,Kuwait telecom infrastructure.,https://www.telecom.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7889,Kuwait Times Technology,English newspaper technology coverage.,https://www.kuwaittimes.com/rss/technology,14,Tecnología,98,Kuwait,en,False,30 +7899,Kuwait University Technology,University technology research.,https://www.ku.edu.kw/technology/rss,14,Tecnología,98,Kuwait,ar,False,30 +7896,Ministry of Communications Kuwait,Kuwaiti communications ministry.,https://www.moc.gov.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7902,Ooredoo Kuwait,Mobile operator news.,https://www.ooredoo.com.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7907,Reuters Kuwait Technology,Reuters technology news on Kuwait.,https://www.reuters.com/rss/kuwait-technology,14,Tecnología,98,Kuwait,en,False,30 +7908,TechCrunch Kuwait,TechCrunch coverage of Kuwait.,https://techcrunch.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7913,TechRadar Kuwait,TechRadar coverage of Kuwait.,https://techradar.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7903,VIVA Kuwait,Mobile operator news.,https://www.viva.com.kw/rss,14,Tecnología,98,Kuwait,ar,False,30 +7909,VentureBeat Kuwait,VentureBeat coverage of Kuwait.,https://venturebeat.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7912,Wired Kuwait,Wired coverage of Kuwait technology.,https://wired.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7910,ZDNet Kuwait,ZDNet technology news on Kuwait.,https://zdnet.com/rss/kuwait,14,Tecnología,98,Kuwait,en,False,30 +7901,Zain Kuwait,Mobile operator and telecom news.,https://www.kw.zain.com/rss,14,Tecnología,98,Kuwait,ar,False,30 +8056,ASEAN Science Laos,ASEAN noticias de ciencia sobre Laos.,https://asean.org/rss/laos-science,1,Ciencia,99,Laos,en,False,30 +8051,Al Jazeera Science Laos,Al Jazeera ciencia sobre Laos.,https://www.aljazeera.com/rss/laos-science,1,Ciencia,99,Laos,en,False,30 +8050,BBC News Laos Science,BBC noticias de ciencia sobre Laos.,https://www.bbc.com/rss/laos-science,1,Ciencia,99,Laos,en,False,30 +8048,Bangkok Post Laos Science,Periodico tailandes ciencia de Laos.,https://www.bangkokpost.com/rss/laos-science,1,Ciencia,99,Laos,en,False,30 +8054,FAO Laos,FAO ciencia agricola y alimentaria.,https://www.fao.org/rss/laos,1,Ciencia,99,Laos,en,False,30 +8032,Khaosan Pathet Lao Science,Agencia oficial noticias de ciencia de Laos.,https://kpl.gov.la/rss/science,1,Ciencia,99,Laos,lo,False,30 +8045,Lao Department of Forestry,Departamento de Silvicultura.,https://www.dof.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8044,Lao Department of Meteorology and Hydrology,Departamento de Meteorologia e Hidrologia.,https://www.dmh.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8035,Lao National Radio Science,Radio nacional ciencia.,https://www.lnr.org.la/rss/science,1,Ciencia,99,Laos,lo,False,30 +8036,Lao National Television Science,Television nacional ciencia.,https://www.lntv.gov.la/rss/science,1,Ciencia,99,Laos,lo,False,30 +8046,Laotian Times Science,Periodico independiente ciencia.,https://www.laotiantimes.com/rss/science,1,Ciencia,99,Laos,en,False,30 +8034,Pathet Lao Lao News Agency Science,Agencia noticias ciencia del Partido.,https://www.pathetlao.la/rss/science,1,Ciencia,99,Laos,lo,False,30 +8049,Reuters Laos Science,Reuters noticias de ciencia sobre Laos.,https://www.reuters.com/rss/laos-science,1,Ciencia,99,Laos,en,False,30 +8047,The Star Laos Science,Periodico regional ciencia de Laos.,https://www.thestar.com.my/rss/laos-science,1,Ciencia,99,Laos,en,True,0 +8055,UNDP Laos Science,PNUD ciencia y tecnologia.,https://www.undp.org/rss/laos/science,1,Ciencia,99,Laos,en,False,30 +8052,UNESCO Laos,UNESCO ciencia en Laos.,https://www.unesco.org/rss/laos,1,Ciencia,99,Laos,en,False,30 +8033,Vientiane Times Science,Periodico oficial en ingles ciencia.,https://www.vientianetimes.org.la/rss/science,1,Ciencia,99,Laos,en,False,30 +8053,WHO Laos,OMS noticias de ciencia medica.,https://www.who.int/rss/laos,1,Ciencia,99,Laos,en,False,30 +8037,ກະຊວງວິທະຍາສາດ ແລະ ເຕັກໂນໂລຢີ,ຂ່າວສານວິທະຍາສາດແລະເຕັກໂນໂລຢີ,https://www.most.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8038,ກະຊວງສາທາລະນະສຸກ ລາວ,ຂ່າວສານວິທະຍາສາດທາງການແພດ,https://www.moh.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8039,ກະຊວງສຶກສາທິການ ແລະ ກິລາ,ຂ່າວສານການສຶກສາແລະວິທະຍາສາດ,https://www.moes.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8092,ຂ່າວສານປະເທດລາວ ດ້ານວິທະຍາສາດ,ຂ່າວສານວິທະຍາສາດຈາກກະຊວງຂ່າວສານ,https://kpl.gov.la/rss/ວິທະຍາສາດ,1,Ciencia,99,Laos,lo,False,30 +8094,ປະເທດລາວຂ່າວສານ ດ້ານວິທະຍາສາດ,ຂ່າວສານວິທະຍາສາດຈາກພັກປະຊາຊົນປະຕິວັດລາວ,https://www.pathetlao.la/rss/ວິທະຍາສາດ,1,Ciencia,99,Laos,lo,False,30 +8040,ມະຫາວິທະຍາໄລແຫ່ງຊາດລາວ,ຂ່າວສານວິທະຍາສາດຈາກມະຫາວິທະຍາໄລ,https://www.nuol.edu.la/rss,1,Ciencia,99,Laos,lo,False,30 +8093,ວຽງຈັນທາມ ດ້ານວິທະຍາສາດ,ຫນັງສືພິມພາສາອັງກິດດ້ານວິທະຍາສາດ,https://www.vientianetimes.org.la/rss/ວິທະຍາສາດ,1,Ciencia,99,Laos,lo,False,30 +8041,ສະຖາບັນວິທະຍາສາດສັງຄົມແຫ່ງຊາດລາວ,ຂ່າວສານວິທະຍາສາດສັງຄົມ,https://www.lass.org.la/rss,1,Ciencia,99,Laos,lo,False,30 +8042,ສະຖາບັນສຸຂະພາບທ້ອງຖິ່ນ ແລະ ສາທາລະນະສຸກ,ຂ່າວສານວິທະຍາສາດສຸຂະພາບ,https://www.ltphi.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +8043,ສະຖາບັນສຸຂະພາບແຫ່ງຊາດລາວ,ຂ່າວສານວິທະຍາສາດສຸຂະພາບ,https://www.nih.gov.la/rss,1,Ciencia,99,Laos,lo,False,30 +7974,ADB Laos Economy,Banco Asiatico Desarrollo economia.,https://www.adb.org/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7976,ASEAN Economy Laos,ASEAN noticias economicas sobre Laos.,https://asean.org/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7971,Al Jazeera Economy Laos,Al Jazeera economia sobre Laos.,https://www.aljazeera.com/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7970,BBC News Laos Economy,BBC noticias economicas sobre Laos.,https://www.bbc.com/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7968,Bangkok Post Laos Economy,Periodico tailandes economia de Laos.,https://www.bangkokpost.com/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7960,Bank of the Lao PDR,Banco Central de Laos.,https://www.bol.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7973,IMF Laos Economy,FMI economia de Laos.,https://www.imf.org/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7952,Khaosan Pathet Lao Economy,Agencia oficial noticias economicas de Laos.,https://kpl.gov.la/rss/economy,4,Economía,99,Laos,lo,False,30 +7962,Lao National Chamber of Commerce,Camara Nacional de Comercio.,https://www.lncci.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7955,Lao National Radio Economy,Radio nacional economia.,https://www.lnr.org.la/rss/economy,4,Economía,99,Laos,lo,False,30 +7956,Lao National Television Economy,Television nacional economia.,https://www.lntv.gov.la/rss/economy,4,Economía,99,Laos,lo,False,30 +7961,Lao Securities Exchange,Bolsa de Valores de Laos.,https://www.lsx.com.la/rss,4,Economía,99,Laos,en,False,30 +7965,Lao Statistics Bureau,Oficina de Estadisticas de Laos.,https://www.lsb.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7966,Laotian Times Economy,Periodico independiente economia.,https://www.laotiantimes.com/rss/economy,4,Economía,99,Laos,en,False,30 +7963,Ministry of Agriculture and Forestry,Ministerio de Agricultura y Silvicultura.,https://www.maf.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7964,Ministry of Energy and Mines,Ministerio de Energia y Minas.,https://www.mem.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7957,Ministry of Finance Laos,Ministerio de Finanzas de Laos.,https://www.mof.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7959,Ministry of Industry and Commerce,Ministerio de Industria y Comercio.,https://www.moic.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7958,Ministry of Planning and Investment,Ministerio de Planificacion e Inversion.,https://www.mpi.gov.la/rss,4,Economía,99,Laos,lo,False,30 +7954,Pathet Lao Lao News Agency Economy,Agencia noticias economia del Partido.,https://www.pathetlao.la/rss/economy,4,Economía,99,Laos,lo,False,30 +7969,Reuters Laos Economy,Reuters noticias economicas sobre Laos.,https://www.reuters.com/rss/laos-economy,4,Economía,99,Laos,en,False,30 +7967,The Star Laos Economy,Periodico regional economia de Laos.,https://www.thestar.com.my/rss/laos-economy,4,Economía,99,Laos,en,True,0 +7975,UNDP Laos Economy,PNUD desarrollo economico.,https://www.undp.org/rss/laos/economy,4,Economía,99,Laos,en,False,30 +7953,Vientiane Times Economy,Periodico oficial en ingles economia.,https://www.vientianetimes.org.la/rss/economy,4,Economía,99,Laos,en,False,30 +7972,World Bank Laos Economy,Banco Mundial economia de Laos.,https://www.worldbank.org/rss/laos-economy,4,Economía,99,Laos,en,False,30 +8017,ADB Laos,Banco Asiatico de Desarrollo sobre Laos.,https://www.adb.org/rss/laos,11,Política,99,Laos,en,False,30 +8013,ASEAN News Laos,Noticias de la ASEAN sobre Laos.,https://asean.org/rss/laos,11,Política,99,Laos,en,False,30 +8007,Al Jazeera Laos,Al Jazeera cobertura de Laos.,https://www.aljazeera.com/rss/laos,11,Política,99,Laos,en,False,30 +8006,BBC News Laos,BBC noticias sobre Laos.,https://www.bbc.com/rss/laos,11,Política,99,Laos,en,False,30 +8004,Bangkok Post Laos,Periodico tailandes cubriendo Laos.,https://www.bangkokpost.com/rss/laos,11,Política,99,Laos,en,False,30 +8012,Eurasianet Laos,Noticias sobre Asia Sudoriental.,https://eurasianet.org/rss/laos,11,Política,99,Laos,en,False,30 +8016,IMF Laos,FMI noticias sobre Laos.,https://www.imf.org/rss/laos,11,Política,99,Laos,en,False,30 +7993,Khaosan Pathet Lao,Agencia oficial de noticias de Laos.,https://kpl.gov.la/rss/politics,11,Política,99,Laos,lo,False,30 +7996,Lao National Radio,Radio nacional de Laos.,https://www.lnr.org.la/rss/politics,11,Política,99,Laos,lo,False,30 +7997,Lao National Television,Television nacional de Laos.,https://www.lntv.gov.la/rss/politics,11,Política,99,Laos,lo,False,30 +8002,Laotian Times,Periodico independiente en ingles.,https://www.laotiantimes.com/rss/politics,11,Política,99,Laos,en,False,30 +7995,Pathet Lao Lao News Agency,Agencia de noticias del Partido.,https://www.pathetlao.la/rss/politics,11,Política,99,Laos,lo,False,30 +8009,Radio Free Asia English,Radio Free Asia en ingles sobre Laos.,https://www.rfa.org/english/rss/laos,11,Política,99,Laos,en,False,30 +8008,Radio Free Asia Lao,Radio Free Asia en laosiano.,https://www.rfa.org/lao/rss,11,Política,99,Laos,lo,True,0 +8005,Reuters Laos,Reuters cobertura de Laos.,https://www.reuters.com/rss/laos,11,Política,99,Laos,en,False,30 +8011,The Diplomat Laos,Analisis politico de Laos.,https://thediplomat.com/rss/laos,11,Política,99,Laos,en,False,30 +8003,The Star Laos,Periodico regional que cubre Laos.,https://www.thestar.com.my/rss/laos,11,Política,99,Laos,en,True,0 +8014,UN News Laos,Noticias de la ONU sobre Laos.,https://news.un.org/rss/laos,11,Política,99,Laos,en,False,30 +7994,Vientiane Times,Periodico oficial en ingles de Laos.,https://www.vientianetimes.org.la/rss/politics,11,Política,99,Laos,en,False,30 +8010,Voice of America Lao,VOA en laosiano.,https://www.voanews.com/lao/rss,11,Política,99,Laos,lo,False,30 +8015,World Bank Laos,Banco Mundial noticias sobre Laos.,https://www.worldbank.org/rss/laos,11,Política,99,Laos,en,False,30 +7998,ກະຊວງການຕ່າງປະເທດ ລາວ,ຂ່າວສານການຕ່າງປະເທດ,https://www.mofa.gov.la/rss,11,Política,99,Laos,lo,False,30 +8068,ຂ່າວສານປະເທດລາວ ດ້ານການເມືອງ,ຂ່າວສານການເມືອງຈາກກະຊວງຂ່າວສານ,https://kpl.gov.la/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +8070,ປະເທດລາວຂ່າວສານ ດ້ານການເມືອງ,ຂ່າວສານຈາກພັກປະຊາຊົນປະຕິວັດລາວ,https://www.pathetlao.la/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +8001,ພັກປະຊາຊົນປະຕິວັດລາວ,ຂ່າວສານຈາກພັກປະຊາຊົນປະຕິວັດລາວ,https://www.lprp.gov.la/rss,11,Política,99,Laos,lo,False,30 +8073,ລາວທາມ ດ້ານການເມືອງ,ຫນັງສືພິມອິດສະຫລະດ້ານການເມືອງ,https://www.laotiantimes.com/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +8071,ວິທະຍຸແຫ່ງຊາດລາວ ດ້ານການເມືອງ,ວິທະຍຸແຫ່ງຊາດຂ່າວການເມືອງ,https://www.lnr.org.la/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +8069,ວຽງຈັນທາມ ດ້ານການເມືອງ,ຫນັງສືພິມພາສາອັງກິດດ້ານການເມືອງ,https://www.vientianetimes.org.la/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +7999,ສະພາແຫ່ງຊາດ ລາວ,ຂ່າວສານຈາກສະພາແຫ່ງຊາດ,https://www.na.gov.la/rss,11,Política,99,Laos,lo,False,30 +8000,ຫ້ອງການນາຍົກລັດຖະມົນຕີ ລາວ,ຂ່າວຈາກຫ້ອງການນາຍົກລັດຖະມົນຕີ,https://www.pmoffice.gov.la/rss,11,Política,99,Laos,lo,False,30 +8072,ໂທລະພາບແຫ່ງຊາດລາວ ດ້ານການເມືອງ,ໂທລະພາບແຫ່ງຊາດຂ່າວການເມືອງ,https://www.lntv.gov.la/rss/ການເມືອງ,11,Política,99,Laos,lo,False,30 +8105,ກະຊວງໂທລະຄົມ ລາວ,ຂ່າວສານໂທລະຄົມແລະເຕັກໂນໂລຢີ,https://www.mtc.gov.la/rss,14,Tecnología,99,Laos,lo,False,30 +8102,ຂ່າວສານປະເທດລາວ ດ້ານເຕັກໂນໂລຢີ,ຂ່າວສານເຕັກໂນໂລຢີຈາກກະຊວງຂ່າວສານ,https://kpl.gov.la/rss/ເຕັກໂນໂລຢີ,14,Tecnología,99,Laos,lo,False,30 +8118,ຂ່າວສານປະເທດລາວ ດ້ານເຕັກໂນໂລຢີ,ຂ່າວສານເຕັກໂນໂລຢີຈາກກະຊວງຂ່າວສານ,https://kpl.gov.la/rss/technology,14,Tecnología,99,Laos,lo,False,30 +8110,ບໍລິສັດ ETL,ຂ່າວສານໂທລະຄົມຈາກ ETL,https://www.etllao.com/rss,14,Tecnología,99,Laos,lo,False,30 +8109,ບໍລິສັດ Lao Telecom,ຂ່າວສານໂທລະຄົມຈາກ Lao Telecom,https://www.laotelecom.com/rss,14,Tecnología,99,Laos,lo,True,0 +8108,ບໍລິສັດ Unitel,ຂ່າວສານໂທລະຄົມຈາກ Unitel,https://www.unitel.com.la/rss,14,Tecnología,99,Laos,lo,False,30 +8107,ບໍລິສັດໂທລະຄົມລາວ,ຂ່າວສານໂທລະຄົມຈາກບໍລິສັດລັດ,https://www.ltc.gov.la/rss,14,Tecnología,99,Laos,lo,False,30 +8120,ປະເທດລາວຂ່າວສານ ດ້ານເຕັກໂນໂລຢີ,ຂ່າວສານເຕັກໂນໂລຢີຈາກພັກປະຊາຊົນປະຕິວັດລາວ,https://www.pathetlao.la/rss/technology,14,Tecnología,99,Laos,lo,False,30 +8104,ປະເທດລາວຂ່າວສານ ດ້ານເຕັກໂນໂລຢີ,ຂ່າວສານເຕັກໂນໂລຢີຈາກພັກປະຊາຊົນປະຕິວັດລາວ,https://www.pathetlao.la/rss/ເຕັກໂນໂລຢີ,14,Tecnología,99,Laos,lo,False,30 +8103,ວຽງຈັນທາມ ດ້ານເຕັກໂນໂລຢີ,ຫນັງສືພິມພາສາອັງກິດດ້ານເຕັກໂນໂລຢີ,https://www.vientianetimes.org.la/rss/ເຕັກໂນໂລຢີ,14,Tecnología,99,Laos,lo,False,30 +8119,ວຽງຈັນທາມ ດ້ານເຕັກໂນໂລຢີ,ຫນັງສືພິມພາສາອັງກິດດ້ານເຕັກໂນໂລຢີ,https://www.vientianetimes.org.la/rss/technology,14,Tecnología,99,Laos,lo,False,30 +8111,ສະມາຄົມເຕັກໂນໂລຢີຂໍ້ມູນຂ່າວສານລາວ,ຂ່າວສານເຕັກໂນໂລຢີຂໍ້ມູນ,https://www.lita.org.la/rss,14,Tecnología,99,Laos,lo,False,30 +8106,ອົງການຄຸ້ມຄອງໂທລະຄົມ ລາວ,ຂ່າວສານການຄຸ້ມຄອງໂທລະຄົມ,https://www.tra.gov.la/rss,14,Tecnología,99,Laos,lo,False,30 +8179,Al Jazeera Science Lesotho,Al Jazeera science news on Lesotho.,https://www.aljazeera.com/rss/lesotho-science,1,Ciencia,100,Lesoto,en,False,30 +8176,AllAfrica Lesotho Science,AllAfrica science coverage.,https://allafrica.com/lesotho/science/rss,1,Ciencia,100,Lesoto,en,False,30 +8178,BBC News Lesotho Science,BBC science coverage of Lesotho.,https://www.bbc.com/rss/lesotho-science,1,Ciencia,100,Lesoto,en,False,30 +8174,Department of Environment,Environmental science news.,https://www.environment.gov.ls/rss,1,Ciencia,100,Lesoto,en,True,0 +8182,FAO Lesotho,FAO agricultural science news.,https://www.fao.org/rss/lesotho,1,Ciencia,100,Lesoto,en,False,30 +8165,Informative Newspaper Science,Lesotho science news and analysis.,https://www.informative.co.ls/rss/science,1,Ciencia,100,Lesoto,en,False,30 +8168,Lesotho Agricultural College,Agricultural science and research.,https://www.lac.edu.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8186,Lesotho Astronomical Society,Astronomy and space science.,https://www.astronomy.org.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8175,Lesotho Bureau of Standards,Standards and quality science.,https://www.lbs.org.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8169,Lesotho College of Education,Education and pedagogical science.,https://www.lce.edu.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8170,Lesotho Health Sciences,Medical and health sciences.,https://www.healthsciences.edu.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8173,Lesotho Meteorological Services,Meteorology and climate science.,https://www.met.gov.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8166,Lesotho News Agency Science,LENA official science news.,https://www.lena.gov.ls/rss/science,1,Ciencia,100,Lesoto,en,False,30 +8187,Lesotho Scientific Society,General scientific community news.,https://www.science.org.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8164,Lesotho Times Science,Lesotho newspaper science coverage.,https://www.lestimes.com/rss/science,1,Ciencia,100,Lesoto,en,False,30 +8172,Ministry of Education Lesotho,Education and science policy.,https://www.education.gov.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8171,Ministry of Health Lesotho,Public health science news.,https://www.health.gov.ls/rss,1,Ciencia,100,Lesoto,en,False,30 +8167,National University of Lesotho,University research and science news.,https://www.nul.ls/rss,1,Ciencia,100,Lesoto,en,True,0 +8163,Public Eye Lesotho Science,Lesotho independent science news.,https://www.publiceyelesotho.com/rss/science,1,Ciencia,100,Lesoto,en,False,30 +8177,Reuters Lesotho Science,Reuters science news on Lesotho.,https://www.reuters.com/rss/lesotho-science,1,Ciencia,100,Lesoto,en,False,30 +8185,SADC Science Lesotho,SADC science and technology news.,https://www.sadc.int/rss/lesotho-science,1,Ciencia,100,Lesoto,en,False,30 +8183,UNDP Lesotho Science,UNDP science and technology projects.,https://www.undp.org/rss/lesotho/science,1,Ciencia,100,Lesoto,en,False,30 +8180,UNESCO Lesotho,UNESCO science programs in Lesotho.,https://www.unesco.org/rss/lesotho,1,Ciencia,100,Lesoto,en,False,30 +8181,WHO Lesotho,World Health Organization science news.,https://www.who.int/rss/lesotho,1,Ciencia,100,Lesoto,en,False,30 +8184,World Bank Lesotho Science,World Bank science and research.,https://www.worldbank.org/rss/lesotho/science,1,Ciencia,100,Lesoto,en,False,30 +8153,AFP Lesotho,AFP news coverage of Lesotho.,https://www.afp.com/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8155,Africanews Lesotho,Africanews coverage of Lesotho.,https://www.africanews.com/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8152,Al Jazeera Lesotho,Al Jazeera coverage of Lesotho.,https://www.aljazeera.com/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8149,AllAfrica Lesotho,AllAfrica coverage of Lesotho.,https://allafrica.com/lesotho/politics/rss,11,Política,100,Lesoto,en,False,30 +8150,BBC News Lesotho,BBC coverage of Lesotho.,https://www.bbc.com/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8158,Commonwealth News Lesotho,Commonwealth coverage of Lesotho.,https://thecommonwealth.org/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8145,Government of Lesotho,Official government portal news.,https://www.gov.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8160,IMF Lesotho,IMF news on Lesotho.,https://www.imf.org/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8140,Informative Newspaper,Lesotho news and political analysis.,https://www.informative.co.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8162,Lesotho Council of Churches,Religious and political perspectives.,https://www.lcc.org.ls/rss,11,Política,100,Lesoto,en,False,30 +8144,Lesotho News Agency,LENA official news agency.,https://www.lena.gov.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8143,Lesotho Television,Lesotho national TV news.,https://www.ltv.gov.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8139,Lesotho Times,Lesotho newspaper political coverage.,https://www.lestimes.com/rss/politics,11,Política,100,Lesoto,en,False,30 +8148,Ministry of Foreign Affairs,Foreign policy news.,https://www.foreign.gov.ls/rss,11,Política,100,Lesoto,en,False,30 +8146,Parliament of Lesotho,Parliamentary proceedings.,https://www.parliament.ls/rss,11,Política,100,Lesoto,en,True,0 +8141,Post Newspaper Lesotho,Lesotho weekly newspaper politics.,https://www.thepost.co.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8147,Prime Minister Office Lesotho,PM office announcements.,https://www.pmo.gov.ls/rss,11,Política,100,Lesoto,en,False,30 +8138,Public Eye Lesotho,Lesotho independent news and politics.,https://www.publiceyelesotho.com/rss/politics,11,Política,100,Lesoto,en,False,30 +8142,Radio Lesotho,Lesotho national radio news.,https://www.radiolesotho.co.ls/rss/politics,11,Política,100,Lesoto,en,False,30 +8151,Reuters Lesotho,Reuters coverage of Lesotho.,https://www.reuters.com/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8156,SADC News Lesotho,SADC regional news on Lesotho.,https://www.sadc.int/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8154,The Guardian Africa Lesotho,Guardian coverage of Lesotho.,https://www.theguardian.com/rss/lesotho,11,Política,100,Lesoto,en,True,0 +8157,UN News Lesotho,UN news on Lesotho.,https://news.un.org/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8161,UNDP Lesotho,UNDP development news.,https://www.undp.org/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8159,World Bank Lesotho,World Bank news on Lesotho.,https://www.worldbank.org/rss/lesotho,11,Política,100,Lesoto,en,False,30 +8210,Africa Tech Review Lesotho,African technology reviews.,https://www.africatechreview.com/rss/lesotho,14,Tecnología,100,Lesoto,en,True,0 +8203,AllAfrica Lesotho Technology,AllAfrica technology coverage.,https://allafrica.com/lesotho/technology/rss,14,Tecnología,100,Lesoto,en,False,30 +8205,BBC News Lesotho Technology,BBC technology coverage of Lesotho.,https://www.bbc.com/rss/lesotho-technology,14,Tecnología,100,Lesoto,en,False,30 +8209,Disrupt Africa Lesotho,African startup tech news.,https://www.disrupt-africa.com/rss/lesotho,14,Tecnología,100,Lesoto,en,False,30 +8198,Econet Telecom Lesotho,Mobile operator news.,https://www.econet.co.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8208,IT News Africa Lesotho,African IT news covering Lesotho.,https://www.itnewsafrica.com/rss/lesotho,14,Tecnología,100,Lesoto,en,False,30 +8193,Informative Newspaper Technology,Lesotho technology news and analysis.,https://www.informative.co.ls/rss/technology,14,Tecnología,100,Lesoto,en,False,30 +8196,Lesotho Communications Authority,Telecom regulatory authority.,https://www.lca.org.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8201,Lesotho Computer Society,IT professional association.,https://www.lcs.org.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8214,Lesotho Digital Transformation,Digital transformation initiatives.,https://www.digital.gov.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8202,Lesotho ICT Association,ICT industry association.,https://www.licta.org.ls/rss,14,Tecnología,100,Lesoto,en,True,0 +8215,Lesotho Internet Society,Internet development and access.,https://www.internet.org.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8194,Lesotho News Agency Technology,LENA official technology news.,https://www.lena.gov.ls/rss/technology,14,Tecnología,100,Lesoto,en,False,30 +8199,Lesotho Telecom,National telecom operator.,https://www.telecom.co.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8192,Lesotho Times Technology,Lesotho newspaper technology coverage.,https://www.lestimes.com/rss/technology,14,Tecnología,100,Lesoto,en,False,30 +8195,Ministry of Communications Lesotho,Communications and technology ministry.,https://www.communications.gov.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8200,National University of Lesotho Technology,University technology programs.,https://www.nul.ls/technology/rss,14,Tecnología,100,Lesoto,en,False,30 +8191,Public Eye Lesotho Technology,Lesotho independent technology news.,https://www.publiceyelesotho.com/rss/technology,14,Tecnología,100,Lesoto,en,False,30 +8204,Reuters Lesotho Technology,Reuters technology news on Lesotho.,https://www.reuters.com/rss/lesotho-technology,14,Tecnología,100,Lesoto,en,False,30 +8213,SADC Technology Lesotho,SADC technology news.,https://www.sadc.int/rss/lesotho-technology,14,Tecnología,100,Lesoto,en,False,30 +8206,TechCrunch Africa Lesotho,TechCrunch Africa coverage of Lesotho.,https://techcrunch.com/rss/africa/lesotho,14,Tecnología,100,Lesoto,en,False,30 +8211,UNDP Lesotho Technology,UNDP technology projects.,https://www.undp.org/rss/lesotho/technology,14,Tecnología,100,Lesoto,en,False,30 +8207,VentureBurn Africa Lesotho,African tech entrepreneurship news.,https://ventureburn.com/rss/africa/lesotho,14,Tecnología,100,Lesoto,en,False,30 +8197,Vodacom Lesotho,Mobile operator and telecom news.,https://www.vodacom.co.ls/rss,14,Tecnología,100,Lesoto,en,False,30 +8212,World Bank Lesotho Technology,World Bank tech development.,https://www.worldbank.org/rss/lesotho/technology,14,Tecnología,100,Lesoto,en,False,30 +8230,Apollo Latvia Economy,News and opinion portal economy.,https://www.apollo.lv/rss/economy,4,Economía,101,Letonia,lv,True,0 +8233,Bank of Latvia,Latvian central bank news.,https://www.bank.lv/rss,4,Economía,101,Letonia,lv,False,30 +8244,Bloomberg Latvia,Bloomberg news on Latvia economy.,https://www.bloomberg.com/rss/latvia,4,Economía,101,Letonia,en,False,30 +8236,Central Statistical Bureau Latvia,Statistical data and reports.,https://www.csb.gov.lv/rss,4,Economía,101,Letonia,lv,False,30 +8228,Delfi Latvia Economy,Popular news portal economic coverage.,https://www.delfi.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8240,Delfi Russian Economy,Delfi Russian edition economy.,https://rus.delfi.lv/rss/economy,4,Economía,101,Letonia,ru,False,30 +8249,European Commission Latvia,EU economic news on Latvia.,https://ec.europa.eu/rss/latvia,4,Economía,101,Letonia,en,False,30 +8250,Eurostat Latvia,Eurostat data and statistics.,https://ec.europa.eu/eurostat/rss/latvia,4,Economía,101,Letonia,en,False,30 +8245,Financial Times Latvia,FT economic analysis on Latvia.,https://www.ft.com/rss/latvia-economy,4,Economía,101,Letonia,en,False,30 +8248,IMF Latvia,IMF economic reports on Latvia.,https://www.imf.org/rss/latvia,4,Economía,101,Letonia,en,False,30 +8226,LETA Economy,Latvian news agency economic coverage.,https://www.leta.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8227,LSM Economy,Latvian public media economic news.,https://www.lsm.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8242,LSM English Economy,LSM English language economic news.,https://eng.lsm.lv/rss/economy,4,Economía,101,Letonia,en,False,30 +8239,LSM Russian Economy,LSM Russian language economic news.,https://rus.lsm.lv/rss/economy,4,Economía,101,Letonia,ru,False,30 +8238,Latvian Chamber of Commerce,Chamber of commerce news.,https://www.chamber.lv/rss,4,Economía,101,Letonia,lv,False,30 +8231,Latvijas Avize Economy,Latvian newspaper economic news.,https://www.la.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8235,Ministry of Economics Latvia,Ministry of Economics news.,https://www.em.gov.lv/rss,4,Economía,101,Letonia,lv,False,30 +8234,Ministry of Finance Latvia,Ministry of Finance news.,https://www.fm.gov.lv/rss,4,Economía,101,Letonia,lv,False,30 +8232,Neatkariga Rīta Avize Economy,Independent newspaper economy.,https://www.nra.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8243,Reuters Latvia Economy,Reuters economic news on Latvia.,https://www.reuters.com/rss/latvia-economy,4,Economía,101,Letonia,en,False,30 +8237,Riga Stock Exchange,Stock market and financial news.,https://www.nasdaqbaltic.com/rss,4,Economía,101,Letonia,en,False,30 +8229,TVNet Latvia Economy,News website economic coverage.,https://www.tvnet.lv/rss/economy,4,Economía,101,Letonia,lv,False,30 +8241,The Baltic Times Economy,English language Baltic economic news.,https://www.baltictimes.com/rss/latvia-economy,4,Economía,101,Letonia,en,False,30 +8246,The Economist Latvia,The Economist on Latvia economy.,https://www.economist.com/rss/latvia,4,Economía,101,Letonia,en,False,30 +8247,World Bank Latvia,World Bank reports on Latvia.,https://www.worldbank.org/rss/latvia,4,Economía,101,Letonia,en,False,30 +8303,Apollo Latvia Society,News and opinion portal society.,https://www.apollo.lv/rss/society,13,Sociedad,101,Letonia,lv,True,0 +8317,BBC News Latvia Society,BBC society coverage of Latvia.,https://www.bbc.com/rss/latvia-society,13,Sociedad,101,Letonia,en,False,30 +8309,Central Statistical Bureau Society,Social statistics and data.,https://www.csb.gov.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8323,Council of Europe Latvia,Council of Europe social issues.,https://www.coe.int/rss/latvia,13,Sociedad,101,Letonia,en,False,30 +8301,Delfi Latvia Society,Popular news portal society coverage.,https://www.delfi.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8313,Delfi Russian Society,Delfi Russian edition society.,https://rus.delfi.lv/rss/society,13,Sociedad,101,Letonia,ru,False,30 +8322,EU Social Affairs Latvia,EU social affairs in Latvia.,https://ec.europa.eu/social/rss/latvia,13,Sociedad,101,Letonia,en,False,30 +8318,Euronews Society Latvia,Euronews society coverage.,https://www.euronews.com/rss/latvia-society,13,Sociedad,101,Letonia,en,False,30 +8299,LETA Society,Latvian news agency society coverage.,https://www.leta.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8315,LSM English Society,LSM English language society news.,https://eng.lsm.lv/rss/society,13,Sociedad,101,Letonia,en,False,30 +8312,LSM Russian Society,LSM Russian language society news.,https://rus.lsm.lv/rss/society,13,Sociedad,101,Letonia,ru,False,30 +8300,LSM Society,Latvian public media society news.,https://www.lsm.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8311,Latvian Red Cross,Humanitarian and social assistance.,https://www.redcross.lv/rss,13,Sociedad,101,Letonia,lv,True,0 +8304,Latvijas Avize Society,Latvian newspaper society news.,https://www.la.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8308,Ministry of Education and Science Society,Education and society news.,https://www.izm.gov.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8307,Ministry of Health Latvia,Health and social care news.,https://www.vm.gov.lv/rss,13,Sociedad,101,Letonia,lv,False,30 +8306,Ministry of Welfare Latvia,Welfare and social affairs news.,https://www.lm.gov.lv/rss,13,Sociedad,101,Letonia,lv,False,30 +8305,Neatkariga Rīta Avize Society,Independent newspaper society.,https://www.nra.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8316,Reuters Latvia Society,Reuters society news on Latvia.,https://www.reuters.com/rss/latvia-society,13,Sociedad,101,Letonia,en,False,30 +8310,State Employment Agency,Employment and labor market news.,https://www.nva.gov.lv/rss,13,Sociedad,101,Letonia,lv,False,30 +8302,TVNet Latvia Society,News website society coverage.,https://www.tvnet.lv/rss/society,13,Sociedad,101,Letonia,lv,False,30 +8314,The Baltic Times Society,English language Baltic society news.,https://www.baltictimes.com/rss/latvia-society,13,Sociedad,101,Letonia,en,False,30 +8320,UNDP Latvia Society,UNDP social development news.,https://www.undp.org/rss/latvia/society,13,Sociedad,101,Letonia,en,False,30 +8319,UNICEF Latvia,UNICEF news on children and society.,https://www.unicef.org/rss/latvia,13,Sociedad,101,Letonia,en,False,30 +8321,WHO Latvia Society,WHO public health and society news.,https://www.who.int/rss/latvia/society,13,Sociedad,101,Letonia,en,False,30 +8266,Apollo Latvia Technology,News and opinion portal technology.,https://www.apollo.lv/rss/technology,14,Tecnología,101,Letonia,lv,True,0 +8281,ArcticStartup Baltic,Startup news in Baltic region.,https://arcticstartup.com/rss/baltic,14,Tecnología,101,Letonia,en,False,30 +8264,Delfi Latvia Technology,Popular news portal technology coverage.,https://www.delfi.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8275,Delfi Russian Technology,Delfi Russian edition technology.,https://rus.delfi.lv/rss/technology,14,Tecnología,101,Letonia,ru,False,30 +8286,Digital Latvia Initiative,National digital transformation.,https://www.digitalalatvia.lv/rss,14,Tecnología,101,Letonia,lv,False,30 +8282,EU Digital Latvia,EU digital policies in Latvia.,https://digital-strategy.ec.europa.eu/rss/latvia,14,Tecnología,101,Letonia,en,False,30 +8280,Euronews Tech Latvia,Euronews technology coverage.,https://www.euronews.com/rss/latvia-technology,14,Tecnología,101,Letonia,en,False,30 +8262,LETA Technology,Latvian news agency technology coverage.,https://www.leta.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8277,LSM English Technology,LSM English language technology news.,https://eng.lsm.lv/rss/technology,14,Tecnología,101,Letonia,en,False,30 +8274,LSM Russian Technology,LSM Russian language technology news.,https://rus.lsm.lv/rss/technology,14,Tecnología,101,Letonia,ru,False,30 +8263,LSM Technology,Latvian public media technology news.,https://www.lsm.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8285,Latvian Computer Society,Computer science and IT community.,https://www.lkb.lv/rss,14,Tecnología,101,Letonia,lv,False,30 +8271,Latvian Information Technology and Telecommunications Association,IT industry association.,https://www.lita.lv/rss,14,Tecnología,101,Letonia,lv,False,30 +8272,Latvian Startup Association,Startup and tech entrepreneurship.,https://www.startuplatvia.lv/rss,14,Tecnología,101,Letonia,lv,True,0 +8284,Latvian University of Technology,Technical university research.,https://www.rtu.lv/rss,14,Tecnología,101,Letonia,lv,True,0 +8267,Latvijas Avize Technology,Latvian newspaper technology news.,https://www.la.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8269,Ministry of Environmental Protection and Regional Development,Digital transformation and IT.,https://www.varam.gov.lv/rss,14,Tecnología,101,Letonia,lv,False,30 +8283,NATO Cyber Defence,NATO cyber security news.,https://www.nato.int/rss/cyber,14,Tecnología,101,Letonia,en,False,30 +8268,Neatkariga Rīta Avize Technology,Independent newspaper technology.,https://www.nra.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8270,Public Utilities Commission,Telecommunications regulation.,https://www.sprk.gov.lv/rss,14,Tecnología,101,Letonia,lv,False,30 +8278,Reuters Latvia Technology,Reuters technology news on Latvia.,https://www.reuters.com/rss/latvia-technology,14,Tecnología,101,Letonia,en,False,30 +8273,Riga TechGirls,Women in technology organization.,https://www.rigatechgirls.org/rss,14,Tecnología,101,Letonia,lv,True,0 +8265,TVNet Latvia Technology,News website technology coverage.,https://www.tvnet.lv/rss/technology,14,Tecnología,101,Letonia,lv,False,30 +8279,TechCrunch Baltic,TechCrunch coverage of Baltic tech.,https://techcrunch.com/rss/baltic,14,Tecnología,101,Letonia,en,False,30 +8276,The Baltic Times Technology,English language Baltic tech news.,https://www.baltictimes.com/rss/latvia-technology,14,Tecnología,101,Letonia,en,False,30 +8559,AllAfrica Liberia Science,Science news from Liberia via AllAfrica.,https://allafrica.com/tools/headlines/rdf/liberia/science/headlines.rdf,1,Ciencia,103,Liberia,en,False,30 +8561,Cuttington University Science,Scientific programs and research from Cuttington.,https://www.cuttington.edu.lr/rss/science,1,Ciencia,103,Liberia,en,False,30 +8564,FrontPage Africa Science,Science section of FrontPage Africa news.,https://frontpageafricaonline.com/rss/science,1,Ciencia,103,Liberia,en,False,30 +8562,Liberia Agricultural Research,Agricultural science and crop research.,https://www.moa.gov.lr/rss/research,1,Ciencia,103,Liberia,en,False,30 +8567,Liberia Biology Research,Biological sciences and biodiversity studies.,https://www.liberiabiology.org/rss,1,Ciencia,103,Liberia,en,False,30 +8566,Liberia Chemistry Association,Chemistry research and laboratory science.,https://www.liberiachemistry.org/rss,1,Ciencia,103,Liberia,en,False,30 +8563,Liberia Climate Science,Climate change research and meteorological data.,https://www.liberiameteorology.org/rss,1,Ciencia,103,Liberia,en,False,30 +8574,Liberia Data Science,Data analysis and computational science.,https://www.liberiadatascience.org/rss,1,Ciencia,103,Liberia,en,False,30 +8560,Liberia Environmental Research,Environmental science and ecological studies.,https://www.liberiaenvironment.org/rss,1,Ciencia,103,Liberia,en,False,30 +8572,Liberia Geology Survey,Geological research and earth sciences.,https://www.liberiageology.gov.lr/rss,1,Ciencia,103,Liberia,en,False,30 +8557,Liberia Medical Research Institute,Health and biomedical research in Liberia.,https://www.lmri.gov.lr/rss,1,Ciencia,103,Liberia,en,False,30 +8571,Liberia Oceanographic Research,Marine science and ocean research.,https://www.liberiaocean.org/rss,1,Ciencia,103,Liberia,en,False,30 +8568,Liberia Physics Society,Physics research and science education.,https://www.liberiaphysics.org/rss,1,Ciencia,103,Liberia,en,False,30 +8575,Liberia Public Health Research,Epidemiology and public health science.,https://www.liberiaphr.org/rss,1,Ciencia,103,Liberia,en,False,30 +8570,Liberia STEM Education,STEM programs and science education initiatives.,https://www.liberiastem.org/rss,1,Ciencia,103,Liberia,en,False,30 +8565,Liberia Science Council,National scientific policy and research funding.,https://www.lsc.gov.lr/rss,1,Ciencia,103,Liberia,en,False,30 +8569,Liberia Science Journal,Academic publications and scientific papers.,https://www.liberiasciencejournal.org/rss,1,Ciencia,103,Liberia,en,False,30 +8573,Liberia Science Museum,Science exhibitions and educational outreach.,https://www.liberiasciencemuseum.org/rss,1,Ciencia,103,Liberia,en,False,30 +8558,Ministry of Health Liberia Science,Public health research and scientific reports.,https://www.moh.gov.lr/rss,1,Ciencia,103,Liberia,en,True,0 +8556,University of Liberia Science,Scientific research and academic studies from UL.,https://www.ul.edu.lr/rss/science,1,Ciencia,103,Liberia,en,False,30 +8504,AllAfrica Liberia Politics,Liberia politics news from the AllAfrica aggregator.,https://allafrica.com/tools/headlines/rdf/liberia/politics/headlines.rdf,11,Política,103,Liberia,en,False,30 +8507,Daily Observer Liberia Politics,Political coverage from Liberias Daily Observer.,https://www.liberianobserver.com/rss/politics,11,Política,103,Liberia,en,False,30 +8508,Executive Mansion Liberia,Official news from the Office of the President.,https://www.emansion.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8505,FrontPage Africa Politics,Political news and investigations from FrontPage Africa.,https://frontpageafricaonline.com/rss/politics,11,Política,103,Liberia,en,False,30 +8515,GNN Liberia Politics,Political news from the Global News Network.,https://gnnliberia.com/rss/politics,11,Política,103,Liberia,en,False,30 +8512,Heritage Liberia Politics,Political commentary and analysis from Heritage.,https://www.heritageliberia.net/rss/politics,11,Política,103,Liberia,en,False,30 +8521,Liberia Anti-Corruption Commission,Anti-corruption and political integrity news.,https://www.lacc.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8518,Liberia Democracy Watch,Political democracy and governance monitoring.,https://www.liberiademocracywatch.org/rss,11,Política,103,Liberia,en,False,30 +8520,Liberia Human Rights Commission,Human rights and political freedom news.,https://www.lhrc.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8511,Liberia National Elections Commission,Election news and political updates.,https://www.necliberia.org/rss,11,Política,103,Liberia,en,False,30 +8509,Ministry of Foreign Affairs Liberia,Official communications and foreign policy.,https://www.mofa.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8517,Ministry of Justice Liberia,Legal and judicial political news.,https://www.moj.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8510,National Legislature Liberia,News from the Liberian Parliament and Senate.,https://www.legislature.gov.lr/rss,11,Política,103,Liberia,en,False,30 +8516,Political Platform Liberia,Platform for political discourse in Liberia.,https://www.politicalplatformliberia.com/rss,11,Política,103,Liberia,en,False,30 +8513,The Analyst Liberia Politics,Political reports from The Analyst newspaper.,https://www.liberiananalyst.com/rss/politics,11,Política,103,Liberia,en,False,30 +8506,The Bush Chicken Politics,Analysis and reports on Liberian political affairs.,https://bushchicken.com/rss/politics,11,Política,103,Liberia,en,False,30 +8503,The New Dawn Liberia Politics,National politics government and policy from The New Dawn.,https://www.thenewdawnliberia.com/rss/politics,11,Política,103,Liberia,en,False,30 +8519,West Africa Democracy Radio,Liberia political news from regional radio.,https://www.wadr.org/rss/liberia/politics,11,Política,103,Liberia,en,False,30 +8514,Women Voices Liberia,Politics from womens perspective in Liberia.,https://www.womenvoicesliberia.com/rss/politics,11,Política,103,Liberia,en,False,30 +8524,AllAfrica Liberia Society,Society and community news from Liberia.,https://allafrica.com/tools/headlines/rdf/liberia/society/headlines.rdf,13,Sociedad,103,Liberia,en,False,30 +8526,Daily Observer Society,Social issues and community reports.,https://www.liberianobserver.com/rss/lifestyle,13,Sociedad,103,Liberia,en,False,30 +8525,FrontPage Africa Society,Society and lifestyle section.,https://frontpageafricaonline.com/rss/lifestyle,13,Sociedad,103,Liberia,en,False,30 +8530,Liberia Childrens Foundation,Child protection and welfare updates.,https://www.liberiachildren.org/rss,13,Sociedad,103,Liberia,en,False,30 +8527,Liberia Community Development,Local community projects and social initiatives.,https://www.liberiacommunity.org/rss,13,Sociedad,103,Liberia,en,False,30 +8539,Liberia Community Radio,Local radio social programs and discussions.,https://www.liberiacommunityradio.org/rss,13,Sociedad,103,Liberia,en,False,30 +8531,Liberia Disabled Persons Union,Disability rights and inclusion news.,https://www.liberiadpu.org/rss,13,Sociedad,103,Liberia,en,False,30 +8536,Liberia Elderly Care,Senior citizens welfare and social support.,https://www.liberiaelderlycare.org/rss,13,Sociedad,103,Liberia,en,False,30 +8535,Liberia Family Association,Family support and social services.,https://www.liberiafamily.org/rss,13,Sociedad,103,Liberia,en,False,30 +8541,Liberia Neighborhood Watch,Community safety and social cohesion.,https://www.liberianeighborhoodwatch.org/rss,13,Sociedad,103,Liberia,en,False,30 +8532,Liberia Religious Council,Interfaith dialogue and religious social action.,https://www.liberiareligiouscouncil.org/rss,13,Sociedad,103,Liberia,en,False,30 +8534,Liberia Rural Development,Rural community and village social news.,https://www.liberiarural.org/rss,13,Sociedad,103,Liberia,en,False,30 +8540,Liberia Social Issues Forum,Discussions on contemporary social problems.,https://www.liberiasocialforum.org/rss,13,Sociedad,103,Liberia,en,False,30 +8537,Liberia Social Workers,Social work profession and community practice.,https://www.liberiasocialworkers.org/rss,13,Sociedad,103,Liberia,en,False,30 +8533,Liberia Urban Development,Urban social issues and city community news.,https://www.liberiaurban.org/rss,13,Sociedad,103,Liberia,en,False,30 +8538,Liberia Volunteering Network,Volunteer opportunities and civic engagement.,https://www.liberiavolunteer.org/rss,13,Sociedad,103,Liberia,en,False,30 +8529,Liberia Women Empowerment,Women rights and gender equality news.,https://www.liberiawomen.org/rss,13,Sociedad,103,Liberia,en,False,30 +8523,Liberia Youth Network,Youth development and social engagement.,https://www.liberiayouthnetwork.org/rss,13,Sociedad,103,Liberia,en,False,30 +8522,Ministry of Gender Liberia Society,Children protection family and social welfare.,https://www.mogcsp.gov.lr/rss,13,Sociedad,103,Liberia,en,True,0 +8528,The New Dawn Society,Social news and community affairs.,https://www.thenewdawnliberia.com/rss/community,13,Sociedad,103,Liberia,en,False,30 +8587,218 TV Libya Politics,Libyan TV network political coverage.,https://www.218tv.net/rss/politics,11,Política,104,Libia,ar,False,30 +8580,Al-Marsad Libya,Libyan political observatory and news.,https://almarsad.co/rss/politics,11,Política,104,Libia,ar,False,30 +8586,Al-Nabaa TV Libya,News channel covering Libyan political affairs.,https://alnabaa.tv/rss/news,11,Política,104,Libia,ar,False,30 +8577,Al-Wasat Libya Politics,Libyan political news from Al-Wasat newspaper.,https://alwasat.ly/news/rss/politics,11,Política,104,Libia,ar,False,30 +8578,AllAfrica Libya Politics,Libya political news from AllAfrica aggregator.,https://allafrica.com/tools/headlines/rdf/libya/politics/headlines.rdf,11,Política,104,Libia,en,False,30 +8581,Libya Al-Ahrar TV,News and political analysis from Libya Al-Ahrar.,https://www.libyaalahrar.tv/rss/news,11,Política,104,Libia,ar,False,30 +8585,Libya Analysis,Analysis of Libyan politics and security.,https://www.libya-analysis.com/rss,11,Política,104,Libia,en,False,30 +8576,Libya Herald Politics,Political news from Libya Herald English newspaper.,https://www.libyaherald.com/rss/politics,11,Política,104,Libia,en,False,30 +8588,Libya Ministry of Foreign Affairs,Foreign policy and international relations.,https://www.foreignaffairs.gov.ly/rss,11,Política,104,Libia,ar,False,30 +8589,Libya Monitor,Political risk and monitoring service.,https://www.libyanmonitor.com/rss,11,Política,104,Libia,en,False,30 +8590,Libya Times Politics,Online news site covering Libyan politics.,https://libyatimes.net/rss/politics,11,Política,104,Libia,en,True,0 +8582,Libya's House of Representatives,Official news from the Tobruk-based parliament.,https://www.parliament.ly/rss,11,Política,104,Libia,ar,False,30 +8584,Libyan Political Dialogue,UN-sponsored political process updates.,https://unsmil.unmissions.org/rss/taxonomy/term/1,11,Política,104,Libia,en,False,30 +8583,Presidential Council Libya,News from the Presidential Council in Tripoli.,https://www.presidency.gov.ly/rss,11,Política,104,Libia,ar,False,30 +8579,Reuters Libya Politics,International coverage of Libyan politics.,https://www.reuters.com/rss/topNews?country=LY,11,Política,104,Libia,en,False,30 +8668,Data Science Institut Ciencia,Investigacion en ciencia de datos y analitica.,https://www.datascience.li/feed/,1,Ciencia,105,Liechtenstein,en,False,30 +8664,Ingenieurwissenschaften Ciencia,Investigacion en ingenieria y ciencias aplicadas.,https://www.ingenieur.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8670,Innovationszentrum Ciencia,Centro de innovacion y transferencia de conocimiento cientifico.,https://www.innovationszentrum.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8662,Institut fur Wissenschaft Ciencia,Instituto de investigacion cientifica y desarrollo tecnologico.,https://www.iw.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8663,Liechtensteinische Naturwissenschaft Ciencia,Estudios de ciencias naturales y medio ambiente.,https://www.naturwissenschaft.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8665,Medizinische Forschung Ciencia,Investigacion medica y proyectos de salud publica.,https://www.medforschung.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8667,Umweltforschung Ciencia,Investigacion ambiental y sostenibilidad.,https://www.umweltforschung.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8661,Universitat Liechtenstein Ciencia,Investigacion y estudios cientificos en la universidad estatal.,https://www.uni.li/forschung/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8669,Wissenschaftskooperation Ciencia,Colaboraciones cientificas internacionales y proyectos.,https://www.wissenschaftkoop.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8666,Zukunftstechnologien Ciencia,Estudio de tecnologias futuras y ciencia de materiales.,https://www.zukunftstech.li/feed/,1,Ciencia,105,Liechtenstein,de,False,30 +8655,Finanzmarktaufsicht Economia,Autoridad de supervision del mercado financiero (FMA).,https://www.fma-li.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8658,Finanzplatz Liechtenstein Economia,Noticias y analisis del centro financiero de Liechtenstein.,https://www.finanzplatz.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8659,Investmentfonds Economia,Informacion sobre fondos de inversion y gestion de activos.,https://www.investmentfonds.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8656,Liechtenstein Bankers Association Economia,Asociacion de bancos y sector financiero.,https://www.bankenverband.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8653,Liechtensteinische Landesbank Economia,Banco nacional y principal institucion financiera del pais.,https://www.llb.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8654,Ministerium fur Finanzen Economia,Ministerio de finanzas y politica fiscal de Liechtenstein.,https://www.finanzministerium.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8660,Steuerwesen Economia,Noticias fiscales y sistema tributario de Liechtenstein.,https://www.steuerverwaltung.li/feed/,4,Economía,105,Liechtenstein,de,False,30 +8657,Wirtschaftskammer Economia,Camara de comercio e industria para empresas y negocios.,https://www.wirtschaftskammer.li/feed/,4,Economía,105,Liechtenstein,de,True,0 +8646,1FL TV Politica,Canal de television con noticias y reportajes politicos.,https://www.1fl.tv/feed/,11,Política,105,Liechtenstein,de,False,30 +8652,Agencia de Noticias LI Politica,Agencia de noticias local con enfoque politico.,https://www.anli.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8651,Liechtenstein Global Politica,Analisis politico y relaciones internacionales.,https://www.global.li/feed/,11,Política,105,Liechtenstein,en,False,30 +8648,Parlamento Liechtenstein Politica,Noticias y actividades del parlamento (Landtag).,https://www.landtag.li/feed/,11,Política,105,Liechtenstein,de,True,0 +8650,Partido Conciencia Ciudadana Politica,Noticias del Partido Conciencia Ciudadana (DU).,https://www.du.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8649,Partido Popular Politica,Noticias y posiciones del Partido Popular (FBP).,https://www.fbp.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8647,Portal del Gobierno Politica,Comunicados oficiales y noticias del gobierno.,https://www.regierung.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8645,Radio L Politica,Radio nacional con noticias y debates politicos.,https://www.radio.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8643,Vaterland Politica,Periodico principal con cobertura politica y noticias nacionales.,https://www.vaterland.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8644,Volksblatt Politica,Diario con analisis politico y noticias de Liechtenstein.,https://www.volksblatt.li/feed/,11,Política,105,Liechtenstein,de,False,30 +8683,Mokslo Lietuva Ciencia,Portal nacional de noticias cientificas y avances en investigacion.,https://www.mokslaslietuvoje.lt/rss,1,Ciencia,106,Lituania,lt,False,30 +8682,Vilniaus Universitetas Ciencia,Investigacion cientifica multidisciplinaria en la universidad mas antigua.,https://www.vu.lt/mokslas/rss,1,Ciencia,106,Lituania,lt,False,30 +8681,ELTA Verslas Economia,Agencia de noticias de negocios y finanzas.,https://www.elta.lt/rss/verslas,4,Economía,106,Lituania,lt,False,30 +8680,Verslo Zinios Economia,Periodico de negocios y economia en Lituania.,https://www.vz.lt/rss/ekonomika,4,Economía,106,Lituania,lt,False,30 +8674,15min Politica,Sitio de noticias con enfoque en politica y sociedad.,https://www.15min.lt/rss/politika,11,Política,106,Lituania,lt,True,0 +8678,Alfa Politica,Canal de television con noticias y debates politicos.,https://www.alfa.lt/rss/politika,11,Política,106,Lituania,lt,False,30 +8676,BNS Politica,Agencia de noticias del Baltico con amplia cobertura politica.,https://www.bns.lt/rss/politika,11,Política,106,Lituania,lt,False,30 +8672,Delfi Politica,Portal de noticias mas popular con seccion de politica y actualidad.,https://www.delfi.lt/rss/politika.xml,11,Política,106,Lituania,lt,True,0 +8673,LRT Politica,Medio de servicio publico con noticias y analisis politico.,https://www.lrt.lt/rss/politika,11,Política,106,Lituania,lt,False,30 +8679,Lietuvos Respublika Politica,Periodico con analisis politico y noticias nacionales.,https://www.lr.lt/rss/politika,11,Política,106,Lituania,lt,False,30 +8671,Lietuvos Rytas Politica,Principal diario lituano con cobertura politica nacional e internacional.,https://www.lrytas.lt/politika/feed/,11,Política,106,Lituania,lt,False,30 +8677,The Baltic Times Politica,Periodico en ingles con noticias politicas de Lituania y la region.,https://www.baltictimes.com/rss/politics,11,Política,106,Lituania,en,False,30 +8675,Vyriausybe Politica,Portal del Gobierno de Lituania con comunicados y decisiones.,https://www.lrv.lt/rss/naujienos,11,Política,106,Lituania,lt,False,30 +8684,15min Technologijos Tecnologia,Portal de noticias con las ultimas novedades en tecnologia y startups.,https://www.15min.lt/rss/technologijos,14,Tecnología,106,Lituania,lt,True,0 +8687,Cyber Security LT Tecnologia,Noticias y analisis sobre ciberseguridad y proteccion de datos.,https://www.cybersecurity.lt/rss,14,Tecnología,106,Lituania,en,False,30 +8688,GameDev Lithuania Tecnologia,Industria del desarrollo de videojuegos y entretenimiento digital.,https://www.gamedev.lt/rss,14,Tecnología,106,Lituania,lt,False,30 +8685,IT Lietuva Tecnologia,Comunidad y noticias de la industria de tecnologias de la informacion.,https://www.itlietuva.lt/rss/naujienos,14,Tecnología,106,Lituania,lt,False,30 +8686,Lietuvos Telekomas Tecnologia,Principal proveedor de telecomunicaciones y servicios de internet.,https://www.telekomas.lt/rss/naujienos,14,Tecnología,106,Lituania,lt,False,30 +8705,ASTROPHYSICS Luxembourg Ciencia,Investigacion en astrofisica y ciencias del espacio.,https://www.astro.lu/rss/,1,Ciencia,107,Luxemburgo,en,False,30 +8703,CRP Henri Tudor Ciencia,Centro de investigacion en tecnologias y sistemas avanzados.,https://www.tudor.lu/rss/,1,Ciencia,107,Luxemburgo,fr,False,30 +8704,Luxembourg Science Center Ciencia,Centro de divulgacion cientifica y educacion en ciencias.,https://www.science-center.lu/rss/,1,Ciencia,107,Luxemburgo,en,False,30 +8706,Materials Science LU Ciencia,Investigacion en ciencia de materiales y nanotecnologia.,https://www.materials.lu/rss/,1,Ciencia,107,Luxemburgo,en,False,30 +8702,Universite du Luxembourg Ciencia,Investigacion cientifica multidisciplinaria en la universidad publica.,https://www.uni.lu/recherche/rss/,1,Ciencia,107,Luxemburgo,fr,False,30 +8700,Delano Economia,Revista en ingles con noticias de economia y finanzas.,https://delano.lu/rss/economy,4,Economía,107,Luxemburgo,en,False,30 +8701,Luxembourg Times Economia,Periodico en ingles con noticias economicas y financieras.,https://www.luxtimes.lu/rss/business,4,Economía,107,Luxemburgo,en,False,30 +8698,Luxembourg for Finance Economia,Agencia de promocion del sector financiero de Luxemburgo.,https://www.luxembourgforfinance.com/rss/news,4,Economía,107,Luxemburgo,en,False,30 +8699,Paperjam Economia,Revista de negocios y economia en Luxemburgo.,https://paperjam.lu/rss/economie,4,Economía,107,Luxemburgo,fr,False,30 +8695,Chronicle.lu Politica,Medio en ingles con cobertura politica y eventos nacionales.,https://chronicle.lu/rss/category/politics,11,Política,107,Luxemburgo,en,False,30 +8694,Delano Politica,Revista en ingles con noticias politicas y de negocios de Luxemburgo.,https://delano.lu/rss/politics,11,Política,107,Luxemburgo,en,False,30 +8693,Gouvernement LU Politica,Portal oficial del gobierno con comunicados y decisiones politicas.,https://gouvernement.lu/rss/actualites,11,Política,107,Luxemburgo,fr,False,30 +8691,L'essentiel Politica,Periodico en frances con noticias politicas y actualidad nacional.,https://www.lessentiel.lu/rss/politique,11,Política,107,Luxemburgo,fr,False,30 +8697,Luxembourg Times Politica,Periodico en ingles con noticias politicas y actualidad nacional.,https://www.luxtimes.lu/rss/politics,11,Política,107,Luxemburgo,en,False,30 +8689,Luxemburger Wort Politica,Principal periodico en aleman con cobertura politica de Luxemburgo.,https://www.wort.lu/rss/politik,11,Política,107,Luxemburgo,de,False,30 +8696,Paperjam Politica,Revista de negocios con analisis politico-economico de Luxemburgo.,https://paperjam.lu/rss/politique,11,Política,107,Luxemburgo,fr,False,30 +8692,RTL Today Politica,Portal de noticias en ingles con seccion de politica luxemburguesa.,https://today.rtl.lu/rss/politics,11,Política,107,Luxemburgo,en,True,0 +8690,Tageblatt Politica,Periodico en aleman con noticias politicas y analisis nacional.,https://www.tageblatt.lu/rss/politik,11,Política,107,Luxemburgo,de,False,30 +8435,Al Akhbar Science,Lebanese newspaper science news and analysis.,https://www.al-akhbar.com/rss/science,1,Ciencia,102,Líbano,ar,False,30 +8453,Al Arabiya Science Lebanon,Al Arabiya science news on Lebanon.,https://www.alarabiya.net/rss/lebanon-science,1,Ciencia,102,Líbano,ar,False,30 +8452,Al Jazeera Science Lebanon,Al Jazeera science news on Lebanon.,https://www.aljazeera.net/rss/lebanon-science,1,Ciencia,102,Líbano,ar,False,30 +8440,American University of Beirut,University research and science news.,https://www.aub.edu.lb/rss,1,Ciencia,102,Líbano,en,False,30 +8436,An Nahar Science,Lebanese daily newspaper science coverage.,https://www.annahar.com/rss/science,1,Ciencia,102,Líbano,ar,False,30 +8451,BBC News Lebanon Science,BBC science coverage of Lebanon.,https://www.bbc.com/rss/lebanon-science,1,Ciencia,102,Líbano,en,False,30 +8444,Beirut Arab University,University research in Arabic.,https://www.bau.edu.lb/rss,1,Ciencia,102,Líbano,ar,False,30 +8456,FAO Lebanon,FAO agricultural science news.,https://www.fao.org/rss/lebanon,1,Ciencia,102,Líbano,en,False,30 +8458,International Center for Agricultural Research,ICARDA agricultural research.,https://www.icarda.org/rss/lebanon,1,Ciencia,102,Líbano,en,False,30 +8437,L'Orient-Le Jour Science,Lebanese French language newspaper science.,https://www.lorientlejour.com/rss/science,1,Ciencia,102,Líbano,fr,False,30 +8448,Lebanese Agricultural Research Institute,Agricultural science research.,https://www.lari.gov.lb/rss,1,Ciencia,102,Líbano,ar,False,30 +8443,Lebanese American University,University science and research.,https://www.lau.edu.lb/rss,1,Ciencia,102,Líbano,en,False,30 +8459,Lebanese Medical Association,Medical science and research.,https://www.lebanesemedical.org/rss,1,Ciencia,102,Líbano,en,False,30 +8449,Lebanese National Council for Scientific Research,National research council.,https://www.ncsr.org.lb/rss,1,Ciencia,102,Líbano,en,False,30 +8442,Lebanese University,National university research news.,https://www.ul.edu.lb/rss,1,Ciencia,102,Líbano,ar,False,30 +8439,Lebanon News Agency Science,Official Lebanese news agency science.,https://www.nna-leb.gov.lb/rss/science,1,Ciencia,102,Líbano,ar,False,30 +8447,Ministry of Education Lebanon,Education and science policy.,https://www.mehe.gov.lb/rss,1,Ciencia,102,Líbano,ar,False,30 +8446,Ministry of Public Health Lebanon,Public health science news.,https://www.moph.gov.lb/rss,1,Ciencia,102,Líbano,ar,False,30 +8445,National Council for Scientific Research,Lebanese scientific research council.,https://www.cnrs.edu.lb/rss,1,Ciencia,102,Líbano,ar,True,0 +8450,Reuters Lebanon Science,Reuters science news on Lebanon.,https://www.reuters.com/rss/lebanon-science,1,Ciencia,102,Líbano,en,False,30 +8438,The Daily Star Lebanon Science,Lebanese English language newspaper science.,https://www.dailystar.com.lb/rss/science,1,Ciencia,102,Líbano,en,False,30 +8457,UNDP Lebanon Science,UNDP science and technology projects.,https://www.undp.org/rss/lebanon/science,1,Ciencia,102,Líbano,en,False,30 +8454,UNESCO Lebanon,UNESCO science programs in Lebanon.,https://www.unesco.org/rss/lebanon,1,Ciencia,102,Líbano,en,False,30 +8441,University of Saint Joseph,French language university science.,https://www.usj.edu.lb/rss,1,Ciencia,102,Líbano,fr,False,30 +8455,WHO Lebanon,World Health Organization science news.,https://www.who.int/rss/lebanon,1,Ciencia,102,Líbano,en,False,30 +8391,Al Akhbar Economy,Lebanese newspaper economic news and analysis.,https://www.al-akhbar.com/rss/economy,4,Economía,102,Líbano,ar,False,30 +8411,Al Arabiya Economy Lebanon,Al Arabiya economic news on Lebanon.,https://www.alarabiya.net/rss/lebanon-economy,4,Economía,102,Líbano,ar,False,30 +8410,Al Jazeera Economy Lebanon,Al Jazeera economic news on Lebanon.,https://www.aljazeera.net/rss/lebanon-economy,4,Economía,102,Líbano,ar,False,30 +8393,Al Joumhouria Economy,Lebanese newspaper economic news.,https://www.aljoumhouria.com/rss/economy,4,Economía,102,Líbano,ar,False,30 +8395,Al Liwaa Economy,Lebanese newspaper economic news.,https://www.aliwaa.com/rss/economy,4,Economía,102,Líbano,ar,False,30 +8394,Al Mustaqbal Economy,Lebanese newspaper economic coverage.,https://www.almustaqbal.com/rss/economy,4,Economía,102,Líbano,ar,False,30 +8392,An Nahar Economy,Lebanese daily newspaper economic coverage.,https://www.annahar.com/rss/economy,4,Economía,102,Líbano,ar,False,30 +8412,Asharq Al Awsat Economy Lebanon,Asharq Al Awsat Lebanon economy.,https://www.aawsat.com/rss/lebanon-economy,4,Economía,102,Líbano,ar,False,30 +8403,Association of Banks in Lebanon,Banking industry news.,https://www.abl.org.lb/rss,4,Economía,102,Líbano,ar,False,30 +8399,Banque du Liban,Banque du Liban (Central Bank of Lebanon).,https://www.bdl.gov.lb/rss,4,Economía,102,Líbano,ar,False,30 +8402,Beirut Stock Exchange,Stock market and financial news.,https://www.bse.com.lb/rss,4,Economía,102,Líbano,en,False,30 +8407,Bloomberg Lebanon,Bloomberg news on Lebanon economy.,https://www.bloomberg.com/rss/lebanon,4,Economía,102,Líbano,en,False,30 +8408,Financial Times Lebanon,FT economic analysis on Lebanon.,https://www.ft.com/rss/lebanon-economy,4,Economía,102,Líbano,en,False,30 +8414,IMF Lebanon,IMF economic reports on Lebanon.,https://www.imf.org/rss/lebanon,4,Economía,102,Líbano,en,False,30 +8396,L'Orient-Le Jour Economy,Lebanese French language newspaper economy.,https://www.lorientlejour.com/rss/economy,4,Economía,102,Líbano,fr,False,30 +8404,Lebanese Chamber of Commerce,Chamber of commerce news.,https://www.ccib.org.lb/rss,4,Economía,102,Líbano,ar,False,30 +8405,Lebanon Investment Development Authority,Investment promotion.,https://www.idal.com.lb/rss,4,Economía,102,Líbano,en,False,30 +8398,Lebanon News Agency Economy,Official Lebanese news agency economy.,https://www.nna-leb.gov.lb/rss/economy,4,Economía,102,Líbano,ar,False,30 +8401,Ministry of Economy and Trade,Lebanese Ministry of Economy and Trade.,https://www.economy.gov.lb/rss,4,Economía,102,Líbano,ar,False,30 +8400,Ministry of Finance Lebanon,Ministry of Finance news.,https://www.finance.gov.lb/rss,4,Economía,102,Líbano,ar,False,30 +8406,Reuters Lebanon Economy,Reuters economic news on Lebanon.,https://www.reuters.com/rss/lebanon-economy,4,Economía,102,Líbano,en,False,30 +8397,The Daily Star Lebanon Economy,Lebanese English language newspaper economy.,https://www.dailystar.com.lb/rss/economy,4,Economía,102,Líbano,en,False,30 +8409,The Economist Lebanon,The Economist on Lebanon economy.,https://www.economist.com/rss/lebanon,4,Economía,102,Líbano,en,False,30 +8415,UNDP Lebanon Economy,UNDP economic development news.,https://www.undp.org/rss/lebanon/economy,4,Economía,102,Líbano,en,False,30 +8413,World Bank Lebanon,World Bank reports on Lebanon.,https://www.worldbank.org/rss/lebanon,4,Economía,102,Líbano,en,False,30 +8368,AFP Lebanon,AFP news coverage of Lebanon.,https://www.afp.com/rss/lebanon,11,Política,102,Líbano,fr,False,30 +8348,Al Akhbar,Lebanese newspaper political news and analysis.,https://www.al-akhbar.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8366,Al Arabiya Lebanon,Al Arabiya coverage of Lebanon.,https://www.alarabiya.net/rss/lebanon,11,Política,102,Líbano,ar,False,30 +8372,Al Hayat Lebanon,Al Hayat coverage of Lebanon.,https://www.alhayat.com/rss/lebanon,11,Política,102,Líbano,ar,False,30 +8365,Al Jazeera Lebanon,Al Jazeera coverage of Lebanon.,https://www.aljazeera.net/rss/lebanon,11,Política,102,Líbano,ar,False,30 +8350,Al Joumhouria,Lebanese newspaper political news.,https://www.aljoumhouria.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8352,Al Liwaa,Lebanese newspaper political news.,https://www.aliwaa.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8358,Al Manar TV,Hezbollah affiliated TV channel.,https://www.almanar.com.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8351,Al Mustaqbal,Lebanese newspaper political coverage.,https://www.almustaqbal.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8349,An Nahar,Lebanese daily newspaper political coverage.,https://www.annahar.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8371,Asharq Al Awsat Lebanon,Asharq Al Awsat coverage of Lebanon.,https://www.aawsat.com/rss/lebanon,11,Política,102,Líbano,ar,False,30 +8369,BBC News Lebanon,BBC coverage of Lebanon.,https://www.bbc.com/rss/lebanon,11,Política,102,Líbano,en,False,30 +8370,France 24 Lebanon,France 24 coverage of Lebanon.,https://www.france24.com/rss/lebanon,11,Política,102,Líbano,fr,True,0 +8353,L'Orient-Le Jour,Lebanese French language newspaper politics.,https://www.lorientlejour.com/rss/politics,11,Política,102,Líbano,fr,False,30 +8357,LBCI Lebanon,Lebanese Broadcasting Corporation International.,https://www.lbci.com/rss/politics,11,Política,102,Líbano,ar,False,30 +8355,Lebanon News Agency,Official Lebanese news agency.,https://www.nna-leb.gov.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8356,MTV Lebanon,Lebanese TV channel political news.,https://www.mtv.com.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8362,Ministry of Foreign Affairs Lebanon,Lebanese foreign policy news.,https://www.mfa.gov.lb/rss,11,Política,102,Líbano,ar,False,30 +8360,NBN Lebanon,National Broadcasting Network.,https://www.nbn.com.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8363,Parliament of Lebanon,Lebanese Parliament proceedings.,https://www.lp.gov.lb/rss,11,Política,102,Líbano,ar,False,30 +8364,Presidency of Lebanon,Office of the President news.,https://www.presidency.gov.lb/rss,11,Política,102,Líbano,ar,False,30 +8361,Radio Lebanon,Official state radio.,https://www.radiolebanon.gov.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8367,Reuters Lebanon,Reuters coverage of Lebanon.,https://www.reuters.com/rss/lebanon,11,Política,102,Líbano,en,False,30 +8359,Tele Liban,Lebanese state television.,https://www.teleliban.com.lb/rss/politics,11,Política,102,Líbano,ar,False,30 +8354,The Daily Star Lebanon,Lebanese English language newspaper politics.,https://www.dailystar.com.lb/rss/politics,11,Política,102,Líbano,en,False,30 +6347,crisisgroup.org Libano,,https://www.crisisgroup.org/rss/82,11,Política,102,Líbano,en,True,0 +8460,Al Akhbar Technology,Lebanese newspaper technology news and analysis.,https://www.al-akhbar.com/rss/technology,14,Tecnología,102,Líbano,ar,False,30 +8468,Alfa Mobile,Lebanese mobile operator.,https://www.alfa.com.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8472,AltCity Lebanon,Media and technology innovation space.,https://www.altcity.me/rss,14,Tecnología,102,Líbano,en,False,30 +8461,An Nahar Technology,Lebanese daily newspaper technology coverage.,https://www.annahar.com/rss/technology,14,Tecnología,102,Líbano,ar,False,30 +8474,ArabNet Lebanon,Digital business and technology conference.,https://www.arabnet.me/rss,14,Tecnología,102,Líbano,en,True,0 +8480,Arabian Business Technology Lebanon,Technology business news.,https://www.arabianbusiness.com/rss/technology/lebanon,14,Tecnología,102,Líbano,en,False,30 +8476,BBC News Lebanon Technology,BBC technology coverage of Lebanon.,https://www.bbc.com/rss/lebanon-technology,14,Tecnología,102,Líbano,en,False,30 +8471,Berytech,Lebanese technology hub and incubator.,https://www.berytech.org/rss,14,Tecnología,102,Líbano,en,False,30 +8483,CNET Middle East Lebanon,CNET technology news on Lebanon.,https://cnet.com/rss/middle-east/lebanon,14,Tecnología,102,Líbano,en,False,30 +8481,Forbes Middle East Lebanon,Forbes tech coverage of Lebanon.,https://www.forbesmiddleeast.com/rss/lebanon,14,Tecnología,102,Líbano,en,False,30 +8462,L'Orient-Le Jour Technology,Lebanese French language newspaper technology.,https://www.lorientlejour.com/rss/technology,14,Tecnología,102,Líbano,fr,False,30 +8470,Lebanese Information Technology Syndicate,IT professional association.,https://www.lits.org.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8464,Lebanon News Agency Technology,Official Lebanese news agency technology.,https://www.nna-leb.gov.lb/rss/technology,14,Tecnología,102,Líbano,ar,False,30 +8479,MENAbytes Lebanon,Middle East tech startup news.,https://www.menabytes.com/rss/lebanon,14,Tecnología,102,Líbano,en,False,30 +8469,MTC Touch,Lebanese mobile operator.,https://www.mtctouch.com.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8465,Ministry of Telecommunications Lebanon,Lebanese telecom ministry news.,https://www.mot.gov.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8467,Ogero Telecom,Lebanese telecom operator.,https://www.ogero.gov.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8475,Reuters Lebanon Technology,Reuters technology news on Lebanon.,https://www.reuters.com/rss/lebanon-technology,14,Tecnología,102,Líbano,en,False,30 +8473,SE Factory Lebanon,Tech education and bootcamp.,https://www.sefactory.io/rss,14,Tecnología,102,Líbano,en,False,30 +8466,TRA Lebanon,Telecommunications Regulatory Authority.,https://www.tra.gov.lb/rss,14,Tecnología,102,Líbano,ar,False,30 +8477,TechCrunch Middle East Lebanon,TechCrunch coverage of Lebanon tech.,https://techcrunch.com/rss/middle-east/lebanon,14,Tecnología,102,Líbano,en,False,30 +8463,The Daily Star Lebanon Technology,Lebanese English language newspaper technology.,https://www.dailystar.com.lb/rss/technology,14,Tecnología,102,Líbano,en,False,30 +8484,UNDP Lebanon Technology,UNDP technology projects.,https://www.undp.org/rss/lebanon/technology,14,Tecnología,102,Líbano,en,False,30 +8478,Wamda Lebanon,Entrepreneurship and tech news.,https://www.wamda.com/rss/lebanon,14,Tecnología,102,Líbano,en,False,30 +8482,ZDNet Middle East Lebanon,ZDNet tech news on Lebanon.,https://zdnet.com/rss/middle-east/lebanon,14,Tecnología,102,Líbano,en,False,30 +8708,Univerzitet Sv. Kiril i Metodij Ciencia,Investigacion cientifica en la universidad mas grande de Macedonia del Norte.,https://www.ukim.edu.mk/rss/nauka,1,Ciencia,108,Macedonia del Norte,mk,False,30 +8707,Ekonomija i Biznis Economia,Periodico especializado en economia y negocios en Macedonia del Norte.,https://www.ekonomijaibiznis.mk/rss,4,Economía,108,Macedonia del Norte,mk,False,30 +8723,AI Macedonia Tecnologia,Investigacion y noticias sobre inteligencia artificial y aprendizaje automatico.,https://ai.mk/rss,14,Tecnología,108,Macedonia del Norte,en,True,0 +8720,Cyber Security MK Tecnologia,Noticias y analisis sobre ciberseguridad y proteccion de datos.,https://cybersecurity.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8719,Digitalna Makedonija Tecnologia,Iniciativa gubernamental para la transformacion digital y sociedad digital.,https://digitalnamakedonija.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8725,E-Education MK Tecnologia,Tecnologia educativa y soluciones digitales para la educacion.,https://e-education.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8722,GameDev Macedonia Tecnologia,Comunidad de desarrollo de videojuegos y entretenimiento interactivo.,https://gamedev.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8717,IT.mk Tecnologia,Portal lider de noticias sobre tecnologia e innovacion en Macedonia del Norte.,https://it.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,True,0 +8721,Makedonski Telekomunikacii Tecnologia,Principal proveedor de telecomunicaciones y servicios de internet.,https://telekom.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8718,Startup Macedonia Tecnologia,Ecosistema de startups y emprendimiento tecnologico en el pais.,https://startupmacedonia.mk/rss,14,Tecnología,108,Macedonia del Norte,en,False,30 +8724,Tehnopolis Tecnologia,Parque tecnologico y centro de innovacion en Skopje.,https://tehnopolis.mk/rss,14,Tecnología,108,Macedonia del Norte,mk,False,30 +8743,Agricultural Research Center,Farming science crop studies and agricultural innovation.,https://www.fofifa.mg/category/actualites/feed/,1,Ciencia,109,Madagascar,mg,True,0 +8745,Climate Change Research,Environmental science climate studies and sustainability.,https://www.cncec.mg/feed/,1,Ciencia,109,Madagascar,fr,False,30 +8741,Geological Survey Madagascar,Earth science mineral resources and geological studies.,https://www.pgm.mg/feed/,1,Ciencia,109,Madagascar,fr,False,30 +8739,Madagascar Biodiversity,Research on unique ecosystems flora and fauna conservation.,https://www.madagascar-biodiversity.org/feed/,1,Ciencia,109,Madagascar,en,False,30 +8736,Ministry of Higher Education,Scientific research university projects and academic news.,https://www.enseignement-superieur.gov.mg/actualites?format=feed&type=rss,1,Ciencia,109,Madagascar,fr,False,30 +8737,National Research Center,Scientific studies research publications and lab updates.,http://www.cnre.mg/actualites?format=rss,1,Ciencia,109,Madagascar,fr,False,30 +8742,Oceanographic Research,Marine science coastal studies and ocean research.,https://www.iorm.mg/actualites?format=rss,1,Ciencia,109,Madagascar,fr,False,30 +8740,Pasteur Institute Madagascar,Medical and biological research disease studies.,https://www.pasteur.mg/category/actualites-scientifiques/feed/,1,Ciencia,109,Madagascar,fr,False,30 +8744,Science Journal Madagascar,Scientific publications research papers and studies.,https://www.mjs.mg/feed/,1,Ciencia,109,Madagascar,fr,False,30 +8738,University of Antananarivo,Academic research science faculty news and conferences.,https://www.univ-antananarivo.mg/category/actualites/feed/,1,Ciencia,109,Madagascar,fr,False,30 +8732,African Development Bank Madagascar,Development finance infrastructure and economic projects.,https://www.afdb.org/en/countries/southern-africa/madagascar/rss,4,Economía,109,Madagascar,en,False,30 +8726,Central Bank of Madagascar,Monetary policy financial stability and economic indicators.,https://www.banque-centrale.mg/actualites?format=feed&type=rss,4,Economía,109,Madagascar,fr,False,30 +8735,Chamber of Commerce Antananarivo,Business news trade events and economic updates.,https://www.cci.mg/category/actualites/feed/,4,Economía,109,Madagascar,fr,False,30 +8731,IMF Madagascar,IMF reports macroeconomic analysis and Article IV consultations.,https://www.imf.org/en/Countries/MDG/rss,4,Economía,109,Madagascar,en,False,30 +8734,Investment in Madagascar,Investment opportunities business climate and regulations.,https://www.invest.gov.mg/actualites?format=feed,4,Economía,109,Madagascar,fr,False,30 +8729,Madagascar Tribune Economia,Economy business and trade news from Malagasy newspaper.,https://www.madagascar-tribune.com/spip.php?page=backend&id_rubrique=3,4,Economía,109,Madagascar,fr,True,0 +8728,Ministry of Economy and Finance,Fiscal policy budget public finance and economic planning.,https://www.economie.gov.mg/actualites?format=feed,4,Economía,109,Madagascar,fr,False,30 +8727,National Institute of Statistics,Economic statistics demography and official indicators.,https://www.instat.mg/category/actualites/feed/,4,Economía,109,Madagascar,fr,False,30 +8733,UNCTAD Madagascar,Trade and development reports and investment policy reviews.,https://unctad.org/topic/least-developed-countries/madagascar/rss,4,Economía,109,Madagascar,en,False,30 +8730,World Bank Madagascar,Development projects economic reports and data on Madagascar.,https://www.worldbank.org/en/country/madagascar/rss,4,Economía,109,Madagascar,en,False,30 +8715,Africa News Madagascar,Political news from Madagascar covered by the Pan-African agency.,https://www.africanews.com/feed/tag/madagascar/politics/,11,Política,109,Madagascar,en,False,30 +8716,AllAfrica Madagascar,Aggregated political news from Madagascar from various sources.,https://allafrica.com/tools/headlines/rdf/madagascar/politics/,11,Política,109,Madagascar,en,False,30 +8711,Madagascar Tribune Politica,Political news and analysis from a leading Malagasy newspaper.,https://www.madagascar-tribune.com/spip.php?page=backend&id_rubrique=2,11,Política,109,Madagascar,fr,True,0 +8712,Midi Madagasikara Politica,Political section of a major national daily newspaper.,https://www.midi-madagasikara.mg/category/politique/feed/,11,Política,109,Madagascar,mg,True,0 +8713,Ministry of Foreign Affairs,Official communications and foreign policy news from Madagascar.,https://www.diplomatie.gov.mg/actualites?format=feed,11,Política,109,Madagascar,fr,False,30 +8710,National Assembly Madagascar,News and legislative updates from the Parliament of Madagascar.,https://www.assemblee-nationale.mg/rss,11,Política,109,Madagascar,mg,True,0 +8709,Presidency of Madagascar,Official announcements and presidential activities from Madagascar.,http://www.presidency.gov.mg/actualites?format=feed&type=rss,11,Política,109,Madagascar,fr,False,30 +8714,UN Madagascar News,UN political and developmental news and reports about Madagascar.,https://madagascar.un.org/en/rss.xml,11,Política,109,Madagascar,en,True,0 +8754,Community Radio Network,Local community news cultural programs and social topics.,https://www.rfr.mg/actualites?format=rss,13,Sociedad,109,Madagascar,mg,False,30 +8753,Disaster Management Bureau,Natural disaster response and social resilience.,https://www.bnrgc.mg/feed/,13,Sociedad,109,Madagascar,fr,False,30 +8750,Human Rights Commission,Human rights social justice and equality reports.,https://www.cndh.mg/feed/,13,Sociedad,109,Madagascar,fr,False,30 +8748,Madagascar Tribune Sociedad,Social issues community news and cultural events.,https://www.madagascar-tribune.com/spip.php?page=backend&id_rubrique=4,13,Sociedad,109,Madagascar,fr,True,0 +8746,Ministry of Population,Social affairs family welfare and demographic news.,https://www.population.gov.mg/actualites?format=feed&type=rss,13,Sociedad,109,Madagascar,fr,False,30 +8747,National Office of Nutrition,Food security nutrition programs and social health.,https://www.onan.mg/category/actualites/feed/,13,Sociedad,109,Madagascar,mg,False,30 +8749,Social Development Fund,Community projects social assistance and development.,https://www.fds.mg/actualites?format=rss,13,Sociedad,109,Madagascar,fr,False,30 +8755,Social Enterprise Madagascar,Social entrepreneurship community initiatives and NGOs.,https://www.socent.mg/blog?format=rss,13,Sociedad,109,Madagascar,en,False,30 +8751,Women and Children Ministry,Gender equality child protection and family support.,https://www.mef.gov.mg/actualites?format=feed,13,Sociedad,109,Madagascar,mg,False,30 +8752,Youth and Sports Ministry,Youth programs sports events and social activities.,https://www.mjs.gov.mg/category/actualites/feed/,13,Sociedad,109,Madagascar,mg,True,0 +8777,Academy of Sciences Malaysia,Scientific research publications and national science advice.,https://www.akademisains.gov.my/rss/,1,Ciencia,110,Malasia,en,False,30 +8785,Climate Change Research,Environmental science climate studies and sustainability.,https://www.ccrc.utm.my/feed/,1,Ciencia,110,Malasia,en,False,30 +8780,Forest Research Institute,Forestry science biodiversity and conservation research.,https://www.frim.gov.my/feed/,1,Ciencia,110,Malasia,en,True,0 +8781,Institute of Medical Research,Medical science health research and disease studies.,https://www.imr.gov.my/feed/,1,Ciencia,110,Malasia,en,False,30 +8782,Malaysian Nuclear Agency,Nuclear science research and applications.,https://www.nuclearmalaysia.gov.my/feed/,1,Ciencia,110,Malasia,ms,False,30 +8779,Malaysian Space Agency,Space research satellite technology and astronomy.,https://www.angkasa.gov.my/feed/,1,Ciencia,110,Malasia,ms,False,30 +8784,Marine Science Institute,Marine research oceanography and aquatic studies.,https://www.inos.edu.my/feed/,1,Ciencia,110,Malasia,en,False,30 +8776,Ministry of Science Technology,National science policy research funding and innovation.,https://www.mosti.gov.my/feed/,1,Ciencia,110,Malasia,ms,True,0 +8783,Science Journal Malaysia,Scientific papers research articles and studies.,https://www.ukm.my/jsm/rss/,1,Ciencia,110,Malasia,en,False,30 +8778,University of Malaya Research,Research news scientific discoveries and academic studies.,https://www.um.edu.my/rss/research-news,1,Ciencia,110,Malasia,en,False,30 +8769,Bernama Business,Economic and business news from national news agency.,https://www.bernama.com/bm/ekonomi/rss.php,4,Economía,110,Malasia,ms,False,30 +8773,Bursa Malaysia News,Stock market announcements and corporate news.,https://www.bursamalaysia.com/rss_node,4,Economía,110,Malasia,en,False,30 +8767,Central Bank of Malaysia,Monetary policy financial stability and banking news.,https://www.bnm.gov.my/rss-feeds,4,Economía,110,Malasia,en,False,30 +8768,Department of Statistics,Economic indicators statistics and data releases.,https://www.dosm.gov.my/rss-feeds,4,Economía,110,Malasia,ms,False,30 +8774,Free Malaysia Today Economy,Economic news and financial updates.,https://www.freemalaysiatoday.com/category/business/feed/,4,Economía,110,Malasia,en,True,0 +8772,Malaysian Investment Authority,Investment news FDI and business climate.,https://www.mida.gov.my/feed/,4,Economía,110,Malasia,en,False,30 +8766,Ministry of Finance Malaysia,Fiscal policy budget reports and economic updates.,https://www.treasury.gov.my/feed/,4,Economía,110,Malasia,ms,False,30 +8771,The Edge Markets,Financial markets business news and economic trends.,https://theedgemalaysia.com/rss/news/business,4,Economía,110,Malasia,en,False,30 +8770,The Star Business,Business news market updates and economic analysis.,https://www.thestar.com.my/rss/Business/Business-News/,4,Economía,110,Malasia,en,True,0 +8775,World Bank Malaysia,Economic reports development data and projects.,https://www.worldbank.org/en/country/malaysia/rss,4,Economía,110,Malasia,en,False,30 +8759,Bernama Political News,Political news and analysis from national news agency.,https://www.bernama.com/bm/politik/rss.php,11,Política,110,Malasia,ms,False,30 +8765,Channel NewsAsia Malaysia Politics,Regional perspective on Malaysian politics.,https://www.channelnewsasia.com/api/v1/rss-feed/feeds/cna/malaysia-politics,11,Política,110,Malasia,en,False,30 +8762,Free Malaysia Today Politics,Latest political news and current affairs.,https://www.freemalaysiatoday.com/category/politics/feed/,11,Política,110,Malasia,en,True,0 +8761,Malaysiakini Politics,Independent political news and in-depth analysis.,https://www.malaysiakini.com/rss/politik,11,Política,110,Malasia,ms,False,30 +8758,Ministry of Foreign Affairs,Foreign policy international relations and diplomacy.,https://www.kln.gov.my/web/guest/berita/-/blogs/rss,11,Política,110,Malasia,en,False,30 +8757,Parliament of Malaysia,Parliamentary proceedings bills and legislative updates.,https://www.parlimen.gov.my/news/feed/,11,Política,110,Malasia,ms,False,30 +8756,Prime Ministers Department Malaysia,Official statements policies and government news.,https://www.pmo.gov.my/feed/,11,Política,110,Malasia,ms,False,30 +8760,The Star Politics,Malaysian political news and commentary from major newspaper.,https://www.thestar.com.my/rss/News/Politics/,11,Política,110,Malasia,en,True,0 +8764,The Sun Daily Politics,Political news and national affairs.,https://www.thesundaily.my/rss/News/Politics,11,Política,110,Malasia,en,False,30 +8763,Utusan Malaysia Politics,Political coverage from a leading Malay-language newspaper.,https://www.utusan.com.my/feed/berita/politik/,11,Política,110,Malasia,ms,False,30 +8795,5G Malaysia,5G network deployment and mobile technology news.,https://www.digital5g.my/feed/,14,Tecnología,110,Malasia,ms,False,30 +8793,Cybersecurity Malaysia,Cybersecurity news threats and protection updates.,https://www.cybersecurity.my/feed/,14,Tecnología,110,Malasia,en,False,30 +8791,Digital News Asia,In-depth tech news startups and digital economy.,https://www.digitalnewsasia.com/rss,14,Tecnología,110,Malasia,en,False,30 +8788,MDEC Digital News,Digital economy startup ecosystem and tech innovation.,https://www.mdec.my/news/rss,14,Tecnología,110,Malasia,en,False,30 +8794,MIMOS Berhad,Technology research R&D and innovation updates.,https://www.mimos.my/feed/,14,Tecnología,110,Malasia,en,True,0 +8786,Malaysian Digital Economy,Digital transformation tech policy and ICT development.,https://www.mdec.my/feed/,14,Tecnología,110,Malasia,en,False,30 +8792,Malaysian Tech Startup,Startup news funding and innovation ecosystem.,https://www.techstartup.my/feed/,14,Tecnología,110,Malasia,en,False,30 +8787,Ministry of Communications,Telecommunications digital infrastructure and tech news.,https://www.kkd.gov.my/feed/,14,Tecnología,110,Malasia,ms,False,30 +8789,Technologie Bernama,Tech news and updates from national news agency.,https://www.bernama.com/bm/teknologi/rss.php,14,Tecnología,110,Malasia,ms,False,30 +8790,The Star Tech,Technology news gadgets and digital trends.,https://www.thestar.com.my/rss/Tech/Tech-News/,14,Tecnología,110,Malasia,en,True,0 +8821,Environmental Affairs Dept,Environmental science conservation and climate studies.,https://www.environment.gov.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8824,Forestry Research Institute,Forest science biodiversity and conservation research.,https://www.forestry.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8818,Lilongwe University Research,Agricultural science environmental and tech research.,https://www.luanar.ac.mw/category/research/feed/,1,Ciencia,111,Malaui,en,False,30 +8819,Malawi Agricultural Research,Farming science crop studies and agricultural innovation.,https://www.dars.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8823,Malawi Science Journal,Scientific publications research papers and studies.,https://www.msj.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8820,Medical Research Council,Health research disease studies and medical science.,https://www.mrc.ac.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8825,National Aquaculture Center,Fisheries science aquaculture and aquatic research.,https://www.aquaculture.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8816,National Commission Science,Science policy research funding and innovation.,https://www.ncst.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8817,University of Malawi Research,Academic research scientific studies and discoveries.,https://www.unima.mw/category/research/feed/,1,Ciencia,111,Malaui,en,False,30 +8822,Water Resources Research,Water science hydrology and resource management.,https://www.water.gov.mw/feed/,1,Ciencia,111,Malaui,en,False,30 +8813,Agriculture Development,Farming economy crop prices and agricultural news.,https://www.agriculture.gov.mw/feed/,4,Economía,111,Malaui,en,False,30 +8815,IMF Malawi,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MWI/rss,4,Economía,111,Malaui,en,False,30 +8812,Malawi Investment Trade,Investment news business climate and trade updates.,https://www.mitc.mw/feed/,4,Economía,111,Malaui,en,False,30 +8809,Malawi News Agency Business,Economic and business news from official agency.,https://www.manaonline.gov.mw/category/business/feed/,4,Economía,111,Malaui,en,False,30 +8807,Ministry of Finance,Economic policy budget reports and fiscal updates.,https://www.finance.gov.mw/feed/,4,Economía,111,Malaui,en,False,30 +8808,National Statistical Office,Economic indicators statistics and data releases.,https://www.nsomalawi.mw/feed/,4,Economía,111,Malaui,en,False,30 +8811,Nyasa Times Business,Business and economic news coverage.,https://www.nyasatimes.com/category/business/feed/,4,Economía,111,Malaui,en,True,0 +8806,Reserve Bank of Malawi,Monetary policy financial stability and banking news.,https://www.rbm.mw/feed/,4,Economía,111,Malaui,en,False,30 +8810,The Nation Business,Business news market updates and economic analysis.,https://www.mwnation.com/category/business/feed/,4,Economía,111,Malaui,en,False,30 +8814,World Bank Malawi,Economic reports development data and projects.,https://www.worldbank.org/en/country/malawi/rss,4,Economía,111,Malaui,en,False,30 +8805,AllAfrica Malawi Politics,Political news aggregated from Malawian sources.,https://allafrica.com/tools/headlines/rdf/malawi/politics/,11,Política,111,Malaui,en,False,30 +8804,Face of Malawi Politics,Political news and government updates.,https://www.faceofmalawi.com/category/politics/feed/,11,Política,111,Malaui,en,True,0 +8799,Malawi News Agency,National political news and official statements.,https://www.manaonline.gov.mw/feed/,11,Política,111,Malaui,en,False,30 +8802,Malawi24 Politics,Political news updates and commentary.,https://www.malawi24.com/category/politics/feed/,11,Política,111,Malaui,en,True,0 +8803,Maravi Post Politics,Political analysis and national affairs coverage.,https://www.maravipost.com/category/politics/feed/,11,Política,111,Malaui,en,True,0 +8798,Ministry of Foreign Affairs,Foreign policy diplomatic news and international relations.,https://www.foreign.gov.mw/feed/,11,Política,111,Malaui,en,False,30 +8801,Nyasa Times Politics,Independent political news and current affairs.,https://www.nyasatimes.com/category/politics/feed/,11,Política,111,Malaui,en,True,0 +8797,Parliament of Malawi,Parliamentary proceedings legislative news and updates.,https://www.parliament.gov.mw/feed/,11,Política,111,Malaui,en,False,30 +8796,State House Malawi,Presidential news government announcements and policies.,https://www.statehouse.gov.mw/feed/,11,Política,111,Malaui,en,False,30 +8800,The Nation Politics,Political coverage and analysis from leading newspaper.,https://www.mwnation.com/category/politics/feed/,11,Política,111,Malaui,en,True,0 +8845,Community Radio Network,Local community news cultural programs and social topics.,https://www.crn.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8837,Department of Social Welfare,Social services community programs and assistance.,https://www.socialwelfare.gov.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8841,Human Rights Commission,Human rights reports social justice and advocacy.,https://www.mhrc.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8838,Malawi News Agency Society,Social news community events and cultural coverage.,https://www.manaonline.gov.mw/category/society/feed/,13,Sociedad,111,Malaui,en,False,30 +8844,Malawi Red Cross,Humanitarian news disaster response and community aid.,https://www.redcross.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8836,Ministry of Gender,Gender equality social welfare and community development.,https://www.gender.gov.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8843,National Youth Council,Youth programs empowerment and social initiatives.,https://www.nyc.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8840,Nyasa Times Society,Community news social affairs and public issues.,https://www.nyasatimes.com/category/society/feed/,13,Sociedad,111,Malaui,en,True,0 +8839,The Nation Society,Social issues community news and lifestyle coverage.,https://www.mwnation.com/category/society/feed/,13,Sociedad,111,Malaui,en,True,0 +8842,Youth and Sports Ministry,Youth development sports activities and programs.,https://www.youthsports.gov.mw/feed/,13,Sociedad,111,Malaui,en,False,30 +8833,Airtel Malawi Tech,Mobile technology digital services and innovations.,https://www.airtel.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8835,Cybersecurity Malawi,Cybersecurity news threats and digital protection.,https://www.csirt.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8828,Digital Malawi Project,Digital transformation e-government and innovation.,https://www.digitalmalawi.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8829,ICT Association Malawi,Technology news industry updates and events.,https://www.ictam.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8827,Malawi Communications,Telecommunications regulatory news and updates.,https://www.macra.mw/feed/,14,Tecnología,111,Malaui,en,True,0 +8834,Malawi Innovation Hub,Tech incubation innovation programs and events.,https://www.mhub.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8826,Ministry Information Technology,Digital policy ICT development and tech governance.,https://www.mitc.gov.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8830,MwTech Magazine,Technology news reviews and digital trends in Malawi.,https://www.mwtech.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8831,Startup Malawi,Tech startup news entrepreneurship and innovation.,https://www.startupmalawi.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8832,Telecom Networks Malawi,Mobile network technology and service updates.,https://www.tnm.co.mw/feed/,14,Tecnología,111,Malaui,en,False,30 +8873,Agricultural Research Center,Farming science food security and agricultural studies.,https://www.agriculture.gov.mv/research/feed/,1,Ciencia,112,Maldivas,dv,False,30 +8872,Climate Change Department,Climate research environmental science and sustainability.,https://www.climatechange.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8875,Fisheries Research Maldives,Fisheries science marine resources and aquaculture.,https://www.fisheries.gov.mv/research/feed/,1,Ciencia,112,Maldivas,en,False,30 +8870,Health Research Council,Medical research public health studies and disease science.,https://www.health.gov.mv/research/feed/,1,Ciencia,112,Maldivas,dv,False,30 +8867,Maldives Marine Research,Marine science coral reef studies and oceanography.,https://www.marine.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8869,Maldives Meteorological,Weather science climate data and meteorological research.,https://www.meteorology.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8871,Maldives Science Journal,Scientific publications research papers and studies.,https://www.msj.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8866,Ministry of Environment,Climate science environmental research and conservation.,https://www.environment.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8868,National University Maldives,Academic research scientific studies and publications.,https://www.mnu.edu.mv/category/research/feed/,1,Ciencia,112,Maldivas,en,False,30 +8874,Water Sanitation Research,Water science resource management and sanitation studies.,https://www.water.gov.mv/feed/,1,Ciencia,112,Maldivas,en,False,30 +8865,IMF Maldives,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MDV/rss,4,Economía,112,Maldivas,en,False,30 +8862,Maldives Inland Revenue,Tax news revenue updates and fiscal policies.,https://mira.gov.mv/feed/,4,Economía,112,Maldivas,dv,False,30 +8857,Maldives Monetary Authority,Monetary policy financial stability and banking news.,https://www.mma.gov.mv/feed/,4,Economía,112,Maldivas,en,False,30 +8863,Maldives Ports Authority,Trade logistics port operations and maritime economy.,https://www.mpa.gov.mv/feed/,4,Economía,112,Maldivas,en,False,30 +8856,Ministry of Finance Maldives,Fiscal policy budget reports and economic planning.,https://www.finance.gov.mv/feed/,4,Economía,112,Maldivas,dv,False,30 +8858,National Bureau Statistics,Economic indicators statistics and data releases.,https://statisticsmaldives.gov.mv/feed/,4,Economía,112,Maldivas,en,True,0 +8859,Sun Business,Economic and business news from leading newspaper.,https://en.sun.mv/category/business/feed/,4,Economía,112,Maldivas,en,False,30 +8860,The Edition Business,Business news market updates and economic analysis.,https://edition.mv/category/business/feed/,4,Economía,112,Maldivas,en,False,30 +8861,Tourism Ministry Maldives,Tourism economy industry updates and visitor statistics.,https://www.tourism.gov.mv/feed/,4,Economía,112,Maldivas,en,False,30 +8864,World Bank Maldives,Economic reports development data and projects.,https://www.worldbank.org/en/country/maldives/rss,4,Economía,112,Maldivas,en,False,30 +8854,AllAfrica Maldives Politics,Political news aggregated from Maldivian sources.,https://allafrica.com/tools/headlines/rdf/maldives/politics/,11,Política,112,Maldivas,en,False,30 +8852,Avas Politics,Political news and government updates.,https://avas.mv/category/politics/feed/,11,Política,112,Maldivas,dv,False,30 +8853,Mihaaru Politics,Political coverage and national affairs.,https://mihaaru.com/category/politics/feed/,11,Política,112,Maldivas,dv,False,30 +8848,Ministry of Foreign Affairs,Foreign policy diplomatic relations and international news.,https://www.foreign.gov.mv/feed/,11,Política,112,Maldivas,en,False,30 +8847,Peoples Majlis Maldives,Parliamentary proceedings legislative news and updates.,https://www.majlis.gov.mv/feed/,11,Política,112,Maldivas,dv,False,30 +8846,Presidency of Maldives,Presidential news government announcements and policies.,https://presidency.gov.mv/feed/,11,Política,112,Maldivas,dv,False,30 +8851,Rajje Political News,Political updates and commentary from local media.,https://rajje.mv/category/politics/feed/,11,Política,112,Maldivas,dv,False,30 +8849,Sun Politics,Political news and analysis from leading newspaper.,https://en.sun.mv/category/politics/feed/,11,Política,112,Maldivas,en,False,30 +8850,The Edition Politics,Independent political news and current affairs coverage.,https://edition.mv/category/politics/feed/,11,Política,112,Maldivas,en,False,30 +8855,UN Maldives News,UN political and developmental news about Maldives.,https://maldives.un.org/en/rss.xml,11,Política,112,Maldivas,en,True,0 +8885,Cybersecurity Maldives,Cybersecurity news threats and digital protection.,https://www.cert.gov.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8882,Dhiraagu Telecom,Mobile and fixed network technology updates.,https://www.dhiraagu.com.mv/feed/,14,Tecnología,112,Maldivas,dv,False,30 +8878,Digital Maldives Project,Digital transformation e-government and innovation.,https://www.digitalmaldives.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8877,Maldives Communications,Telecommunications regulatory news and updates.,https://www.communications.gov.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8879,Maldives ICT Association,Tech industry news innovations and events.,https://www.icta.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8884,Maldives Innovation Hub,Tech incubation innovation programs and events.,https://www.innovate.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8876,Ministry of Technology,Digital policy ICT development and tech governance.,https://www.tech.gov.mv/feed/,14,Tecnología,112,Maldivas,dv,False,30 +8883,Ooredoo Maldives,Mobile technology digital services and innovations.,https://www.ooredoo.mv/feed/,14,Tecnología,112,Maldivas,dv,False,30 +8881,Startup Maldives,Tech startup news entrepreneurship and innovation.,https://www.startupmaldives.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8880,Tech Magazine Maldives,Technology news reviews and digital trends.,https://www.techmv.mv/feed/,14,Tecnología,112,Maldivas,en,False,30 +8941,Archaeology Malta,Archaeological research historical studies and discoveries.,https://heritage.gov.mt/feed/,1,Ciencia,114,Malta,en,False,30 +8943,Energy Water Agency,Energy science water resources and sustainable tech.,https://energywateragency.gov.mt/feed/,1,Ciencia,114,Malta,en,True,0 +8939,Institute Earth Systems,Environmental science climate studies and sustainability.,https://www.um.edu.mt/iest/feed/,1,Ciencia,114,Malta,en,False,30 +8938,Malta Council Science Tech,Research funding innovation and science strategy.,https://mcst.gov.mt/feed/,1,Ciencia,114,Malta,en,True,0 +8944,Malta Science Journal,Scientific publications research papers and studies.,https://www.ums.edu.mt/sciencejournal/feed/,1,Ciencia,114,Malta,en,False,30 +8940,Marine Biology Research,Marine science oceanography and aquatic studies.,https://www.um.edu.mt/biology/marine/feed/,1,Ciencia,114,Malta,en,False,30 +8942,Medical School Research,Health research disease studies and medical science.,https://www.um.edu.mt/medicine/research/feed/,1,Ciencia,114,Malta,en,False,30 +8936,Ministry for Education Science,Science policy research funding and academic news.,https://education.gov.mt/science/feed/,1,Ciencia,114,Malta,en,False,30 +8945,Space Research Malta,Space science satellite technology and astronomy.,https://www.space.gov.mt/feed/,1,Ciencia,114,Malta,en,False,30 +8937,University of Malta Research,Academic research scientific studies and discoveries.,https://www.um.edu.mt/rss/research,1,Ciencia,114,Malta,en,False,30 +8927,Central Bank of Malta,Monetary policy financial stability and banking news.,https://www.centralbankmalta.org/rss,4,Economía,114,Malta,en,False,30 +8933,Gaming Authority Malta,Gaming industry news regulation and economic impact.,https://www.mga.org.mt/feed/,4,Economía,114,Malta,en,True,0 +8931,Malta Chamber Commerce,Business news trade events and economic updates.,https://www.maltachamber.org.mt/feed/,4,Economía,114,Malta,en,True,0 +8935,Malta Enterprise,Investment news business support and economic development.,https://www.maltaenterprise.com/feed/,4,Economía,114,Malta,en,False,30 +8932,Malta Financial Services,Financial services news regulation and sector updates.,https://www.mfsa.mt/feed/,4,Economía,114,Malta,en,True,0 +8930,MaltaToday Business,Business news market updates and economic analysis.,https://www.maltatoday.com.mt/rss/business,4,Economía,114,Malta,en,False,30 +8926,Ministry for Finance Malta,Fiscal policy budget reports and economic planning.,https://finance.gov.mt/feed/,4,Economía,114,Malta,en,False,30 +8928,National Statistics Office,Economic indicators statistics and data releases.,https://nso.gov.mt/feed/,4,Economía,114,Malta,en,False,30 +8929,Times of Malta Business,Economic and business news from leading newspaper.,https://timesofmalta.com/rss/business,4,Economía,114,Malta,en,True,0 +8934,Tourism Authority Malta,Tourism economy visitor statistics and industry news.,https://www.mta.com.mt/feed/,4,Economía,114,Malta,en,True,0 +8924,AllAfrica Malta Politics,Political news aggregated from Maltese sources.,https://allafrica.com/tools/headlines/rdf/malta/politics/,11,Política,114,Malta,en,False,30 +8925,EU Affairs Malta,Malta news related to European Union politics.,https://meae.gov.mt/en/eu/Pages/rss.aspx,11,Política,114,Malta,en,False,30 +8923,Lovin Malta Politics,Political updates and analysis for younger audience.,https://lovinmalta.com/rss/politics,11,Política,114,Malta,en,False,30 +8920,MaltaToday Politics,Independent political news and current affairs.,https://www.maltatoday.com.mt/rss/politics,11,Política,114,Malta,en,False,30 +8918,Ministry for Foreign Affairs,Foreign policy diplomatic relations and EU affairs.,https://foreign.gov.mt/feed/,11,Política,114,Malta,en,False,30 +8916,Office of the President Malta,Presidential news official announcements and state events.,https://president.gov.mt/feed/,11,Política,114,Malta,en,False,30 +8917,Parliament of Malta,Parliamentary proceedings legislative news and updates.,https://www.parlament.mt/rss/,11,Política,114,Malta,en,False,30 +8921,TVM News Politics,Political coverage from national broadcaster.,https://www.tvmnews.mt/en/rss/politics,11,Política,114,Malta,en,False,30 +8922,The Malta Independent Politics,Political news commentary and government updates.,https://www.independent.com.mt/rss/politics,11,Política,114,Malta,en,False,30 +8919,Times of Malta Politics,Political news and analysis from leading newspaper.,https://timesofmalta.com/rss/politics,11,Política,114,Malta,en,True,0 +8965,Community Malta,Local community news events and social initiatives.,https://community.gov.mt/feed/,13,Sociedad,114,Malta,en,False,30 +8963,Elderly Care Services,Senior citizen news care services and social support.,https://activeageing.gov.mt/feed/,13,Sociedad,114,Malta,en,False,30 +8961,Human Rights Malta,Human rights reports equality and social justice.,https://humanrights.gov.mt/feed/,13,Sociedad,114,Malta,en,False,30 +8964,Immigration Malta,Migration news integration and community affairs.,https://identitymalta.com/feed/,13,Sociedad,114,Malta,en,False,30 +8959,MaltaToday Society,Social issues community news and cultural coverage.,https://www.maltatoday.com.mt/rss/society,13,Sociedad,114,Malta,en,False,30 +8956,Ministry for Social Policy,Social welfare family services and community support.,https://socialpolicy.gov.mt/feed/,13,Sociedad,114,Malta,en,False,30 +8957,National Commission Persons,Disability rights inclusion and accessibility news.,https://knpd.org/feed/,13,Sociedad,114,Malta,en,False,30 +8958,Times of Malta Society,Social news community events and lifestyle.,https://timesofmalta.com/rss/society,13,Sociedad,114,Malta,en,True,0 +8960,Voluntary Organisations,Nonprofit news community initiatives and NGO updates.,https://vofunding.org.mt/feed/,13,Sociedad,114,Malta,en,False,30 +8962,Youth Agency Malta,Youth programs activities and social development.,https://agenzijazghazagh.gov.mt/feed/,13,Sociedad,114,Malta,en,True,0 +8955,AI Research Malta,Artificial intelligence research and tech innovation.,https://ai.gov.mt/feed/,14,Tecnología,114,Malta,en,False,30 +8954,Blockchain Island,Blockchain technology crypto and fintech news.,https://blockchainisland.mt/feed/,14,Tecnología,114,Malta,en,False,30 +8947,Communications Authority,Telecommunications regulatory news and updates.,https://www.mca.org.mt/feed/,14,Tecnología,114,Malta,en,False,30 +8953,Cybersecurity Malta,Cybersecurity news threats and protection updates.,https://cybersecuritymalta.org/feed/,14,Tecnología,114,Malta,en,False,30 +8946,Malta Digital Authority,Digital policy e-government and ICT strategy.,https://digitalmalta.gov.mt/feed/,14,Tecnología,114,Malta,en,True,0 +8948,Malta IT Law,Technology law digital regulation and policy news.,https://www.mitla.org.mt/feed/,14,Tecnología,114,Malta,en,False,30 +8950,MaltaToday Tech,Tech news startups and innovation coverage.,https://www.maltatoday.com.mt/rss/technology,14,Tecnología,114,Malta,en,False,30 +8952,Startup Malta,Tech startup news entrepreneurship and ecosystem.,https://www.startupmalta.org/feed/,14,Tecnología,114,Malta,en,False,30 +8951,Tech Malta Association,Tech industry news events and community updates.,https://www.techmalta.org/feed/,14,Tecnología,114,Malta,en,False,30 +8949,Times of Malta Tech,Technology news gadgets and digital trends.,https://timesofmalta.com/rss/technology,14,Tecnología,114,Malta,en,True,0 +8911,Eau et Assainissement,Water science hydrology and resource management.,https://www.eau.gouv.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8912,Energie Mali,Energy science renewable resources and technology.,https://www.energie.gov.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8910,Environnement Mali,Environmental science conservation and climate studies.,https://www.environnement.gouv.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8915,Geologie et Mines,Geological science mining resources and earth studies.,https://www.geologie.gouv.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8908,Institut National Recherche,Agricultural research farming science and innovation.,https://www.ier.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8909,Institut Pasteur Mali,Medical research disease studies and health science.,https://www.pasteur.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8906,Ministere Enseignement Superieur,Science policy research funding and academic news.,https://www.mesrs.gouv.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8914,Office Niger Agriculture,Irrigation science water management and farming.,https://www.omvs.org/feed/,1,Ciencia,113,Malí,fr,True,0 +8913,Sciences Journal Mali,Scientific publications research papers and studies.,https://www.msm.ml/feed/,1,Ciencia,113,Malí,fr,False,30 +8907,Universite Bamako Research,Academic research scientific studies and discoveries.,https://www.ub.edu.ml/category/recherche/feed/,1,Ciencia,113,Malí,fr,False,30 +8899,AMAP Economie,Economic and business news from Malian Press Agency.,https://www.amap.ml/economie/feed/,4,Economía,113,Malí,fr,False,30 +8902,Agriculture Ministry Mali,Farming economy agricultural policies and crop news.,https://www.agriculture.gov.ml/feed/,4,Economía,113,Malí,fr,False,30 +8897,Banque Centrale Mali,Monetary policy financial stability and banking news.,https://www.bceao.ml/feed/,4,Economía,113,Malí,fr,False,30 +8901,Chambre Commerce Mali,Business news trade events and economic updates.,https://www.ccim.ml/feed/,4,Economía,113,Malí,fr,False,30 +8905,IMF Mali,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/fr/Countries/MLI/rss,4,Economía,113,Malí,fr,False,30 +8898,Institut National Statistique,Economic indicators statistics and data releases.,https://www.instat-mali.org/feed/,4,Economía,113,Malí,fr,False,30 +8900,Malijet Economie,Business news market updates and economic analysis.,https://www.malijet.com/economie/feed/,4,Economía,113,Malí,fr,False,30 +8903,Mines Ministry Mali,Mining economy natural resources and sector updates.,https://www.mines.gouv.ml/feed/,4,Economía,113,Malí,fr,False,30 +8896,Ministere Economie Finances,Fiscal policy budget reports and economic planning.,https://www.finances.gouv.ml/feed/,4,Economía,113,Malí,fr,False,30 +8904,World Bank Mali,Economic reports development data and projects.,https://www.worldbank.org/fr/country/mali/rss,4,Economía,113,Malí,fr,False,30 +8889,AMAP Politique,Political news from Malian Press Agency.,https://www.amap.ml/politique/feed/,11,Política,113,Malí,fr,False,30 +8894,AllAfrica Mali Politics,Political news aggregated from Malian sources.,https://allafrica.com/tools/headlines/rdf/mali/politics/,11,Política,113,Malí,fr,False,30 +8887,Assemblee Nationale Mali,Parliamentary proceedings legislative news and updates.,https://www.assemblee-nationale.ml/feed/,11,Política,113,Malí,fr,False,30 +8891,Bamada.net Politique,Political news and current affairs.,https://bamada.net/category/politique/feed/,11,Política,113,Malí,fr,True,0 +8892,Journal du Mali Politique,Political updates and government news.,https://www.journaldumali.com/category/politique/feed/,11,Política,113,Malí,fr,True,0 +8890,Malijet Politique,Political coverage and analysis from news portal.,https://www.malijet.com/politique/feed/,11,Política,113,Malí,fr,False,30 +8888,Ministere Affaires Etrangeres,Foreign policy diplomatic relations and international news.,https://www.diplomatie.gouv.ml/feed/,11,Política,113,Malí,fr,False,30 +8886,Presidence du Mali,Presidential news government announcements and policies.,https://www.presidence.ml/feed/,11,Política,113,Malí,fr,False,30 +8893,Studio Tamani Politique,Political news from independent radio network.,https://www.studiotamani.org/category/politique/feed/,11,Política,113,Malí,fr,False,30 +8895,UN Mali News,UN political and security news about Mali.,https://mali.un.org/fr/rss.xml,11,Política,113,Malí,fr,True,0 +8993,Energy Research Center,Energy science renewable resources and technology.,https://www.masen.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8991,Geological Survey,Geological science mining resources and earth studies.,https://www.managemgroup.com/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8986,Ministry Higher Education,Science policy research funding and academic news.,https://www.enssup.gov.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8994,Morocco Science Journal,Scientific publications research papers and studies.,https://www.msj.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8987,National Center Research,Scientific research technology and innovation funding.,https://www.cnrst.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8990,National Meteorology,Weather science climate data and meteorological research.,https://www.marocmeteo.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8989,Scientific Institute,Medical research health studies and biomedical science.,https://www.insr.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8995,Space Research Morocco,Space science satellite technology and astronomy.,https://www.space.gov.ma/feed/,1,Ciencia,115,Marruecos,ar,False,30 +8988,University Mohammed V,Academic research scientific studies and discoveries.,https://www.um5.ac.ma/feed/,1,Ciencia,115,Marruecos,fr,False,30 +8992,Water Resources Research,Water science hydrology and resource management.,https://www.water.gov.ma/feed/,1,Ciencia,115,Marruecos,ar,False,30 +8977,Bank Al-Maghrib,Monetary policy financial stability and banking news.,https://www.bkam.ma/feed/,4,Economía,115,Marruecos,fr,False,30 +8982,Casablanca Stock Exchange,Stock market news financial data and corporate updates.,https://www.casablanca-bourse.com/feed/,4,Economía,115,Marruecos,fr,False,30 +8978,High Commission Planning,Economic indicators statistics and development planning.,https://www.hcp.ma/feed/,4,Economía,115,Marruecos,fr,False,30 +8985,IMF Morocco,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/fr/Countries/MAR/rss,4,Economía,115,Marruecos,fr,False,30 +8983,Investment Morocco,Investment news business climate and economic development.,https://www.invest.gov.ma/feed/,4,Economía,115,Marruecos,fr,False,30 +8980,Le Matin Economie,Business news market updates and economic analysis.,https://lematin.ma/economie/feed/,4,Economía,115,Marruecos,fr,False,30 +8979,MAP Economie,Economic and business news from Moroccan Press Agency.,https://www.map.ma/fr/economie/feed/,4,Economía,115,Marruecos,fr,False,30 +8981,Medias24 Economie,Business news financial markets and sector updates.,https://www.medias24.com/economie/feed/,4,Economía,115,Marruecos,fr,False,30 +8976,Ministry of Economy Finance,Fiscal policy budget reports and economic planning.,https://www.finances.gov.ma/feed/,4,Economía,115,Marruecos,fr,False,30 +8984,World Bank Morocco,Economic reports development data and projects.,https://www.worldbank.org/fr/country/morocco/rss,4,Economía,115,Marruecos,fr,False,30 +8973,Al Ahdath Politique,Political news from Arabic-language newspaper.,https://www.alahdath.ma/politique/feed/,11,Política,115,Marruecos,ar,False,30 +8974,AllAfrica Morocco Politics,Political news aggregated from Moroccan sources.,https://allafrica.com/tools/headlines/rdf/morocco/politics/,11,Política,115,Marruecos,fr,False,30 +8971,Hespress Politique,Political news and current affairs from news portal.,https://www.hespress.com/politique/feed/,11,Política,115,Marruecos,ar,True,0 +8970,Le Matin Politique,Political coverage and analysis from leading newspaper.,https://lematin.ma/politique/feed/,11,Política,115,Marruecos,fr,False,30 +8969,MAP Politique,Political news from Moroccan Press Agency.,https://www.map.ma/fr/politique/feed/,11,Política,115,Marruecos,fr,False,30 +8972,Medias24 Politique,Political news government updates and analysis.,https://www.medias24.com/politique/feed/,11,Política,115,Marruecos,fr,False,30 +8968,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://www.diplomatie.ma/feed/,11,Política,115,Marruecos,fr,False,30 +8967,Parliament of Morocco,Parliamentary proceedings legislative news and updates.,https://www.parlement.ma/feed/,11,Política,115,Marruecos,ar,False,30 +8966,Royal Cabinet Morocco,Royal announcements government news and official statements.,https://www.maroc.ma/feed/,11,Política,115,Marruecos,ar,False,30 +8975,UN Morocco News,UN political and developmental news about Morocco.,https://morocco.un.org/fr/rss.xml,11,Política,115,Marruecos,fr,True,0 +9005,Culture Ministry Morocco,Cultural news heritage preservation and arts.,https://www.culture.gov.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +9004,Health Ministry Morocco,Public health news disease prevention and medical care.,https://www.sante.gov.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +9000,Hespress Societe,Social news and current affairs from news portal.,https://www.hespress.com/societe/feed/,13,Sociedad,115,Marruecos,ar,True,0 +9003,Human Rights Council,Human rights reports social justice and advocacy.,https://www.cndh.ma/feed/,13,Sociedad,115,Marruecos,fr,False,30 +8999,Le Matin Societe,Social issues community news and cultural coverage.,https://lematin.ma/societe/feed/,13,Sociedad,115,Marruecos,fr,False,30 +8998,MAP Societe,Social news from Moroccan Press Agency.,https://www.map.ma/fr/societe/feed/,13,Sociedad,115,Marruecos,fr,False,30 +8996,Ministry of Solidarity,Social development family welfare and community support.,https://www.social.gov.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +8997,National Initiative Development,Human development poverty reduction and social programs.,https://www.indh.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +9002,Women Family Ministry,Gender equality women empowerment and family affairs.,https://www.femme.gov.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +9001,Youth and Sports Ministry,Youth programs sports activities and social development.,https://www.jeunesse.gov.ma/feed/,13,Sociedad,115,Marruecos,ar,False,30 +9034,Energy Efficiency Agency,Energy science renewable resources and technology.,https://www.meea.govmu.org/feed/,1,Ciencia,116,Mauricio,en,False,30 +9031,Food Agricultural Research,Food security agricultural technology and studies.,https://www.farei.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9033,Health Research Mauritius,Medical research public health and disease studies.,https://www.hsrc.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9028,Mauritius Research Council,Research funding innovation and science strategy.,https://www.mrc.org.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9035,Mauritius Science Journal,Scientific publications research papers and studies.,https://www.msj.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9029,Meteorological Services,Weather science climate data and meteorological research.,https://metservice.intnet.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9026,Ministry Education Science,Science policy research funding and academic news.,https://mes.govmu.org/feed/,1,Ciencia,116,Mauricio,en,False,30 +9032,Oceanography Institute,Marine science oceanography and aquatic studies.,https://www.moi.govmu.org/feed/,1,Ciencia,116,Mauricio,en,False,30 +9030,Sugar Research Institute,Agricultural research farming science and innovation.,https://www.msiri.mu/feed/,1,Ciencia,116,Mauricio,en,False,30 +9027,University Mauritius Research,Academic research scientific studies and discoveries.,https://www.uom.ac.mu/research/feed/,1,Ciencia,116,Mauricio,en,False,30 +9017,Bank of Mauritius,Monetary policy financial stability and banking news.,https://bom.mu/feed/,4,Economía,116,Mauricio,en,False,30 +9022,Financial Services Commission,Financial regulation investment news and sector updates.,https://www.fscmauritius.org/feed/,4,Economía,116,Mauricio,en,False,30 +9025,IMF Mauritius,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MUS/rss,4,Economía,116,Mauricio,en,False,30 +9021,LExpress Economie,Business news trade and economic coverage.,https://www.lexpress.mu/economie/feed/,4,Economía,116,Mauricio,fr,False,30 +9019,Le Defi Media Economie,Economic and business news from media group.,https://www.defimedia.info/economie/feed/,4,Economía,116,Mauricio,fr,False,30 +9020,Le Mauricien Economie,Business news market updates and financial analysis.,https://www.lemauricien.com/economie/feed/,4,Economía,116,Mauricio,fr,False,30 +9023,Mauritius Chamber Commerce,Business news trade events and economic updates.,https://www.mchamber.org/feed/,4,Economía,116,Mauricio,en,True,0 +9016,Ministry of Finance Mauritius,Fiscal policy budget reports and economic planning.,https://mof.govmu.org/feed/,4,Economía,116,Mauricio,en,False,30 +9018,Statistics Mauritius,Economic indicators statistics and data releases.,https://statsmauritius.govmu.org/feed/,4,Economía,116,Mauricio,en,False,30 +9024,World Bank Mauritius,Economic reports development data and projects.,https://www.worldbank.org/en/country/mauritius/rss,4,Economía,116,Mauricio,en,False,30 +9014,AllAfrica Mauritius Politics,Political news aggregated from Mauritian sources.,https://allafrica.com/tools/headlines/rdf/mauritius/politics/,11,Política,116,Mauricio,en,False,30 +9013,ION News Politics,Political updates and government news.,https://www.ionnews.mu/category/politics/feed/,11,Política,116,Mauricio,en,False,30 +9012,LExpress Politique,Political news and current affairs from newspaper.,https://www.lexpress.mu/politique/feed/,11,Política,116,Mauricio,fr,False,30 +9010,Le Defi Media Politique,Political coverage and analysis from media group.,https://www.defimedia.info/politique/feed/,11,Política,116,Mauricio,fr,False,30 +9011,Le Mauricien Politique,Political news from leading French-language newspaper.,https://www.lemauricien.com/politique/feed/,11,Política,116,Mauricio,fr,False,30 +9009,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://foreign.govmu.org/feed/,11,Política,116,Mauricio,en,False,30 +9007,National Assembly Mauritius,Parliamentary proceedings legislative news and updates.,https://mauritiusassembly.govmu.org/feed/,11,Política,116,Mauricio,en,False,30 +9006,President Office Mauritius,Presidential news official announcements and state events.,https://president.govmu.org/feed/,11,Política,116,Mauricio,en,False,30 +9008,Prime Minister Office,Government news policies and official statements.,https://pmo.govmu.org/feed/,11,Política,116,Mauricio,en,False,30 +9015,UN Mauritius News,UN political and developmental news about Mauritius.,https://mauritius.un.org/en/rss.xml,11,Política,116,Mauricio,en,True,0 +9045,Culture Ministry Mauritius,Cultural news heritage preservation and arts.,https://culture.govmu.org/feed/,13,Sociedad,116,Mauricio,en,False,30 +9042,Gender Equality Ministry,Gender equality women empowerment and family affairs.,https://gender.govmu.org/feed/,13,Sociedad,116,Mauricio,en,False,30 +9044,Health Ministry Mauritius,Public health news disease prevention and medical care.,https://health.govmu.org/feed/,13,Sociedad,116,Mauricio,en,False,30 +9043,Human Rights Commission,Human rights reports social justice and advocacy.,https://nhrc.mu/feed/,13,Sociedad,116,Mauricio,en,False,30 +9040,LExpress Societe,Social news and current affairs from newspaper.,https://www.lexpress.mu/societe/feed/,13,Sociedad,116,Mauricio,fr,False,30 +9038,Le Defi Media Societe,Social news from media group.,https://www.defimedia.info/societe/feed/,13,Sociedad,116,Mauricio,fr,False,30 +9039,Le Mauricien Societe,Social issues community news and cultural coverage.,https://www.lemauricien.com/societe/feed/,13,Sociedad,116,Mauricio,fr,False,30 +9036,Ministry Social Integration,Social welfare family support and community development.,https://socialsecurity.govmu.org/feed/,13,Sociedad,116,Mauricio,en,False,30 +9037,National Empowerment Foundation,Poverty reduction social programs and empowerment.,https://nef.mu/feed/,13,Sociedad,116,Mauricio,en,True,0 +9041,Youth Ministry Mauritius,Youth programs sports activities and social development.,https://youth.govmu.org/feed/,13,Sociedad,116,Mauricio,en,False,30 +9059,AMI Economie,Economic and business news from Mauritanian News Agency.,https://www.ami.mr/economie/feed/,4,Economía,117,Mauritania,ar,False,30 +9060,AlAkhbar Economie,Business news market updates and economic analysis.,https://www.alakhbar.info/economie/feed/,4,Economía,117,Mauritania,ar,False,30 +9057,Banque Centrale Mauritanie,Monetary policy financial stability and banking news.,https://www.bcm.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9062,Fisheries Ministry,Fishing economy marine resources and industry news.,https://www.peche.gov.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9065,IMF Mauritania,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/ar/Countries/MRT/rss,4,Economía,117,Mauritania,ar,False,30 +9063,Investment Mauritania,Investment news business climate and economic development.,https://www.invest.gov.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9061,Mining Ministry Mauritania,Mining economy natural resources and sector updates.,https://www.mines.gov.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9056,Ministere Economie Finances,Fiscal policy budget reports and economic planning.,https://www.finances.gov.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9058,Office National Statistique,Economic indicators statistics and data releases.,https://www.ons.mr/feed/,4,Economía,117,Mauritania,fr,False,30 +9064,World Bank Mauritania,Economic reports development data and projects.,https://www.worldbank.org/ar/country/mauritania/rss,4,Economía,117,Mauritania,ar,False,30 +9049,AMI Politique,Political news from Mauritanian News Agency.,https://www.ami.mr/politique/feed/,11,Política,117,Mauritania,ar,False,30 +9050,AlAkhbar Politique,Political coverage from Arabic news outlet.,https://www.alakhbar.info/politique/feed/,11,Política,117,Mauritania,ar,False,30 +9054,AllAfrica Mauritania Politics,Political news aggregated from Mauritanian sources.,https://allafrica.com/tools/headlines/rdf/mauritania/politics/,11,Política,117,Mauritania,fr,False,30 +9047,Assemblee Nationale,Parliamentary proceedings legislative news and updates.,https://www.assembleenationale.mr/feed/,11,Política,117,Mauritania,ar,False,30 +9052,CRIDEM Politique,Political updates and government news from portal.,https://www.cridem.org/politique/feed/,11,Política,117,Mauritania,fr,False,30 +9048,Ministere Affaires Etrangeres,Foreign policy diplomatic relations and international news.,https://www.diplomatie.gov.mr/feed/,11,Política,117,Mauritania,fr,False,30 +9046,Presidence Mauritania,Presidential news government announcements and policies.,https://www.presidence.mr/feed/,11,Política,117,Mauritania,ar,False,30 +9051,Sahara Media Politique,Political news and current affairs.,https://www.saharamedias.net/politique/feed/,11,Política,117,Mauritania,ar,False,30 +9053,Taqadoumy Politique,Political analysis and national affairs coverage.,https://www.taqadoumy.com/politique/feed/,11,Política,117,Mauritania,ar,True,0 +9055,UN Mauritania News,UN political and developmental news about Mauritania.,https://mauritania.un.org/ar/rss.xml,11,Política,117,Mauritania,ar,False,30 +9141,Agriculture Research Center,Farming science food security and agricultural studies.,http://www.fsmagriculture.fm/research/feed/,1,Ciencia,119,Micronesia,en,False,30 +9138,College Micronesia Research,Academic research scientific studies and education.,http://www.comfsm.fm/research/feed/,1,Ciencia,119,Micronesia,en,False,30 +9136,FSM Environment Agency,Environmental science conservation and climate studies.,http://www.fsmenvironment.fm/feed/,1,Ciencia,119,Micronesia,en,False,30 +9145,Geological Survey FSM,Geological science earth studies and natural resources.,http://www.fsmgeology.fm/feed/,1,Ciencia,119,Micronesia,en,False,30 +9142,Health Research FSM,Medical research public health and disease studies.,http://www.fsmhealth.fm/research/feed/,1,Ciencia,119,Micronesia,en,False,30 +9137,Micronesia Conservation,Marine science coral reef research and biodiversity.,http://www.micronesiaconservation.org/feed/,1,Ciencia,119,Micronesia,en,False,30 +9140,Micronesia Marine Research,Marine biology oceanography and aquatic studies.,http://www.micronesiamarineresearch.org/feed/,1,Ciencia,119,Micronesia,en,False,30 +9144,Micronesia Science Journal,Scientific publications research papers and studies.,http://www.msj.fm/feed/,1,Ciencia,119,Micronesia,en,False,30 +9139,Pacific Climate Change,Climate science regional research and sustainability.,http://www.pacificclimatechange.net/feed/,1,Ciencia,119,Micronesia,en,False,30 +9143,Water Resources Research,Water science hydrology and resource management.,http://www.fsmwater.fm/feed/,1,Ciencia,119,Micronesia,en,False,30 +9135,ADB Micronesia,Development finance projects and economic updates.,https://www.adb.org/countries/micronesia/rss,4,Economía,119,Micronesia,en,False,30 +9133,Agriculture FSM Economy,Farming economy agricultural development and crop news.,http://www.fsmagriculture.fm/feed/,4,Economía,119,Micronesia,en,False,30 +9127,FSM Banking Regulation,Financial regulation banking news and monetary updates.,http://www.banking.fm/feed/,4,Economía,119,Micronesia,en,False,30 +9126,FSM Department Finance,Fiscal policy budget reports and economic planning.,http://www.fsmfinance.fm/feed/,4,Economía,119,Micronesia,en,False,30 +9128,FSM Statistics Office,Economic indicators statistics and data releases.,http://www.fsmstatistics.fm/feed/,4,Economía,119,Micronesia,en,False,30 +9131,Fisheries FSM Economy,Fishing economy marine resources and industry news.,http://www.fsmfisheries.fm/feed/,4,Economía,119,Micronesia,en,False,30 +9129,Kaselehlie Press Business,Economic and business news from newspaper.,http://www.kpress.info/category/business/feed/,4,Economía,119,Micronesia,en,False,30 +9132,Tourism FSM Economy,Tourism industry visitor statistics and updates.,http://www.visit-fsm.com/feed/,4,Economía,119,Micronesia,en,False,30 +9134,World Bank Micronesia,Economic reports development data and projects.,https://www.worldbank.org/en/country/fsm/rss,4,Economía,119,Micronesia,en,False,30 +9114,AllAfrica Micronesia Politics,Political news aggregated from regional sources.,https://allafrica.com/tools/headlines/rdf/micronesia/politics/,11,Política,119,Micronesia,en,False,30 +9107,Congress Federated States,Legislative news parliamentary proceedings and bills.,http://www.cfsm.fm/feed/,11,Política,119,Micronesia,en,False,30 +9108,Department Foreign Affairs,Foreign policy diplomatic relations and international news.,http://www.foreignaffairs.gov.fm/feed/,11,Política,119,Micronesia,en,False,30 +9106,FSM Government Portal,National government news policies and official announcements.,http://www.fsmgov.org/feed/,11,Política,119,Micronesia,en,False,30 +9109,Kaselehlie Press,Political news from Micronesian newspaper.,http://www.kpress.info/feed/,11,Política,119,Micronesia,en,False,30 +9111,Marianas Variety Politics,Political news from regional newspaper.,http://www.mvariety.com/category/politics/feed/,11,Política,119,Micronesia,en,False,30 +9110,Pacific Islands Report,Political coverage and regional analysis.,http://www.pireport.org/rss/micronesia.xml,11,Política,119,Micronesia,en,False,30 +9112,Pacific News Center,Political updates and government news.,http://www.pacificnewscenter.com/category/micronesia/feed/,11,Política,119,Micronesia,en,False,30 +9115,UN Micronesia News,UN political and developmental news about Micronesia.,https://micronesia.un.org/en/rss.xml,11,Política,119,Micronesia,en,True,0 +9164,Community Radio FSM,Local community news cultural programs and social topics.,http://www.fsmradio.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9163,Culture Heritage FSM,Cultural news heritage preservation and arts.,http://www.fsmculture.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9156,FSM Department Health,Public health news disease prevention and medical care.,http://www.fsmhealth.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9157,FSM Education Department,Education news schools and academic development.,http://www.fsmeducation.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9162,Human Rights FSM,Human rights reports social justice and advocacy.,http://www.fsmhumanrights.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9158,Kaselehlie Press Society,Social news from Micronesian newspaper.,http://www.kpress.info/category/society/feed/,13,Sociedad,119,Micronesia,en,False,30 +9159,Pacific Islands Society,Social issues community news and cultural coverage.,http://www.pacificislandssociety.com/feed/,13,Sociedad,119,Micronesia,en,False,30 +9165,Social Services FSM,Social welfare community support and assistance.,http://www.fsmsocialservices.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9161,Women Affairs FSM,Gender equality women empowerment and family affairs.,http://www.fsmwomen.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9160,Youth Development FSM,Youth programs activities and social development.,http://www.fsmyouth.fm/feed/,13,Sociedad,119,Micronesia,en,False,30 +9153,Cybersecurity FSM,Cybersecurity news threats and digital protection.,http://www.fsmcybersecurity.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9154,Digital Media FSM,Digital media news online platforms and content.,http://www.digitalmedia.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9146,FSM Telecom Regulation,Telecommunications regulatory news and updates.,http://www.fsmtelecom.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9150,FSM Telecommunications,Mobile and fixed network technology updates.,http://www.fsm.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9151,Internet Society FSM,Internet development connectivity and digital access.,http://www.internetsociety.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9148,Kaselehlie Press Tech,Tech news from Micronesian newspaper.,http://www.kpress.info/category/technology/feed/,14,Tecnología,119,Micronesia,en,False,30 +9147,Micronesia IT Office,Digital policy ICT development and e-government.,http://www.fsmit.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9155,Micronesia Tech Hub,Tech incubation innovation programs and events.,http://www.techhub.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9149,Pacific Islands Tech,Tech news and digital trends in Pacific region.,http://www.pacificislandstech.com/feed/,14,Tecnología,119,Micronesia,en,False,30 +9152,Startup Micronesia,Tech startup news entrepreneurship and innovation.,http://www.startupmicronesia.fm/feed/,14,Tecnología,119,Micronesia,en,False,30 +9187,Academia de Stiinte,Scientific research publications and national science advice.,https://www.asm.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9195,Institutul de Agricultura,Agricultural research farming science and innovation.,https://www.agricultura.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9192,Institutul de Chimie,Chemistry research materials science and innovation.,https://www.chem.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9190,Institutul de Ecologie,Environmental science conservation and climate studies.,https://www.ecologie.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9191,Institutul de Fizica,Physics research technology and scientific studies.,https://www.phys.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9189,Institutul de Genetica,Medical research genetics and health science.,https://www.genetica.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9193,Institutul de Matematica,Mathematics research scientific computing and studies.,https://www.math.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9186,Ministerul Educatiei,Science policy research funding and academic news.,https://mecc.gov.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9194,Revista Stiintifica,Scientific publications research papers and studies.,https://www.revista.md/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9188,Universitatea de Stat,Academic research scientific studies and discoveries.,https://www.usm.md/research/feed/,1,Ciencia,120,Moldavia,ro,False,30 +9182,Agentia Investitii,Investment news business climate and economic development.,https://invest.gov.md/feed/,4,Economía,120,Moldavia,ro,False,30 +9177,Banca Nationala Moldovei,Monetary policy financial stability and banking news.,https://www.bnm.md/feed/,4,Economía,120,Moldavia,ro,False,30 +9178,Biroul National Statistica,Economic indicators statistics and data releases.,https://statistica.gov.md/feed/,4,Economía,120,Moldavia,ro,True,0 +9183,Camera de Comert,Business news trade events and economic updates.,https://www.chamber.md/feed/,4,Economía,120,Moldavia,ro,False,30 +9181,Deschide MD Economic,Business news trade and investment updates.,https://deschide.md/economic/feed/,4,Economía,120,Moldavia,ro,False,30 +9185,IMF Moldova,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MDA/rss,4,Economía,120,Moldavia,en,False,30 +9176,Ministerul Economiei,Fiscal policy budget reports and economic planning.,https://economie.gov.md/feed/,4,Economía,120,Moldavia,ro,False,30 +9179,Moldpres Economic,Economic and business news from Moldovan Press Agency.,https://www.moldpres.md/economic/feed/,4,Economía,120,Moldavia,ro,False,30 +9180,Unimedia Economic,Business news market updates and economic analysis.,https://unimedia.info/economic/feed/,4,Economía,120,Moldavia,ro,False,30 +9184,World Bank Moldova,Economic reports development data and projects.,https://www.worldbank.org/en/country/moldova/rss,4,Economía,120,Moldavia,en,False,30 +9224,Cultura Moldova,Cultural news heritage preservation and arts.,https://culture.gov.md/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9220,Deschide MD Social,Social news and current affairs.,https://deschide.md/social/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9225,Educatie Moldova,Education news schools and academic development.,https://educatie.gov.md/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9222,Egalitate de Gen,Gender equality women empowerment and family affairs.,https://egalitate.gov.md/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9223,Institutul Drepturilor,Human rights reports social justice and advocacy.,https://www.drepturileomului.md/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9216,Ministerul Muncii,Social protection labor market and welfare news.,https://mmpsf.gov.md/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9217,Ministerul Sanatatii,Public health news disease prevention and medical care.,https://ms.gov.md/feed/,13,Sociedad,120,Moldavia,ro,True,0 +9218,Moldpres Social,Social news from Moldovan Press Agency.,https://www.moldpres.md/social/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9221,Tineret Moldova,Youth programs activities and social development.,https://tineret.gov.md/feed/,13,Sociedad,120,Moldavia,ro,True,0 +9219,Unimedia Social,Social issues community news and cultural coverage.,https://unimedia.info/social/feed/,13,Sociedad,120,Moldavia,ro,False,30 +9206,Agentia pentru Digitizare,Digital policy e-government and ICT strategy.,https://digitization.gov.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9207,Autoritatea Reglementare,Telecommunications regulatory news and updates.,https://www.regulator.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9210,Deschide MD Tech,Tech news startups and digital economy.,https://deschide.md/tech/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9211,IT Moldova Association,Tech industry news events and community updates.,https://www.ita.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9213,Moldcell Telecom,Mobile network technology and service updates.,https://www.moldcell.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9208,Moldpres Tehnologii,Tech news from Moldovan Press Agency.,https://www.moldpres.md/tehnologii/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9215,Moldtelecom News,Telecom news broadband and corporate updates.,https://www.moldtelecom.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9214,Orange Moldova Tech,Mobile technology digital services and innovations.,https://www.orange.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9212,Startup Moldova,Tech startup news entrepreneurship and ecosystem.,https://www.startupmoldova.md/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9209,Unimedia Tehnologii,Technology news digital trends and innovations.,https://unimedia.info/tehnologii/feed/,14,Tecnología,120,Moldavia,ro,False,30 +9325,Climate Change Research,Climate science environmental studies and sustainability.,https://www.climate.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9319,Institute of Astronomy,Astronomy research space science and technology.,https://www.astronomy.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9321,Institute of Biology,Biology research biodiversity and environmental science.,https://www.biology.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9323,Institute of Engineering,Engineering research technology and innovation.,https://www.engineering.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9320,Institute of Geology,Geological science mining resources and earth studies.,https://www.geology.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9322,Institute of Medicine,Medical research public health and disease studies.,https://www.medicine.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9316,Ministry of Education Science,Science policy research funding and academic news.,https://www.mes.gov.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9324,Mongolia Science Journal,Scientific publications research papers and studies.,https://www.msj.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9317,Mongolian Academy Sciences,Scientific research publications and national science advice.,https://www.mas.ac.mn/feed/,1,Ciencia,122,Mongolia,mn,False,30 +9318,National University Mongolia,Academic research scientific studies and discoveries.,https://www.num.edu.mn/research/feed/,1,Ciencia,122,Mongolia,mn,True,0 +9307,Bank of Mongolia,Monetary policy financial stability and banking news.,https://www.mongolbank.mn/feed/,4,Economía,122,Mongolia,mn,False,30 +9315,IMF Mongolia,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MNG/rss,4,Economía,122,Mongolia,en,False,30 +9313,Investment Mongolia,Investment news business climate and economic development.,https://www.investmongolia.gov.mn/feed/,4,Economía,122,Mongolia,en,True,0 +9312,Mining Ministry Mongolia,Mining economy natural resources and sector updates.,https://www.mram.mn/feed/,4,Economía,122,Mongolia,mn,False,30 +9306,Ministry of Finance Mongolia,Fiscal policy budget reports and economic planning.,https://www.mof.gov.mn/feed/,4,Economía,122,Mongolia,mn,False,30 +9309,Montsame Economy,Economic and business news from Mongolian News Agency.,https://www.montsame.mn/economy/feed/,4,Economía,122,Mongolia,mn,False,30 +9308,National Statistics Office,Economic indicators statistics and data releases.,https://www.nso.mn/feed/,4,Economía,122,Mongolia,mn,False,30 +9310,News MN Economy,Business news market updates and economic analysis.,https://news.mn/economy/feed/,4,Economía,122,Mongolia,mn,False,30 +9311,UB Post Business,Business news trade and investment updates.,https://theubpost.mn/business/feed/,4,Economía,122,Mongolia,en,False,30 +9314,World Bank Mongolia,Economic reports development data and projects.,https://www.worldbank.org/en/country/mongolia/rss,4,Economía,122,Mongolia,en,False,30 +9304,AllAfrica Mongolia Politics,Political news aggregated from Mongolian sources.,https://allafrica.com/tools/headlines/rdf/mongolia/politics/,11,Política,122,Mongolia,en,False,30 +9303,GoGo Mongolia Politics,Political updates and government news.,https://gogo.mn/politics/feed/,11,Política,122,Mongolia,mn,False,30 +9299,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://www.mfa.gov.mn/feed/,11,Política,122,Mongolia,mn,False,30 +9300,Montsame Politics,Political news from Mongolian News Agency.,https://www.montsame.mn/politics/feed/,11,Política,122,Mongolia,mn,False,30 +9301,News MN Politics,Political coverage and analysis from news portal.,https://news.mn/politics/feed/,11,Política,122,Mongolia,mn,False,30 +9296,President of Mongolia,Presidential news official announcements and state events.,https://president.mn/feed/,11,Política,122,Mongolia,mn,True,0 +9298,Prime Minister Office,Government news policies and official statements.,https://www.pmis.gov.mn/feed/,11,Política,122,Mongolia,mn,False,30 +9297,State Great Khural,Parliamentary proceedings legislative news and updates.,https://www.parliament.mn/feed/,11,Política,122,Mongolia,mn,False,30 +9302,UB Post Politics,Political news from English-language newspaper.,https://theubpost.mn/politics/feed/,11,Política,122,Mongolia,en,False,30 +9305,UN Mongolia News,UN political and developmental news about Mongolia.,https://mongolia.un.org/en/rss.xml,11,Política,122,Mongolia,en,True,0 +9372,Biological Institute,Biology research biodiversity and environmental science.,https://www.ucg.ac.me/biologija/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9375,Climate Change Research,Climate science environmental studies and sustainability.,https://www.klima.gov.me/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9370,Institute of Geology,Geological science mining resources and earth studies.,https://www.ucg.ac.me/geologija/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9369,Institute of Marine Biology,Marine science oceanography and aquatic studies.,https://www.ucg.ac.me/mbi/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9371,Institute of Public Health,Medical research public health and disease studies.,https://www.ijzcg.me/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9366,Ministry of Education Science,Science policy research funding and academic news.,https://www.gov.me/obrazovanje/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9367,Montenegrin Academy Sciences,Scientific research publications and national science advice.,https://www.canu.ac.me/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9374,Montenegro Science Journal,Scientific publications research papers and studies.,https://www.msj.me/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9373,Physics Institute,Physics research technology and scientific studies.,https://www.ucg.ac.me/fizika/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9368,University of Montenegro,Academic research scientific studies and discoveries.,https://www.ucg.ac.me/istrazivanje/feed/,1,Ciencia,123,Montenegro,sr,False,30 +9361,CdM Economy,Business news trade and investment updates.,https://www.cdm.me/ekonomija/feed/,4,Economía,123,Montenegro,sr,True,0 +9357,Central Bank Montenegro,Monetary policy financial stability and banking news.,https://www.cbcg.me/feed/,4,Economía,123,Montenegro,sr,False,30 +9365,IMF Montenegro,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/MNE/rss,4,Economía,123,Montenegro,en,False,30 +9363,Investment Agency Montenegro,Investment news business climate and economic development.,https://www.investmontenegro.me/feed/,4,Economía,123,Montenegro,en,False,30 +9356,Ministry of Finance Montenegro,Fiscal policy budget reports and economic planning.,https://www.gov.me/finansije/feed/,4,Economía,123,Montenegro,sr,False,30 +9358,Monstat Statistics,Economic indicators statistics and data releases.,https://www.monstat.org/feed/,4,Economía,123,Montenegro,sr,False,30 +9359,RTCG Economy,Economic and business news from national broadcaster.,https://www.rtcg.me/ekonomija/feed/,4,Economía,123,Montenegro,sr,False,30 +9362,Tourism Ministry Montenegro,Tourism economy visitor statistics and industry news.,https://www.gov.me/turizam/feed/,4,Economía,123,Montenegro,sr,False,30 +9360,Vijesti Economy,Business news market updates and economic analysis.,https://www.vijesti.me/ekonomija/feed/,4,Economía,123,Montenegro,sr,False,30 +9364,World Bank Montenegro,Economic reports development data and projects.,https://www.worldbank.org/en/country/montenegro/rss,4,Economía,123,Montenegro,en,False,30 +9354,AllAfrica Montenegro Politics,Political news aggregated from Montenegrin sources.,https://allafrica.com/tools/headlines/rdf/montenegro/politics/,11,Política,123,Montenegro,en,False,30 +9352,CdM Politics,Political news from independent news portal.,https://www.cdm.me/politika/feed/,11,Política,123,Montenegro,sr,True,0 +9353,Dan Politics,Political updates and government news from newspaper.,https://www.dan.co.me/politika/feed/,11,Política,123,Montenegro,sr,False,30 +9348,Government of Montenegro,Government news policies and official statements.,https://www.gov.me/feed/,11,Política,123,Montenegro,sr,False,30 +9349,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://www.mfa.gov.me/feed/,11,Política,123,Montenegro,sr,False,30 +9347,Parliament of Montenegro,Parliamentary proceedings legislative news and updates.,https://www.skupstina.me/feed/,11,Política,123,Montenegro,sr,False,30 +9346,Presidency of Montenegro,Presidential news official announcements and state events.,https://www.predsjednik.me/feed/,11,Política,123,Montenegro,sr,False,30 +9350,RTCG Politics,Political news from national broadcaster.,https://www.rtcg.me/politika/feed/,11,Política,123,Montenegro,sr,False,30 +9355,UN Montenegro News,UN political and developmental news about Montenegro.,https://montenegro.un.org/en/rss.xml,11,Política,123,Montenegro,en,True,0 +9351,Vijesti Politics,Political coverage and analysis from leading newspaper.,https://www.vijesti.me/politika/feed/,11,Política,123,Montenegro,sr,False,30 +9423,Center Biotecnologia,Biotechnology research medical innovation and science.,https://www.cbiotech.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9425,Instituto Ambiente,Environmental science conservation and climate studies.,https://www.moa.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9421,Instituto Geologico,Geological science mining resources and earth studies.,https://www.igeo.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9418,Instituto Investigacao Agraria,Agricultural research farming science and innovation.,https://www.iiam.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9422,Instituto Meteorologico,Weather science climate data and meteorological research.,https://www.inam.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9419,Instituto Nacional Saude,Medical research public health and disease studies.,https://www.ins.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,True,0 +9420,Instituto Oceanografico,Marine science oceanography and aquatic studies.,https://www.inom.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9424,Revista Ciencia Mocambique,Scientific publications research papers and studies.,https://www.rcm.gov.mz/feed/,1,Ciencia,124,Mozambique,pt,False,30 +9417,Universidade Eduardo Mondlane,Academic research scientific studies and discoveries.,https://www.uem.mz/investigacao/feed/,1,Ciencia,124,Mozambique,pt,True,0 +9400,AIM Politica,Political news from Mozambique News Agency.,https://www.aim.gov.mz/politica/feed/,11,Política,124,Mozambique,pt,False,30 +9404,AllAfrica Mozambique Politics,Political news aggregated from Mozambican sources.,https://allafrica.com/tools/headlines/rdf/mozambique/politics/,11,Política,124,Mozambique,en,False,30 +9397,Assembleia da Republica,Parliamentary proceedings legislative news and updates.,https://www.parlamento.gov.mz/feed/,11,Política,124,Mozambique,pt,False,30 +9403,Canal de Mozambique,Political updates and government news from media.,https://www.canal.co.mz/politica/feed/,11,Política,124,Mozambique,pt,True,0 +9398,Governo de Mozambique,Government news policies and official statements.,https://www.gov.mz/feed/,11,Política,124,Mozambique,pt,False,30 +9399,Ministerio Negocios Estrangeiros,Foreign policy diplomatic relations and international news.,https://www.minec.gov.mz/feed/,11,Política,124,Mozambique,pt,False,30 +9402,Noticias Politica,Political news from leading daily newspaper.,https://noticias.co.mz/politica/feed/,11,Política,124,Mozambique,pt,False,30 +9401,O Pais Politica,Political coverage and analysis from newspaper.,https://opais.co.mz/politica/feed/,11,Política,124,Mozambique,pt,False,30 +9396,Presidencia Mozambique,Presidential news government announcements and policies.,https://www.portaldogoverno.gov.mz/feed/,11,Política,124,Mozambique,pt,False,30 +9405,UN Mozambique News,UN political and developmental news about Mozambique.,https://mozambique.un.org/pt/rss.xml,11,Política,124,Mozambique,pt,True,0 +9447,AIM Sociedade,Social news from Mozambique News Agency.,https://www.aim.gov.mz/sociedade/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9452,Comissao Direitos Humanos,Human rights reports social justice and advocacy.,https://www.cdh.org.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9445,Ministerio Acao Social,Social welfare family support and community services.,https://www.mas.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9416,Ministerio Cultura Turismo,Cultural news heritage preservation and arts.,https://www.mct.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9453,Ministerio Educacao Desenvolvimento,Education news schools and academic development.,https://www.mined.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9451,Ministerio Genero Crianca,Gender equality women empowerment and family affairs.,https://www.mgc.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9450,Ministerio Juventude Desporto,Youth programs activities and social development.,https://www.mjd.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9446,Ministerio Saude,Public health news disease prevention and medical care.,https://www.misau.gov.mz/feed/,13,Sociedad,124,Mozambique,pt,True,0 +9449,Noticias Sociedade,Social news and current affairs from newspaper.,https://noticias.co.mz/sociedade/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9448,O Pais Sociedade,Social issues community news and cultural coverage.,https://opais.co.mz/sociedade/feed/,13,Sociedad,124,Mozambique,pt,False,30 +9428,AIM Tecnologia,Tech news from Mozambique News Agency.,https://www.aim.gov.mz/tecnologia/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9431,Associacao Tecnologia Informacao,Tech industry news events and community updates.,https://www.ati.co.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9427,Autoridade Reguladora Comunicacoes,Telecommunications regulatory news and updates.,https://www.incm.gov.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9435,Cybersecurity Mozambique,Security news digital threats and protection.,https://www.cert.gov.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9426,Instituto Tecnologias Informacao,Digital policy ICT strategy and e-government.,https://www.icit.gov.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9430,Mozambique Tech Hub,Tech news startups and digital economy.,https://www.techhub.co.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9429,O Pais Tecnologia,Technology news digital trends and innovations.,https://opais.co.mz/tecnologia/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9432,Startup Mozambique,Tech startup news entrepreneurship and ecosystem.,https://www.startupmoz.co.mz/feed/,14,Tecnología,124,Mozambique,en,False,30 +9433,TMCel Telecom,Mobile network technology and service updates.,https://www.tmcel.co.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9434,Vodacom Mozambique,Mobile technology digital services and innovations.,https://www.vodacom.co.mz/feed/,14,Tecnología,124,Mozambique,pt,False,30 +9077,Banco de Mexico,Monetary policy financial stability and banking news.,https://www.banxico.org.mx/rss/rss.html,4,Economía,118,México,es,False,30 +9083,Bolsa Mexicana Valores,Stock market news financial data and corporate updates.,https://www.bmv.com.mx/rss,4,Economía,118,México,es,False,30 +9080,El Universal Economia,Business news market updates and economic analysis.,https://www.eluniversal.com.mx/rss/economia.xml,4,Economía,118,México,es,False,30 +9082,Excelsior Economia,Economic news trade and investment updates.,https://www.excelsior.com.mx/rss/economia,4,Economía,118,México,es,False,30 +9078,INEGI Statistics,Economic indicators statistics and data releases.,https://www.inegi.org.mx/rss/rss.html,4,Economía,118,México,es,True,0 +9079,Notimex Economia,Economic and business news from Mexican news agency.,https://www.notimex.gob.mx/economia/feed/,4,Economía,118,México,es,False,30 +9084,ProMexico Investment,Investment news business climate and economic development.,https://www.gob.mx/promexico/feed/,4,Economía,118,México,es,False,30 +9081,Reforma Economia,Business news financial markets and sector updates.,https://www.reforma.com/rss/economia.xml,4,Economía,118,México,es,False,30 +9076,Secretaria de Hacienda,Fiscal policy budget reports and economic planning.,https://www.gob.mx/hacienda/feed/,4,Economía,118,México,es,True,0 +9085,World Bank Mexico,Economic reports development data and projects.,https://www.worldbank.org/es/country/mexico/rss,4,Economía,118,México,es,False,30 +9105,CNDH Mexico,Human rights reports social justice and advocacy.,https://www.cndh.org.mx/feed/,13,Sociedad,118,México,es,False,30 +9099,El Universal Sociedad,Social issues community news and cultural coverage.,https://www.eluniversal.com.mx/rss/sociedad.xml,13,Sociedad,118,México,es,False,30 +9101,Excelsior Sociedad,Social updates and community news.,https://www.excelsior.com.mx/rss/sociedad,13,Sociedad,118,México,es,False,30 +9102,IMSS Social Security,Public health social security and medical care news.,https://www.imss.gob.mx/feed/,13,Sociedad,118,México,es,False,30 +9097,INEGI Sociedad,Social statistics demographics and quality of life data.,https://www.inegi.org.mx/rss/sociedad.xml,13,Sociedad,118,México,es,True,0 +9103,INJUVE Mexico,Youth programs activities and social development.,https://www.gob.mx/injuve/feed/,13,Sociedad,118,México,es,False,30 +9104,INMUJERES Mexico,Gender equality women empowerment and family affairs.,https://www.gob.mx/inmujeres/feed/,13,Sociedad,118,México,es,False,30 +9098,Notimex Sociedad,Social news from Mexican news agency.,https://www.notimex.gob.mx/sociedad/feed/,13,Sociedad,118,México,es,False,30 +9100,Reforma Sociedad,Social news and current affairs.,https://www.reforma.com/rss/sociedad.xml,13,Sociedad,118,México,es,False,30 +9096,Secretaria Bienestar,Social development poverty reduction and community support.,https://www.gob.mx/bienestar/feed/,13,Sociedad,118,México,es,True,0 +9240,Chambre de Commerce,Business news trade events and economic updates.,https://www.monaco-chamber.mc/feed/,4,Economía,121,Mónaco,fr,False,30 +9236,Departement Finances,Fiscal policy budget reports and economic planning.,https://www.gouv.mc/finances/feed/,4,Economía,121,Mónaco,fr,False,30 +9241,Finance Monaco,Financial services banking and investment news.,https://www.financemonaco.com/feed/,4,Economía,121,Mónaco,en,True,0 +9243,Immobilier Monaco,Real estate market property news and economic impact.,https://www.immobilier.mc/feed/,4,Economía,121,Mónaco,fr,False,30 +9237,Institut Monagasque,Economic statistics data analysis and indicators.,https://www.imsee.mc/feed/,4,Economía,121,Mónaco,fr,False,30 +9244,Monaco Business News,Business climate corporate news and economic development.,https://www.monacobusinessnews.com/feed/,4,Economía,121,Mónaco,en,True,0 +9239,Monaco Info Economie,Business news market updates and financial analysis.,https://www.monacoinfo.mc/economie/feed/,4,Economía,121,Mónaco,fr,False,30 +9238,Monaco Matin Economie,Economic and business news from newspaper.,https://www.monacomatin.mc/economie/feed/,4,Economía,121,Mónaco,fr,False,30 +9242,Tourisme Monaco,Tourism economy visitor statistics and industry news.,https://www.visitmonaco.com/feed/,4,Economía,121,Mónaco,fr,False,30 +9245,Yacht Industry Monaco,Maritime economy yachting industry and business news.,https://www.yachtindustry.mc/feed/,4,Economía,121,Mónaco,en,False,30 +9234,AllAfrica Monaco Politics,Political news aggregated from Monegasque sources.,https://allafrica.com/tools/headlines/rdf/monaco/politics/,11,Política,121,Mónaco,en,False,30 +9227,Conseil National,Parliamentary proceedings legislative news and updates.,https://www.conseil-national.mc/feed/,11,Política,121,Mónaco,fr,False,30 +9228,Gouvernement Monaco,Government news policies and official statements.,https://www.gouv.mc/feed/,11,Política,121,Mónaco,fr,False,30 +9233,LObservateur Monaco,Political updates and government news.,https://www.lobservateur.mc/politique/feed/,11,Política,121,Mónaco,fr,False,30 +9231,Monaco Info Politique,Political coverage from public broadcaster.,https://www.monacoinfo.mc/politique/feed/,11,Política,121,Mónaco,fr,False,30 +9230,Monaco Matin Politique,Political news from leading Monegasque newspaper.,https://www.monacomatin.mc/politique/feed/,11,Política,121,Mónaco,fr,False,30 +9232,Monaco Tribune Politique,Political news and current affairs.,https://www.monacotribune.com/politique/feed/,11,Política,121,Mónaco,fr,False,30 +9226,Palais Princier Monaco,Royal news official announcements and state events.,https://www.palais.mc/feed/,11,Política,121,Mónaco,fr,False,30 +9229,Relations Exterieures,Foreign policy diplomatic relations and international news.,https://www.gouv.mc/exterieur/feed/,11,Política,121,Mónaco,fr,False,30 +9235,UN Monaco News,UN political news about Monaco.,https://monaco.un.org/fr/rss.xml,11,Política,121,Mónaco,fr,False,30 +9274,AI Monaco,Artificial intelligence research and applications.,https://www.aimonaco.mc/feed/,14,Tecnología,121,Mónaco,en,False,30 +9273,Blockchain Monaco,Blockchain technology crypto and fintech news.,https://www.blockchainmonaco.mc/feed/,14,Tecnología,121,Mónaco,en,False,30 +9272,Cybersecurity Monaco,Security news digital threats and protection.,https://www.cybersecuritymonaco.mc/feed/,14,Tecnología,121,Mónaco,en,False,30 +9266,Digital Monaco,Digital transformation e-government and smart city.,https://www.digitalmonaco.mc/feed/,14,Tecnología,121,Mónaco,fr,False,30 +9271,Monaco IT Association,Tech industry news events and community updates.,https://www.itmonaco.mc/feed/,14,Tecnología,121,Mónaco,fr,False,30 +9269,Monaco Info Technologie,Technology updates digital trends and innovation.,https://www.monacoinfo.mc/technologie/feed/,14,Tecnología,121,Mónaco,fr,False,30 +9268,Monaco Matin Technologie,Tech news from Monegasque newspaper.,https://www.monacomatin.mc/technologie/feed/,14,Tecnología,121,Mónaco,fr,False,30 +9267,Monaco Telecom,Telecom services broadband and technology news.,https://www.monaco-telecom.mc/feed/,14,Tecnología,121,Mónaco,fr,True,0 +9275,Smart City Monaco,Smart technology urban innovation and IoT projects.,https://www.smartcitymonaco.mc/feed/,14,Tecnología,121,Mónaco,fr,False,30 +9270,Startup Monaco,Tech startup news entrepreneurship and ecosystem.,https://www.startupmonaco.mc/feed/,14,Tecnología,121,Mónaco,en,False,30 +9491,Agriculture Research Council,Farming science food security and agricultural studies.,https://www.arc.org.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9489,Desert Research Foundation,Environmental science desert studies and sustainability.,https://www.drfn.org.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9493,Energy Research Center,Energy science renewable resources and technology.,https://www.erc.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9490,Marine Research Institute,Marine science oceanography and aquatic studies.,https://www.mri.gov.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9484,Ministry Higher Education,Science policy research funding and academic news.,https://www.mhet.gov.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9492,Namibia Science Journal,Scientific publications research papers and studies.,https://www.nsj.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9487,Namibia Scientific Society,Scientific research publications and community outreach.,https://www.scientificsociety.org.na/feed/,1,Ciencia,125,Namibia,en,False,30 +9486,Namibia University Science,Science and technology research and innovation.,https://www.nust.na/research/feed/,1,Ciencia,125,Namibia,en,False,30 +9488,National Museum Namibia,Natural history research biodiversity and conservation.,https://www.namibiamuseum.org/feed/,1,Ciencia,125,Namibia,en,False,30 +9485,University of Namibia Research,Academic research scientific studies and discoveries.,https://www.unam.edu.na/research/feed/,1,Ciencia,125,Namibia,en,True,0 +9465,Bank of Namibia,Monetary policy financial stability and banking news.,https://www.bon.com.na/feed/,4,Economía,125,Namibia,en,False,30 +9470,Chamber of Commerce Namibia,Business news trade events and economic updates.,https://www.ncci.org.na/feed/,4,Economía,125,Namibia,en,False,30 +9473,IMF Namibia,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/NAM/rss,4,Economía,125,Namibia,en,False,30 +9464,Ministry of Finance Namibia,Fiscal policy budget reports and economic planning.,https://www.mof.gov.na/feed/,4,Economía,125,Namibia,en,False,30 +9471,Namibia Investment Center,Investment news business climate and economic development.,https://www.investnamibia.com.na/feed/,4,Economía,125,Namibia,en,False,30 +9466,Namibia Statistics Agency,Economic indicators statistics and data releases.,https://www.nsa.org.na/feed/,4,Economía,125,Namibia,en,True,0 +9467,Nampa Economy,Economic and business news from national news agency.,https://www.nampa.org/economy/feed/,4,Economía,125,Namibia,en,False,30 +9469,New Era Business,Business news trade and investment updates.,https://www.newera.com.na/business/feed/,4,Economía,125,Namibia,en,False,30 +9468,The Namibian Business,Business news market updates and economic analysis.,https://www.namibian.com.na/business/feed/,4,Economía,125,Namibia,en,False,30 +9472,World Bank Namibia,Economic reports development data and projects.,https://www.worldbank.org/en/country/namibia/rss,4,Economía,125,Namibia,en,False,30 +9462,AllAfrica Namibia Politics,Political news aggregated from Namibian sources.,https://allafrica.com/tools/headlines/rdf/namibia/politics/,11,Política,125,Namibia,en,False,30 +9457,Ministry International Relations,Foreign policy diplomatic relations and international news.,https://www.mir.gov.na/feed/,11,Política,125,Namibia,en,False,30 +9458,Namibia Press Agency Politics,Political news from national news agency.,https://www.nampa.org/politics/feed/,11,Política,125,Namibia,en,False,30 +9460,New Era Politics,Political news from government newspaper.,https://www.newera.com.na/politics/feed/,11,Política,125,Namibia,en,False,30 +9456,Office Prime Minister,Government news policies and official statements.,https://www.opm.gov.na/feed/,11,Política,125,Namibia,en,False,30 +9455,Parliament of Namibia,Parliamentary proceedings legislative news and updates.,https://www.parliament.na/feed/,11,Política,125,Namibia,en,True,0 +9454,Presidency of Namibia,Presidential news government announcements and policies.,https://www.op.gov.na/feed/,11,Política,125,Namibia,en,False,30 +9461,Republikein Politics,Political updates and government news from Afrikaans paper.,https://www.republikein.com.na/politics/feed/,11,Política,125,Namibia,af,False,30 +9459,The Namibian Politics,Political coverage and analysis from leading newspaper.,https://www.namibian.com.na/politics/feed/,11,Política,125,Namibia,en,False,30 +9463,UN Namibia News,UN political and developmental news about Namibia.,https://namibia.un.org/en/rss.xml,11,Política,125,Namibia,en,True,0 +9521,Human Rights Commission,Human rights reports social justice and advocacy.,https://www.nhrc.org.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9522,Ministry of Culture Arts,Cultural news heritage preservation and arts.,https://www.mcac.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9523,Ministry of Education,Education news schools and academic development.,https://www.moe.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9520,Ministry of Gender Equality,Gender equality women empowerment and family affairs.,https://www.mge.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9515,Ministry of Health,Public health news disease prevention and medical care.,https://www.mhss.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9514,Ministry of Social Development,Social welfare family support and community services.,https://www.msds.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9519,Ministry of Youth Sports,Youth programs activities and social development.,https://www.mys.gov.na/feed/,13,Sociedad,125,Namibia,en,False,30 +9516,Nampa Society,Social news from national news agency.,https://www.nampa.org/society/feed/,13,Sociedad,125,Namibia,en,False,30 +9518,New Era Society,Social news and current affairs from newspaper.,https://www.newera.com.na/society/feed/,13,Sociedad,125,Namibia,en,False,30 +9517,The Namibian Society,Social issues community news and cultural coverage.,https://www.namibian.com.na/society/feed/,13,Sociedad,125,Namibia,en,False,30 +9568,Agriculture Research Nauru,Farming science food security and agricultural studies.,https://www.agriculture.gov.nr/research/feed/,1,Ciencia,126,Nauru,en,False,30 +9571,Energy Research Nauru,Energy science renewable resources and technology.,https://www.energy.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9570,Geological Survey Nauru,Geological science earth studies and natural resources.,https://www.geology.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9566,Marine Research Nauru,Marine science coral reef studies and oceanography.,https://www.marine.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9562,Ministry Education Nauru,Science education research and academic news.,https://www.education.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9564,Nauru Environment Agency,Environmental science conservation and climate studies.,https://www.environment.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9563,Nauru Health Research,Public health research and medical studies.,https://www.health.gov.nr/research/feed/,1,Ciencia,126,Nauru,en,False,30 +9569,Nauru Science Journal,Scientific publications research papers and studies.,https://www.nsj.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9565,Pacific Climate Change,Climate science regional research and sustainability.,http://www.pacificclimatechange.net/nauru/feed/,1,Ciencia,126,Nauru,en,False,30 +9567,Water Resources Nauru,Water science hydrology and resource management.,https://www.water.gov.nr/feed/,1,Ciencia,126,Nauru,en,False,30 +9541,ADB Nauru,Development finance projects and economic updates.,https://www.adb.org/countries/nauru/rss,4,Economía,126,Nauru,en,False,30 +9537,Fisheries Nauru Economy,Fishing industry news and marine resources.,https://www.fisheries.gov.nr/feed/,4,Economía,126,Nauru,en,False,30 +9533,Ministry Finance Nauru,Fiscal policy budget reports and economic planning.,https://www.finance.gov.nr/feed/,4,Economía,126,Nauru,en,False,30 +9539,Nauru Air Corporation,Transportation economy airline and logistics news.,https://www.nauruair.com.nr/feed/,4,Economía,126,Nauru,en,False,30 +9536,Nauru Bulletin Economy,Economic and business news from government bulletin.,https://www.naurubulletin.gov.nr/economy/feed/,4,Economía,126,Nauru,en,False,30 +9534,Nauru Revenue Office,Taxation customs and revenue news.,https://www.revenue.gov.nr/feed/,4,Economía,126,Nauru,en,False,30 +9130,Pacific Islands Report Economy,Economic coverage and regional analysis.,http://www.pireport.org/rss/economy.xml,4,Economía,126,Nauru,en,False,30 +9538,Phosphate Resources,Mining economy phosphate industry and exports.,https://www.prln.com.nr/feed/,4,Economía,126,Nauru,en,False,30 +9535,Statistics Nauru,Economic indicators statistics and data releases.,https://www.statistics.gov.nr/feed/,4,Economía,126,Nauru,en,False,30 +9540,World Bank Nauru,Economic reports development data and projects.,https://www.worldbank.org/en/country/nauru/rss,4,Economía,126,Nauru,en,False,30 +9530,AllAfrica Nauru Politics,Political news aggregated from regional sources.,https://allafrica.com/tools/headlines/rdf/nauru/politics/,11,Política,126,Nauru,en,False,30 +9532,Forum Fisheries Agency,Pacific regional cooperation and political news.,https://www.ffa.int/rss,11,Política,126,Nauru,en,False,30 +9524,Government of Nauru,Government news official announcements and policies.,https://www.naurugov.nr/feed/,11,Política,126,Nauru,en,False,30 +9527,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://www.foreignaffairs.gov.nr/feed/,11,Política,126,Nauru,en,False,30 +9528,Nauru Bulletin,Government news and official publications.,https://www.naurubulletin.gov.nr/feed/,11,Política,126,Nauru,en,False,30 +9529,Pacific Islands Report Nauru,Political coverage and regional analysis.,http://www.pireport.org/rss/nauru.xml,11,Política,126,Nauru,en,False,30 +9525,Parliament of Nauru,Parliamentary proceedings legislative news and updates.,https://www.nauruparliament.nr/feed/,11,Política,126,Nauru,en,False,30 +9526,President Office Nauru,Presidential news and official statements.,https://www.presidency.gov.nr/feed/,11,Política,126,Nauru,en,False,30 +9113,Radio New Zealand Pacific,Regional political news covering Nauru.,http://www.rnz.co.nz/rss/pacific.xml,11,Política,126,Nauru,en,True,0 +9531,UN Nauru News,UN political and developmental news about Nauru.,https://nauru.un.org/en/rss.xml,11,Política,126,Nauru,en,False,30 +9581,Community Radio Nauru,Local community news cultural programs and social topics.,https://www.radio.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9579,Culture Heritage Nauru,Cultural news heritage preservation and arts.,https://www.culture.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9580,Education Nauru,Education news schools and academic development.,https://www.education.gov.nr/news/feed/,13,Sociedad,126,Nauru,en,False,30 +9578,Human Rights Nauru,Human rights reports social justice and advocacy.,https://www.humanrights.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9572,Ministry Social Services,Social welfare family support and community services.,https://www.socialservices.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9573,Ministry of Health Nauru,Public health news disease prevention and medical care.,https://www.health.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9574,Nauru Bulletin Society,Social news from government bulletin.,https://www.naurubulletin.gov.nr/society/feed/,13,Sociedad,126,Nauru,en,False,30 +9575,Pacific Islands Society,Social issues community news and cultural coverage.,http://www.pacificislandssociety.com/nauru/feed/,13,Sociedad,126,Nauru,en,False,30 +9577,Women Affairs Nauru,Gender equality women empowerment and family affairs.,https://www.women.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9576,Youth Development Nauru,Youth programs activities and social development.,https://www.youth.gov.nr/feed/,13,Sociedad,126,Nauru,en,False,30 +9604,Central Bureau Statistics,Economic indicators statistics and data releases.,https://cbs.gov.np/feed/,4,Economía,127,Nepal,ne,False,30 +9609,Federation Nepalese Chambers,Business news trade events and economic updates.,https://fncci.org.np/feed/,4,Economía,127,Nepal,ne,False,30 +9611,IMF Nepal,IMF reports macroeconomic analysis and consultations.,https://www.imf.org/en/Countries/NPL/rss,4,Economía,127,Nepal,en,False,30 +9606,Kantipur Economy,Business news market updates and economic analysis.,https://ekantipur.com/economy/feed/,4,Economía,127,Nepal,ne,False,30 +9602,Ministry of Finance Nepal,Fiscal policy budget reports and economic planning.,https://mof.gov.np/feed/,4,Economía,127,Nepal,ne,False,30 +9608,Nepal Investment Board,Investment news business climate and economic development.,https://nib.gov.np/feed/,4,Economía,127,Nepal,en,False,30 +9603,Nepal Rastra Bank,Monetary policy financial stability and banking news.,https://nrb.org.np/feed/,4,Economía,127,Nepal,ne,True,0 +9605,RSS Nepal Economy,Economic and business news from national news agency.,https://www.rss.gov.np/economy/feed/,4,Economía,127,Nepal,ne,False,30 +9607,The Kathmandu Post Business,Business news trade and investment updates.,https://kathmandupost.com/business/feed/,4,Economía,127,Nepal,en,False,30 +9610,World Bank Nepal,Economic reports development data and projects.,https://www.worldbank.org/en/country/nepal/rss,4,Economía,127,Nepal,en,False,30 +9600,AllAfrica Nepal Politics,Political news aggregated from Nepali sources.,https://allafrica.com/tools/headlines/rdf/nepal/politics/,11,Política,127,Nepal,en,False,30 +9597,Kantipur Politics,Political coverage and analysis from leading newspaper.,https://ekantipur.com/politics/feed/,11,Política,127,Nepal,ne,False,30 +9595,Ministry Foreign Affairs,Foreign policy diplomatic relations and international news.,https://mofa.gov.np/feed/,11,Política,127,Nepal,ne,False,30 +9599,Nagarik News Politics,Political updates and government news.,https://nagariknews.nagariknetwork.com/politics/feed/,11,Política,127,Nepal,ne,False,30 +9593,Parliament of Nepal,Parliamentary proceedings legislative news and updates.,https://parliament.gov.np/feed/,11,Política,127,Nepal,ne,False,30 +9592,President Office Nepal,Presidential news official announcements and state events.,https://president.gov.np/feed/,11,Política,127,Nepal,ne,False,30 +9594,Prime Minister Office,Government news policies and official statements.,https://pmo.gov.np/feed/,11,Política,127,Nepal,ne,False,30 +9596,RSS Nepal Politics,Political news from national news agency.,https://www.rss.gov.np/politics/feed/,11,Política,127,Nepal,ne,False,30 +9598,The Kathmandu Post Politics,Political news from English-language newspaper.,https://kathmandupost.com/politics/feed/,11,Política,127,Nepal,en,False,30 +9601,UN Nepal News,UN political and developmental news about Nepal.,https://nepal.un.org/en/rss.xml,11,Política,127,Nepal,en,True,0 +6354,crisisgroup.org Omán,,https://www.crisisgroup.org/rss/157,11,Política,133,Omán,,True,0 +7,BBC News - Technology,"Noticias de tecnología de la BBC (Reino Unido, inglés).",https://feeds.bbci.co.uk/news/technology/rss.xml,14,Tecnología,144,Reino Unido,en,True,0 +6348,crisisgroup.org Siria,,https://www.crisisgroup.org/rss/83,11,Política,164,Siria,en,True,0 +9591,Dagens Nyheter Ekonomi,Dagstidningens bevakning av ekonomi och näringsliv.,https://www.dn.se/rss/ekonomi,4,Economía,170,Suecia,sv,True,0 +9583,Finansdepartementet,Finanspolitik budget ekonomisk planering.,https://www.regeringen.se/finansdepartementet/rss,4,Economía,170,Suecia,sv,False,30 +9589,IMF Sweden,Macroekonomiska bedömningar finanssektor.,https://www.imf.org/en/Countries/SWE/rss,4,Economía,170,Suecia,en,False,30 +9584,Näringsdepartementet,Näringspolitik tillväxt innovation handel.,https://www.regeringen.se/naringsdepartementet/rss,4,Economía,170,Suecia,sv,False,30 +9590,SVT Nyheter Ekonomi,Ekonomiska nyheter och analys från public service.,https://www.svt.se/nyheter/rss.xml?category=ekonomi,4,Economía,170,Suecia,sv,True,0 +9586,Skatteverket,Skatteregler beskattningsinformation.,https://www.skatteverket.se/rss,4,Economía,170,Suecia,sv,False,30 +9585,Statistiska centralbyrån SCB,Ekonomisk statistik handelsdata indikatorer.,https://www.scb.se/rss,4,Economía,170,Suecia,sv,False,30 +9582,Sveriges Riksbank,Penningpolitik finansiell stabilitet bankregleringar.,https://www.riksbank.se/sv/rss,4,Economía,170,Suecia,sv,False,30 +9587,Tullverket,Tullregler handelsprocedurer internationell handel.,https://www.tullverket.se/rss,4,Economía,170,Suecia,sv,False,30 +9588,World Bank Sweden,Ekonomiska rapporter utvecklingssamarbete.,https://www.worldbank.org/en/country/sweden/rss,4,Economía,170,Suecia,en,False,30 +9561,Dagens Nyheter Politik,Dagstidningens bevakning av politiska utvecklingar.,https://www.dn.se/rss/politik,11,Política,170,Suecia,sv,True,0 +9558,Moderaterna,Politiska nyheter partiståndpunkter och aktiviteter.,https://www.moderaterna.se/rss,11,Política,170,Suecia,sv,True,0 +9552,Regeringen Sverige,Officiella nyheter och beslut från Sveriges regering.,https://www.regeringen.se/rss,11,Política,170,Suecia,sv,False,30 +9553,Riksdagen,Parlamentariska förhandlingar lagstiftning och debatter.,https://www.riksdagen.se/sv/rss,11,Política,170,Suecia,sv,False,30 +9559,SVT Nyheter Politik,Politiska nyheter och analys från public service.,https://www.svt.se/nyheter/rss.xml?category=politik,11,Política,170,Suecia,sv,True,0 +9557,Socialdemokraterna,Politiska nyheter partiståndpunkter och aktiviteter.,https://www.socialdemokraterna.se/rss,11,Política,170,Suecia,sv,False,30 +9555,Statsrådsberedningen,Samordning av regeringens arbete officiella publikationer.,https://www.regeringen.se/srb/rss,11,Política,170,Suecia,sv,False,30 +9560,Sveriges Radio Ekot Politik,Politiska nyhetsrapportering och aktuella händelser.,https://sverigesradio.se/rss.xml?programid=83,11,Política,170,Suecia,sv,False,30 +9554,Utrikesdepartementet,Utrikespolitik diplomatiska förbindelser internationella frågor.,https://www.regeringen.se/ud/rss,11,Política,170,Suecia,sv,False,30 +9556,Valmyndigheten,Valinformation röstningsprocedurer officiella resultat.,https://www.val.se/rss,11,Política,170,Suecia,sv,False,30 +9499,CERN,European particle physics research laboratory.,https://home.cern/rss,1,Ciencia,171,Suiza,en,False,30 +9497,Paul Scherrer Institute PSI,Interdisciplinary research large research facilities.,https://www.psi.ch/en/rss,1,Ciencia,171,Suiza,en,False,30 +9498,Swiss Academies of Arts and Sciences,Science policy research promotion scientific advice.,https://www.akademien-schweiz.ch/en/rss,1,Ciencia,171,Suiza,en,False,30 +9495,Swiss Federal Institute of Technology Lausanne EPFL,Scientific research engineering innovation.,https://www.epfl.ch/en/rss,1,Ciencia,171,Suiza,en,True,0 +9494,Swiss Federal Institute of Technology Zurich ETH,Scientific research technology innovation academic news.,https://www.ethz.ch/en/rss.html,1,Ciencia,171,Suiza,en,False,30 +9500,Swiss Institute of Bioinformatics,Bioinformatics research computational biology.,https://www.sib.swiss/en/rss,1,Ciencia,171,Suiza,en,False,30 +9496,Swiss National Science Foundation SNSF,Research funding scientific projects grants.,https://www.snf.ch/en/rss,1,Ciencia,171,Suiza,en,False,30 +9501,Swiss Polar Institute,Polar research climate studies environmental science.,https://swisspolar.ch/en/rss,1,Ciencia,171,Suiza,en,True,0 +9503,Swissinfo Science,Science technology research news.,https://www.swissinfo.ch/eng/science/rss,1,Ciencia,171,Suiza,en,False,30 +9502,UNESCO Switzerland,Science policy international cooperation.,https://www.unesco.ch/en/rss/science,1,Ciencia,171,Suiza,en,False,30 +9476,Federal Department of Foreign Affairs,Foreign policy international relations diplomatic news.,https://www.eda.admin.ch/eda/en/home/rss.xml,11,Política,171,Suiza,en,False,30 +9483,Le Temps Politics,Newspaper coverage of Swiss political developments.,https://www.letemps.ch/rss/politique,11,Política,171,Suiza,fr,False,30 +9480,Social Democratic Party of Switzerland SP,Political party news positions and activities.,https://www.sp-ps.ch/rss,11,Política,171,Suiza,de,True,0 +9482,Swiss Broadcasting Corporation SRF Politics,Political coverage from public broadcaster.,https://www.srf.ch/news/rss/politik,11,Política,171,Suiza,de,False,30 +9477,Swiss Federal Chancellery,Federal administration coordination official publications.,https://www.bk.admin.ch/bk/en/home/rss.xml,11,Política,171,Suiza,en,False,30 +9474,Swiss Federal Council Portal,Official government news federal decisions and policies.,https://www.admin.ch/gov/en/rss.xml,11,Política,171,Suiza,en,True,0 +9478,Swiss Federal Statistical Office Elections,Election results voter statistics political participation.,https://www.bfs.admin.ch/bfs/en/home/rss/politics.xml,11,Política,171,Suiza,en,False,30 +9475,Swiss Parliament,Parliamentary proceedings legislation and debates.,https://www.parlament.ch/en/rss,11,Política,171,Suiza,en,False,30 +9479,Swiss People's Party SVP,Political party news positions and activities.,https://www.svp.ch/rss,11,Política,171,Suiza,de,False,30 +9481,Swissinfo Politics,Political news analysis and current affairs.,https://www.swissinfo.ch/eng/politics/rss,11,Política,171,Suiza,en,False,30 +9549,Caritas Switzerland,Poverty reduction social assistance charity.,https://www.caritas.ch/en/rss,13,Sociedad,171,Suiza,en,False,30 +9546,Federal Office for Gender Equality FOGE,Gender equality womens rights equal opportunities.,https://www.ebg.admin.ch/ebg/en/home/rss.xml,13,Sociedad,171,Suiza,en,False,30 +9542,Federal Office of Public Health FOPH,Public health disease prevention healthcare.,https://www.bag.admin.ch/bag/en/home/rss.xml,13,Sociedad,171,Suiza,en,False,30 +9544,Federal Social Insurance Office FSIO,Social security pension system disability insurance.,https://www.bsv.admin.ch/bsv/en/home/rss.xml,13,Sociedad,171,Suiza,en,False,30 +9551,Neue Zürcher Zeitung Gesellschaft,Newspaper coverage of social issues.,https://www.nzz.ch/rss/gesellschaft,13,Sociedad,171,Suiza,de,False,30 +9543,State Secretariat for Education Research and Innovation SERI,Education policy research innovation.,https://www.sbfi.admin.ch/sbfi/en/home/rss.xml,13,Sociedad,171,Suiza,en,False,30 +9545,Swiss Federal Statistical Office Social,Social statistics demographic data living conditions.,https://www.bfs.admin.ch/bfs/en/home/rss/society.xml,13,Sociedad,171,Suiza,en,False,30 +9547,Swiss Red Cross,Humanitarian aid disaster response social services.,https://www.redcross.ch/en/rss,13,Sociedad,171,Suiza,en,False,30 +9550,Swissinfo Society,Social issues community life cultural topics.,https://www.swissinfo.ch/eng/society/rss,13,Sociedad,171,Suiza,en,False,30 +9548,UNICEF Switzerland,Childrens rights education protection.,https://www.unicef.ch/en/rss,13,Sociedad,171,Suiza,en,False,30 +9511,Innosuisse,Swiss innovation agency startup funding.,https://www.innosuisse.ch/inno/en/home/rss.xml,14,Tecnología,171,Suiza,en,False,30 +9507,Sunrise UPC,Telecom services digital solutions.,https://www.sunrise.ch/en/rss,14,Tecnología,171,Suiza,en,False,30 +9510,Swiss Digital Initiative,Digital trust ethics technology governance.,https://www.swissdigitalinitiative.org/en/rss,14,Tecnología,171,Suiza,en,False,30 +9509,Swiss Economic Forum SEF Tech,Technology entrepreneurship innovation events.,https://www.swisseconomicforum.ch/en/rss,14,Tecnología,171,Suiza,en,False,30 +9504,Swiss Federal Office of Communications OFCOM,ICT regulations telecommunications digital policy.,https://www.bakom.admin.ch/bakom/en/home/rss.xml,14,Tecnología,171,Suiza,en,False,30 +9508,Swiss ICT,Professional association for ICT industry.,https://www.swissict.ch/en/rss,14,Tecnología,171,Suiza,en,False,30 +9506,Swisscom,Telecom services network technology innovation.,https://www.swisscom.ch/en/about/rss.html,14,Tecnología,171,Suiza,en,False,30 +9512,Swissinfo Technology,Technology news digital trends.,https://www.swissinfo.ch/eng/technology/rss,14,Tecnología,171,Suiza,en,False,30 +9505,digitalswitzerland,National initiative for digital innovation and transformation.,https://digitalswitzerland.com/en/rss,14,Tecnología,171,Suiza,en,False,30 +9513,inside-it.ch,IT news digital business technology.,https://www.inside-it.ch/en/rss,14,Tecnología,171,Suiza,en,False,30 +9406,Anton de Kom University of Suriname,University research scientific studies academic news.,https://www.uvs.edu/rss/research,1,Ciencia,172,Surinam,nl,False,30 +9409,Foundation for Nature Conservation Suriname,Biodiversity research wildlife conservation.,https://www.stinasu.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9412,Geological and Mining Service Suriname,Geological research mining studies natural resources.,https://www.gmd.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9411,Medical Mission Suriname,Medical research public health tropical diseases.,https://www.mdzs.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9408,National Institute for Environment and Development,Environmental research sustainability studies.,https://www.nimos.org/rss,1,Ciencia,172,Surinam,nl,False,30 +9415,Scientific Research Suriname,Aggregator of scientific news research publications.,https://www.wetenschap.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9410,Suriname Agricultural Research Center,Agricultural studies crop research farming innovations.,https://www.celos.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9413,Suriname Space Center,Astronomy space research satellite data.,https://www.srsc.sr/rss,1,Ciencia,172,Surinam,nl,False,30 +9414,UNESCO Suriname,Science education capacity building international cooperation.,https://www.unesco.org/suriname/rss/science,1,Ciencia,172,Surinam,en,False,30 +9386,Central Bank of Suriname,Monetary policy financial stability banking regulations.,https://www.cbvs.sr/rss,4,Economía,172,Surinam,nl,False,30 +9394,De Ware Tijd Economy,Newspaper coverage of economic business news.,https://www.dwtonline.com/rss/economie,4,Economía,172,Surinam,nl,False,30 +9388,General Bureau of Statistics Suriname,Economic indicators trade data statistics.,https://www.statistics-suriname.org/rss,4,Economía,172,Surinam,nl,True,0 +9393,Inter-American Development Bank Suriname,Development projects economic studies.,https://www.iadb.org/en/countries/suriname/rss,4,Economía,172,Surinam,en,False,30 +9387,Ministry of Finance Suriname,Fiscal policy budget economic planning.,https://www.gov.sr/ministeries/financien/rss,4,Economía,172,Surinam,nl,False,30 +9395,Starnieuws Economy,News website covering Surinamese economy.,https://www.starnieuws.com/rss/economie,4,Economía,172,Surinam,nl,False,30 +9391,State Oil Company Suriname,Oil gas industry news energy sector updates.,https://www.staatsolie.com/rss,4,Economía,172,Surinam,nl,False,30 +9390,Suriname Chamber of Commerce and Industry,Business regulations investment climate updates.,https://www.surinamechamber.org/rss,4,Economía,172,Surinam,nl,False,30 +9389,Suriname Trade and Industry Association,Business news trade opportunities economic updates.,https://www.vsis.sr/rss,4,Economía,172,Surinam,nl,False,30 +9392,World Bank Suriname,Economic reports development project funding.,https://www.worldbank.org/en/country/suriname/rss,4,Economía,172,Surinam,en,False,30 +9385,De Ware Tijd Politics,Newspaper coverage of political developments.,https://www.dwtonline.com/rss/politiek,11,Política,172,Surinam,nl,False,30 +9376,Government of Suriname Portal,Official government news and public announcements.,https://www.gov.sr/rss,11,Política,172,Surinam,nl,True,0 +9380,Independent Electoral Bureau Suriname,Election information voter registration results.,https://www.vsb.sr/rss,11,Política,172,Surinam,nl,False,30 +9379,Ministry of Foreign Affairs Suriname,Foreign policy international relations diplomatic news.,https://www.gov.sr/ministeries/bibis/rss,11,Política,172,Surinam,nl,False,30 +9378,National Assembly of Suriname,Parliamentary proceedings legislation and debates.,https://www.dna.sr/rss,11,Política,172,Surinam,nl,False,30 +9381,National Democratic Party NDP,Political party news activities and statements.,https://www.ndp.sr/rss,11,Política,172,Surinam,nl,False,30 +9377,President of Suriname,Presidential activities speeches and official statements.,https://www.president.sr/rss,11,Política,172,Surinam,nl,False,30 +9382,Progressive Reform Party VHP,Political party news activities and policy positions.,https://www.vhp.sr/rss,11,Política,172,Surinam,nl,False,30 +9383,STVS News Politics,Political news coverage on national television.,https://www.stvs.sr/rss/politics,11,Política,172,Surinam,nl,False,30 +9384,Waterkant Net Politics,News website covering Surinamese politics.,https://www.waterkant.net/rss/politiek,11,Política,172,Surinam,nl,False,30 +9443,De Ware Tijd Society,Newspaper coverage of social issues community life.,https://www.dwtonline.com/rss/samenleving,13,Sociedad,172,Surinam,nl,False,30 +9439,Gender Bureau Suriname,Gender equality womens rights empowerment.,https://www.genderbureau.sr/rss,13,Sociedad,172,Surinam,nl,False,30 +9438,General Bureau of Statistics Social Data,Social statistics demographic data living conditions.,https://www.statistics-suriname.org/rss/social,13,Sociedad,172,Surinam,nl,False,30 +9407,Ministry of Education Science and Culture,Education policies school programs literacy.,https://www.gov.sr/ministeries/minov/rss,13,Sociedad,172,Surinam,nl,False,30 +9436,Ministry of Health Suriname,Public health programs disease control healthcare.,https://www.gov.sr/ministeries/volksgezondheid/rss,13,Sociedad,172,Surinam,nl,False,30 +9437,Ministry of Social Affairs and Housing,Social welfare poverty reduction community development.,https://www.gov.sr/ministeries/sozavo/rss,13,Sociedad,172,Surinam,nl,False,30 +9444,Starnieuws Society,News website covering social cultural topics.,https://www.starnieuws.com/rss/samenleving,13,Sociedad,172,Surinam,nl,False,30 +9440,Suriname Red Cross,Humanitarian aid disaster response community health.,https://www.rodekruis.sr/rss,13,Sociedad,172,Surinam,nl,True,0 +9442,UNDP Suriname,Sustainable development community projects.,https://www.sr.undp.org/rss,13,Sociedad,172,Surinam,en,False,30 +9441,UNICEF Suriname,Childrens rights education social protection.,https://www.unicef.org/suriname/rss.xml,13,Sociedad,172,Surinam,en,False,30 +9345,Bangkok Post Sci Tech News,Science technology research news in Thailand.,https://www.bangkokpost.com/rss/scitech,1,Ciencia,173,Tailandia,en,False,30 +9340,Chulalongkorn University Research,Academic research multidisciplinary scientific studies.,https://www.chula.ac.th/rss/research,1,Ciencia,173,Tailandia,en,False,30 +9343,Department of National Parks Wildlife and Plant Conservation,Wildlife research biodiversity conservation ecology.,https://www.dnp.go.th/rss,1,Ciencia,173,Tailandia,th,False,30 +9339,Mahidol University Research,Medical research health sciences scientific publications.,https://www.mahidol.ac.th/rss/research,1,Ciencia,173,Tailandia,en,False,30 +9336,Ministry of Higher Education Science Research and Innovation,National research policies science funding innovation programs.,https://www.mhesi.go.th/rss,1,Ciencia,173,Tailandia,th,False,30 +9342,National Astronomical Research Institute of Thailand,Astronomy space research astrophysics.,https://www.narit.or.th/rss,1,Ciencia,173,Tailandia,en,False,30 +9337,National Science and Technology Development Agency,Technology research scientific projects innovation grants.,https://www.nstda.or.th/rss,1,Ciencia,173,Tailandia,th,False,30 +9341,Thailand Center of Excellence for Life Sciences,Biotechnology medical research life sciences.,https://www.tcels.or.th/rss,1,Ciencia,173,Tailandia,en,False,30 +9338,Thailand Institute of Scientific and Technological Research,Applied research technology development industrial innovations.,https://www.tistr.or.th/rss,1,Ciencia,173,Tailandia,th,False,30 +9344,UNESCO Bangkok Science,Regional science education research cooperation.,https://www.unescobkk.org/rss/science,1,Ciencia,173,Tailandia,en,False,30 +9333,Asian Development Bank Thailand,Development projects economic assistance.,https://www.adb.org/countries/thailand/rss,4,Economía,173,Tailandia,en,False,30 +9334,Bangkok Post Business,Thai business financial economic news.,https://www.bangkokpost.com/rss/business,4,Economía,173,Tailandia,en,False,30 +9326,Bank of Thailand,Monetary policy financial stability banking regulations.,https://www.bot.or.th/rss,4,Economía,173,Tailandia,en,False,30 +9329,Ministry of Commerce Thailand,Trade policies export import regulations.,https://www.moc.go.th/rss,4,Economía,173,Tailandia,th,False,30 +9327,Ministry of Finance Thailand,Fiscal policy budget economic planning.,https://www.mof.go.th/rss,4,Economía,173,Tailandia,th,True,0 +9328,National Economic and Social Development Council,Economic planning development strategies indicators.,https://www.nesdc.go.th/rss,4,Economía,173,Tailandia,th,True,0 +9331,Stock Exchange of Thailand,Financial market news stock trading updates.,https://www.set.or.th/rss,4,Economía,173,Tailandia,en,False,30 +9330,Thai Customs Department,Customs regulations trade procedures updates.,https://www.customs.go.th/rss,4,Economía,173,Tailandia,th,False,30 +9335,The Nation Thailand Business,Business news and economic analysis.,https://www.nationthailand.com/rss/business,4,Economía,173,Tailandia,en,False,30 +9332,World Bank Thailand,Economic reports development project funding.,https://www.worldbank.org/en/country/thailand/rss,4,Economía,173,Tailandia,en,False,30 +9293,Bangkok Post Politics,Political news coverage and analysis.,https://www.bangkokpost.com/rss/politics,11,Política,173,Tailandia,en,False,30 +9290,Election Commission of Thailand,Election news voting procedures official results.,https://www.ect.go.th/rss,11,Política,173,Tailandia,th,False,30 +9286,Government of Thailand Portal,Official government news and public announcements.,https://www.thaigov.go.th/rss,11,Política,173,Tailandia,th,False,30 +9295,Khaosod English Politics,Thai political news and developments.,https://www.khaosodenglish.com/rss/politics,11,Política,173,Tailandia,en,False,30 +9288,Ministry of Foreign Affairs Thailand,Foreign policy diplomatic relations international news.,https://www.mfa.go.th/rss,11,Política,173,Tailandia,en,False,30 +9287,Office of the Prime Minister Thailand,Prime ministerial activities policy announcements.,https://www.thaigov.go.th/rss/pm,11,Política,173,Tailandia,th,False,30 +9291,Palang Pracharath Party,Ruling party news political activities.,https://www.pprp.or.th/rss,11,Política,173,Tailandia,th,True,0 +9289,Parliament of Thailand,Parliamentary proceedings legislation updates.,https://www.parliament.go.th/rss,11,Política,173,Tailandia,th,False,30 +9292,Pheu Thai Party,Major opposition party activities policy positions.,https://www.pheuthai.or.th/rss,11,Política,173,Tailandia,th,False,30 +9294,Thai PBS World Politics,News on Thai politics and current affairs.,https://www.thaipbsworld.com/rss/politics,11,Política,173,Tailandia,en,False,30 +9265,AllAfrica Tanzania Science,Science news and research developments from Tanzania.,https://allafrica.com/tanzania/science/rss,1,Ciencia,174,Tanzania,en,False,30 +9262,Ifakara Health Institute,Medical research public health studies and disease control.,https://www.ihi.or.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9259,Nelson Mandela African Institution of Science and Technology,STEM education research and technological innovations.,https://www.nm-aist.ac.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9258,Sokoine University of Agriculture,Agricultural research livestock studies and farming innovations.,https://www.sua.ac.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9256,Tanzania Commission for Science and Technology,National research policies scientific innovations and funding programs.,https://www.costech.or.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9261,Tanzania Fisheries Research Institute,Aquatic sciences fishery management and marine conservation.,https://www.tafiri.go.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9263,Tanzania Meteorological Authority,Weather research climate studies and environmental monitoring.,https://www.meteo.go.tz/rss,1,Ciencia,174,Tanzania,en,False,30 +9260,Tanzania Wildlife Research Institute,Wildlife conservation biodiversity studies and ecological research.,https://www.tawiri.or.tz/rss,1,Ciencia,174,Tanzania,en,True,0 +9264,UNESCO Tanzania Science,Science education capacity building and international research cooperation.,https://www.unesco.org/en/countries/tz/rss/science,1,Ciencia,174,Tanzania,en,False,30 +9257,University of Dar es Salaam Research,Academic research scientific publications and university projects.,https://www.udsm.ac.tz/rss/research,1,Ciencia,174,Tanzania,en,False,30 +9253,African Development Bank Tanzania,Development projects infrastructure financing and economic studies.,https://www.afdb.org/en/countries/tanzania/rss,4,Economía,174,Tanzania,en,False,30 +9246,Bank of Tanzania,Monetary policy banking regulations and financial stability reports.,https://www.bot.go.tz/rss,4,Economía,174,Tanzania,en,False,30 +9252,IMF Tanzania,Macroeconomic assessments fiscal analysis and country reports.,https://www.imf.org/en/Countries/TZA/rss,4,Economía,174,Tanzania,en,False,30 +9247,Ministry of Finance Tanzania,Fiscal policy budget announcements and economic planning.,https://www.mof.go.tz/rss,4,Economía,174,Tanzania,en,False,30 +9248,National Bureau of Statistics Tanzania,Official economic statistics indicators and trade data.,https://www.nbs.go.tz/rss,4,Economía,174,Tanzania,en,False,30 +9250,Tanzania Investment Centre,Investment opportunities business regulations and economic incentives.,https://www.tic.go.tz/rss,4,Economía,174,Tanzania,en,False,30 +9249,Tanzania Revenue Authority,Tax policies customs regulations and revenue collection updates.,https://www.tra.go.tz/rss,4,Economía,174,Tanzania,en,False,30 +9254,The Citizen Tanzania Business,News on Tanzanian economy business and financial markets.,https://www.thecitizen.co.tz/rss/business,4,Economía,174,Tanzania,en,False,30 +9255,The East African Business,Tanzania business news and regional economic analysis.,https://www.theeastafrican.co.ke/rss/business,4,Economía,174,Tanzania,en,False,30 +9251,World Bank Tanzania,Economic reports development indicators and project funding.,https://www.worldbank.org/en/country/tanzania/rss,4,Economía,174,Tanzania,en,False,30 +9201,ACT Wazalendo Opposition Party,Opposition party activities political statements and policy positions.,https://actwazalendo.or.tz/rss,11,Política,174,Tanzania,en,False,30 +9205,AllAfrica Tanzania Politics,Political news aggregation and analysis from Tanzanian sources.,https://allafrica.com/tanzania/politics/rss,11,Política,174,Tanzania,en,False,30 +9200,Chama Cha Mapinduzi CCM,Ruling party news political activities and party announcements.,https://www.ccm.or.tz/rss,11,Política,174,Tanzania,sw,False,30 +9196,Government of Tanzania Portal,Official government news presidential activities and public announcements.,https://www.tanzania.go.tz/rss,11,Política,174,Tanzania,en,False,30 +9199,Ministry of Foreign Affairs Tanzania,Foreign policy diplomatic relations and international cooperation.,https://www.foreign.go.tz/rss,11,Política,174,Tanzania,en,False,30 +9202,National Electoral Commission Tanzania,Election news voter registration and electoral results.,https://www.nec.go.tz/rss,11,Política,174,Tanzania,en,False,30 +9198,Parliament of Tanzania Bunge,Parliamentary proceedings legislation updates and committee news.,https://www.parliament.go.tz/rss,11,Política,174,Tanzania,sw,False,30 +9197,President of Tanzania United Republic,Presidential speeches directives and official engagements.,https://www.ikulu.go.tz/rss,11,Política,174,Tanzania,en,False,30 +9203,The Citizen Tanzania Politics,News coverage of Tanzanian politics and current affairs.,https://www.thecitizen.co.tz/rss/politics,11,Política,174,Tanzania,en,False,30 +9204,The East African Tanzania Politics,Regional perspective on Tanzanian political developments.,https://www.theeastafrican.co.ke/rss/tanzania/politics,11,Política,174,Tanzania,en,False,30 +9285,IPTE Tanzania,Social welfare community development and public assistance programs.,https://www.iptetanzania.go.tz/rss,13,Sociedad,174,Tanzania,sw,False,30 +9277,Ministry of Education Science and Technology,Education policies school programs and literacy initiatives.,https://www.moe.go.tz/rss,13,Sociedad,174,Tanzania,en,False,30 +9276,Ministry of Health Tanzania,Public health programs disease control and healthcare services.,https://www.moh.go.tz/rss,13,Sociedad,174,Tanzania,en,False,30 +9278,Ministry of Labor Employment and Youth Development,Youth employment labor rights and job market policies.,https://www.mleyd.go.tz/rss,13,Sociedad,174,Tanzania,en,False,30 +9279,National Bureau of Statistics Social Data,Social statistics demographic surveys and living conditions data.,https://www.nbs.go.tz/social/rss,13,Sociedad,174,Tanzania,en,False,30 +9280,Tanzania Gender Networking Programme,Gender equality women rights and social empowerment.,https://www.tgnp.or.tz/rss,13,Sociedad,174,Tanzania,en,False,30 +9281,Tanzania Red Cross Society,Humanitarian aid disaster response and community health programs.,https://www.trcs.or.tz/rss,13,Sociedad,174,Tanzania,en,True,0 +9284,The Citizen Tanzania Society,News on social issues community life and cultural events.,https://www.thecitizen.co.tz/rss/society,13,Sociedad,174,Tanzania,en,False,30 +9283,UNDP Tanzania Development,Sustainable development community projects and poverty reduction.,https://www.tz.undp.org/rss,13,Sociedad,174,Tanzania,en,False,30 +9282,UNICEF Tanzania,Childrens rights education and social protection programs.,https://www.unicef.org/tanzania/rss.xml,13,Sociedad,174,Tanzania,en,False,30 +9095,Asia Plus Tajikistan Economy,News on business finance trade and economic developments in Tajikistan.,https://asiaplustj.info/en/news/economy/rss,4,Economía,175,Tayikistán,en,False,30 +9091,Asian Development Bank in Tajikistan,Development projects financing and economic assistance programs.,https://www.adb.org/countries/tajikistan/rss,4,Economía,175,Tayikistán,en,False,30 +9090,Customs Service of Tajikistan,Trade regulations import export statistics and customs procedures.,https://www.customs.tj/en/rss,4,Economía,175,Tayikistán,en,False,30 +9094,Eurasian Development Bank Tajikistan,Regional integration projects and infrastructure financing.,https://eabr.org/en/projects/tajikistan/rss,4,Economía,175,Tayikistán,en,False,30 +9093,International Monetary Fund Tajikistan,Macroeconomic assessments fiscal analysis and financial assistance.,https://www.imf.org/en/Countries/TJK/rss,4,Economía,175,Tayikistán,en,False,30 +9087,Ministry of Economic Development and Trade Tajikistan,Economic planning investment policies and trade statistics.,https://medt.tj/en/rss,4,Economía,175,Tayikistán,en,False,30 +9086,National Bank of Tajikistan,Monetary policy banking regulations and financial stability reports.,https://www.nbt.tj/en/rss,4,Economía,175,Tayikistán,en,False,30 +9088,Statistical Agency under President of Tajikistan,Official economic statistics indicators and demographic data.,https://www.stat.tj/en/rss,4,Economía,175,Tayikistán,en,True,0 +9089,Tajikistan Tax Committee,Fiscal policy tax regulations and revenue collection updates.,https://www.pom.tj/en/rss,4,Economía,175,Tayikistán,en,False,30 +9092,World Bank Tajikistan,Economic reports development indicators and project funding in Tajikistan.,https://www.worldbank.org/en/country/tajikistan/rss,4,Economía,175,Tayikistán,en,False,30 +9072,Asia Plus Tajikistan Politics,Independent news agency covering politics and current affairs in Tajikistan.,https://asiaplustj.info/en/news/politics/rss,11,Política,175,Tayikistán,en,False,30 +9070,Assembly of Representatives Majlisi Namoyandagon,News and legislative updates from the lower house of parliament.,https://www.parlament.tj/en/rss/majlisinamoyandagon,11,Política,175,Tayikistán,en,False,30 +9075,CABAR.asia Tajikistan Analysis,Expert analytical articles on political and social issues in Tajikistan.,https://cabar.asia/category/tajikistan-en/rss,11,Política,175,Tayikistán,en,False,30 +9071,Central Commission for Elections and Referendums,Election news voting procedures and official results.,https://www.mirsaid.tj/en/rss,11,Política,175,Tayikistán,en,False,30 +9074,Eurasianet Tajikistan Politics,Reporting and analysis on political developments in Tajikistan and Central Asia.,https://eurasianet.org/tajikistan/rss,11,Política,175,Tayikistán,en,False,30 +9066,Government of Tajikistan Portal,Official portal for government news presidential activities and legal documents.,https://www.gov.tj/en/rss,11,Política,175,Tayikistán,en,False,30 +9068,Ministry of Foreign Affairs Tajikistan,Foreign policy diplomatic relations and international news.,https://mfa.tj/en/rss,11,Política,175,Tayikistán,en,False,30 +9069,National Assembly of Tajikistan Majlisi Milli,News and legislative updates from the upper house of parliament.,https://www.parlament.tj/en/rss/majlisimilli,11,Política,175,Tayikistán,en,False,30 +9067,President of Tajikistan,Official announcements decrees and activities of President Emomali Rahmon.,https://www.president.tj/en/rss,11,Política,175,Tayikistán,en,False,30 +9073,Radio Free Europe Radio Liberty Tajik Service,News and analysis on Tajik politics and human rights.,https://www.rferl.org/rss/rferl-news-tajikistan-politics.xml,11,Política,175,Tayikistán,en,False,30 +9173,Asia Plus Tajikistan Society,News on social issues community events and daily life in Tajikistan.,https://asiaplustj.info/en/news/society/rss,13,Sociedad,175,Tayikistán,en,False,30 +9167,Committee on Women and Family Affairs Tajikistan,Gender equality family support and women empowerment initiatives.,https://women.tj/en/rss,13,Sociedad,175,Tayikistán,en,False,30 +9166,Ministry of Labor and Social Protection Tajikistan,Employment social welfare programs and labor market policies.,https://mol.tj/en/rss,13,Sociedad,175,Tayikistán,en,False,30 +9168,National Statistical Committee Tajikistan,Social statistics demographic data and quality of life indicators.,https://stat.tj/en/social/rss,13,Sociedad,175,Tayikistán,en,True,0 +9174,Radio Ozodi Tajikistan Society,Social issues cultural topics and community discussions in Tajikistan.,https://www.ozodi.org/rss/tajikistan-society.xml,13,Sociedad,175,Tayikistán,tg,False,30 +9175,Tajikistan Civil Society Organizations,NGO activities community projects and social advocacy work.,https://csotajikistan.tj/en/rss,13,Sociedad,175,Tayikistán,en,False,30 +9170,Tajikistan Red Crescent Society,Humanitarian aid disaster response and community health programs.,https://redcrescent.tj/en/rss,13,Sociedad,175,Tayikistán,en,False,30 +9172,UNDP Tajikistan Development Projects,Sustainable development poverty reduction and community empowerment.,https://www.tj.undp.org/rss,13,Sociedad,175,Tayikistán,en,False,30 +9171,UNICEF Tajikistan Social Programs,Childrens rights education and social protection initiatives.,https://www.unicef.org/tajikistan/rss.xml,13,Sociedad,175,Tayikistán,en,False,30 +9169,Youth Committee of Tajikistan,Youth programs education initiatives and volunteer opportunities.,https://youth.tj/en/rss,13,Sociedad,175,Tayikistán,en,False,30 +9123,Asia Plus Tajikistan Technology,News on tech developments internet and innovations in Tajikistan.,https://asiaplustj.info/en/news/technology/rss,14,Tecnología,175,Tayikistán,en,False,30 +9117,Communications Service Tajikistan,Telecommunications regulations internet development and network infrastructure.,https://comservice.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9124,Digital Center of Tajikistan,Digital skills training IT education and technology workshops.,https://digitalcenter.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9119,Megafon Tajikistan Innovations,Mobile internet advancements and telecom developments.,https://megafon.tj/en/news/rss,14,Tecnología,175,Tayikistán,ru,False,30 +9116,Ministry of Industry and New Technologies Tajikistan,Technology policies digital transformation and industrial innovations.,https://minprom.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9125,Startups Tajikistan,Tech startup news entrepreneurship and innovation ecosystem.,https://startups.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9120,Tajikistan Digital Development,IT startups e-government initiatives and digital economy projects.,https://digital.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9122,Tajikistan IT Park,Technology hub startups incubation and IT investment opportunities.,https://itpark.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9121,Tajikistan Open Source Community,Software development open source projects and tech community events.,https://opensource.tj/en/rss,14,Tecnología,175,Tayikistán,en,False,30 +9118,Tcell Tajikistan Tech News,Mobile technologies network updates and digital services in Tajikistan.,https://www.tcell.tj/en/news/rss,14,Tecnología,175,Tayikistán,en,False,30 +8638,Agricultura Cientifica Ciencia,Innovacion y ciencia aplicada a la agricultura.,https://agrociencia.tl/feed/,1,Ciencia,176,Timor Oriental,te,False,30 +8635,Biodiversidade Timor Ciencia,Estudios sobre biodiversidad y ecosistemas terrestres y marinos.,https://biodiversidade.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8640,Clima e Meteorologia Ciencia,Centro de estudios climaticos y meteorologicos.,https://climatemor.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8642,Energia e Ambiente Ciencia,Ciencia para el desarrollo energetico y ambiental.,https://energiaeambiente.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8633,Instituto de Ciencias Timor Ciencia,Investigacion cientifica y estudios academicos en Timor Oriental.,https://cienciatimor.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8637,Mar e Costa Ciencia,Investigacion oceanografica y costera de Timor-Leste.,https://marecosta.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8636,Observatorio Geofisico Ciencia,Monitoreo sismico y geofisica del territorio.,https://observatoriog.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8641,Quimica e Materiais Ciencia,Investigacion en quimica y ciencia de materiales.,https://quimicatl.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8639,Salud Publica Investigacion Ciencia,Estudios e investigacion en salud y medicina.,https://saludciencia.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8634,Universidade Nacional Ciencia,Facultad de ciencias y publicaciones de investigacion.,https://ciencia.untl.tl/feed/,1,Ciencia,176,Timor Oriental,pt,False,30 +8618,Banco Central de Timor-Leste Economia,Banco central y autoridad monetaria de Timor Oriental.,https://bancocentral.tl/feed/,4,Economía,176,Timor Oriental,pt,False,30 +8622,Banco Mundial Timor Economia,Indicadores de desarrollo y analisis economico.,https://www.worldbank.org/en/country/timor-leste/rss,4,Economía,176,Timor Oriental,en,False,30 +8619,Estatisticas Timor-Leste Economia,Oficina nacional de estadisticas e indicadores economicos.,https://statistics.gov.tl/feed/,4,Economía,176,Timor Oriental,pt,False,30 +8623,Fundo Petrolifero Economia,Informes sobre el fondo soberano de Timor Oriental.,https://petroleumfund.tl/feed/,4,Economía,176,Timor Oriental,en,False,30 +8620,Petroleo e Gas Timor Economia,Gestion de recursos petroliferos y fondos soberanos.,https://petroleum.tl/feed/,4,Economía,176,Timor Oriental,en,False,30 +8621,Turismo Timor Economia,Desarrollo del turismo e industria hotelera.,https://turismo.tl/feed/,4,Economía,176,Timor Oriental,pt,False,30 +8612,Dili Weekly Politica,Semanal con enfoque en politica y desarrollo nacional.,https://diliweekly.com/feed/,11,Política,176,Timor Oriental,en,False,30 +8613,GMN TV Politica,Canal de television con noticias y debates politicos.,https://gmntv.tl/feed/,11,Política,176,Timor Oriental,te,False,30 +8611,Jornal Nacional Politica,Periodico con analisis politico y noticias nacionales.,https://jornalnacional.tl/feed/,11,Política,176,Timor Oriental,pt,False,30 +8615,La'o Hamutuk Politica,Instituto de monitoreo y analisis de politicas publicas.,https://laohamutuk.org/feed/,11,Política,176,Timor Oriental,en,False,30 +8617,Parlamento Nacional Politica,Noticias y actividades del parlamento nacional.,https://parlamento.tl/feed/,11,Política,176,Timor Oriental,pt,False,30 +8616,Presidencia RDTL Politica,Comunicados y noticias de la presidencia de la republica.,https://presidenciarepublica.tl/feed/,11,Política,176,Timor Oriental,pt,True,0 +8614,RTP Timor Politica,Cobertura de la radio television portuguesa en Timor.,https://rtp.pt/timor/feed/,11,Política,176,Timor Oriental,pt,False,30 +8610,Suara Timor Lorosae Politica,Diario lider con cobertura politica y social.,https://stl.news/feed/,11,Política,176,Timor Oriental,pt,True,0 +8609,Tatoli Agencia Estatal Politica,Agencia oficial de noticias del gobierno de Timor-Leste.,https://tatoli.tl/feed/,11,Política,176,Timor Oriental,pt,True,0 +8608,Timor Post Politica,Noticias politicas y gubernamentales de Timor Oriental.,https://timorpost.tl/feed/,11,Política,176,Timor Oriental,pt,False,30 +8632,Ciberseguranca Timor Tecnologia,Seguridad informatica y proteccion de datos.,https://ciberseguranca.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8625,Comunicacoes Timor Tecnologia,Servicios de telecomunicaciones y conectividad.,https://comunicacoes.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8631,E-Gov Timor Tecnologia,Gobierno electronico y servicios publicos digitales.,https://egov.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8630,Energia Renovavel Tech Tecnologia,Tecnologia aplicada a energias renovables.,https://energiatech.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8626,ICT Timor-Leste Tecnologia,Politicas y proyectos de tecnologias de la informacion.,https://ict.gov.tl/feed/,14,Tecnología,176,Timor Oriental,en,False,30 +8628,Radio Comunitaria Digital Tecnologia,Medios digitales y tecnologia comunitaria.,https://rcdigital.tl/feed/,14,Tecnología,176,Timor Oriental,te,False,30 +8629,Satelite Timor Tecnologia,Infraestructura satelital y comunicaciones.,https://satelite.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8627,Tech Innovacao Timor Tecnologia,Innovacion tecnologica y emprendimiento.,https://techinnov.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8624,Tecno Timor Tecnologia,Noticias y actualizaciones sobre tecnologia en Timor Oriental.,https://tecnomotor.tl/feed/,14,Tecnología,176,Timor Oriental,pt,False,30 +8595,24 Heures au Bénin - Togo,Portal regional que cubre Benin y Togo. Noticias politicas.,https://www.24haubenin.info/togo/feed/,11,Política,177,Togo,fr,False,30 +8596,Africa Top Success - Togo,Portal panafricano. Noticias generales y politicas de Togo.,https://www.africatopsuccess.com/togo/feed/,11,Política,177,Togo,fr,False,30 +8602,Alliance Nationale pour le Changement (ANC),Partido de oposicion historico.,https://anctogo.tg/feed/,11,Política,177,Togo,fr,False,30 +8598,Assemblée Nationale Togolaise,Parlamento de Togo. Noticias legislativas.,https://assemblee-nationale.tg/feed/,11,Política,177,Togo,fr,False,30 +8605,BBC News Afrique - Togo,Seccion de Togo de la BBC en frances.,https://www.bbc.com/afrique/topics/cwr9jrrvrg2t/rss,11,Política,177,Togo,fr,False,30 +4108,France 24 - Afrique,Noticias africanas de France 24. Cobertura ocasional de Togo.,https://www.france24.com/fr/afrique/rss,11,Política,177,Togo,fr,True,0 +8606,Jeune Afrique - Togo,Revista panafricana. Analisis politico y economico.,https://www.jeuneafrique.com/tag/togo/feed/,11,Política,177,Togo,fr,False,30 +8594,Liberté Togo,Periodico privado de oposicion. Analisis politico critico.,https://liberte-togo.com/feed/,11,Política,177,Togo,fr,False,30 +8601,Parti National Panafricain (PNP),Principal partido de oposicion. Comunicados y actividades.,https://pnp.tg/feed/,11,Política,177,Togo,fr,False,30 +8599,Primature Togo,Oficina del Primer Ministro. Comunicados y noticias de gobierno.,https://primature.gouv.tg/feed/,11,Política,177,Togo,fr,False,30 +8603,RFI Afrique - Togo,Seccion de Togo de Radio France Internationale. Cobertura regional francesa.,https://www.rfi.fr/fr/tag/togo/feed/,11,Política,177,Togo,fr,False,30 +8593,Radio Lomé,Radio estatal. Noticias y programas de actualidad.,https://www.radiolome.tg/feed/,11,Política,177,Togo,fr,False,30 +8607,The Africa Report - Togo,Revista de analisis politico y economico del continente.,https://www.theafricareport.com/country/togo/feed/,11,Política,177,Togo,en,False,30 +8591,Togo Presse,Agencia de noticias oficial del Estado. Noticias politicas y gubernamentales.,https://www.togopresse.tg/feed/,11,Política,177,Togo,fr,True,0 +8597,Togosite,Portal de noticias y agregador de contenido local.,https://www.togosite.com/feed/,11,Política,177,Togo,fr,True,0 +8592,Télévision Togolaise (TVT),Television estatal. Noticias nacionales.,https://www.tvt.tg/feed/,11,Política,177,Togo,fr,False,30 +8600,Union pour la République (UNIR),Partido en el poder. Comunicados y noticias.,https://unir.tg/feed/,11,Política,177,Togo,fr,True,0 +8604,VOA Afrique - Togo,Seccion de Togo de la Voz de America. Perspectiva estadounidense.,https://www.voaafrique.com/z/1759/rss.xml,11,Política,177,Togo,fr,False,30 +8542,Matangi Tonga Online - Environment,Seccion de medio ambiente y clima del principal portal.,https://matangitonga.to/taxonomy/term/13/feed,1,Ciencia,178,Tonga,en,False,30 +8543,Ministry of Health (Tonga),Noticias oficiales sobre salud publica (si existe feed).,https://www.health.gov.to/rss,1,Ciencia,178,Tonga,en,False,30 +7753,NASA - Earth Observatory,Imagenes e investigacion sobre la Tierra (region del Pacifico).,https://earthobservatory.nasa.gov/feeds/rss,1,Ciencia,178,Tonga,en,False,30 +7550,Nature - News,Noticias cientificas globales de la revista Nature.,https://www.nature.com/nature.rss,1,Ciencia,178,Tonga,en,True,0 +7872,New Scientist - News,Noticias de ciencia y tecnologia.,https://www.newscientist.com/feed/home,1,Ciencia,178,Tonga,en,True,0 +7870,Science - News,Noticias de la revista Science.,https://www.science.org/rss/news.xml,1,Ciencia,178,Tonga,en,False,30 +1301,ScienceDaily - All News,Agregador global de los ultimos hallazgos cientificos.,https://www.sciencedaily.com/rss/all.xml,1,Ciencia,178,Tonga,en,True,0 +1303,Scientific American - News,Noticias de la revista de divulgacion cientifica.,https://www.scientificamerican.com/rss/,1,Ciencia,178,Tonga,en,False,30 +8544,Secretariat of the Pacific Regional Environment Programme (SPREP),Agencia regional ambiental. Noticias cientificas sobre el Pacifico.,https://www.sprep.org/rss,1,Ciencia,178,Tonga,en,False,30 +8546,UNESCO - Pacific,Noticias de la UNESCO sobre ciencia y educacion en la region.,https://www.unesco.org/en/countries/to/rss,1,Ciencia,178,Tonga,en,False,30 +8545,World Health Organization (WHO) - Western Pacific,Noticias de salud publica e investigacion para la region.,https://www.who.int/westernpacific/news/rss/en/,1,Ciencia,178,Tonga,en,False,30 +8553,Asian Development Bank (ADB) - Tonga,Proyectos de financiacion y analisis economico.,https://www.adb.org/countries/tonga/rss,4,Economía,178,Tonga,en,False,30 +8555,IMF - Tonga,Evaluaciones economicas del Fondo Monetario Internacional.,https://www.imf.org/en/Countries/TON/rss,4,Economía,178,Tonga,en,False,30 +8548,Kaniva Tonga - Business,Noticias economicas y de desarrollo.,https://kanivatonga.nz/category/business/feed/,4,Economía,178,Tonga,en,False,30 +8547,Matangi Tonga Online - Business,Seccion de negocios y economia del principal portal local.,https://matangitonga.to/taxonomy/term/12/feed,4,Economía,178,Tonga,en,False,30 +8550,Ministry of Finance (Tonga),Presupuesto y politica fiscal (si existe feed).,https://www.finance.gov.to/rss,4,Economía,178,Tonga,en,False,30 +8551,Ministry of Trade and Economic Development,Comercio y desarrollo economico (si existe feed).,https://www.trade.gov.to/rss,4,Economía,178,Tonga,en,False,30 +8549,National Reserve Bank of Tonga,Banco central. Informes y politicas monetarias (si existe feed).,https://www.nrbt.gov.to/rss,4,Economía,178,Tonga,en,False,30 +2229,Pacific Islands Forum Secretariat,Noticias sobre cooperacion economica regional en el Pacifico.,https://www.forumsec.org/feed/,4,Economía,178,Tonga,en,False,30 +8552,Tonga Chamber of Commerce and Industry,Gremio empresarial. Noticias del sector privado (si existe feed).,https://www.tongachamber.org/rss,4,Economía,178,Tonga,en,False,30 +8554,World Bank - Tonga,Informes y datos del Banco Mundial.,https://www.worldbank.org/en/country/tonga/rss,4,Economía,178,Tonga,en,False,30 +8431,Caribbean Industrial Research Institute (CARIRI),Investigacion aplicada e innovacion para la industria.,https://www.cariri.com/rss,1,Ciencia,179,Trinidad y Tobago,en,True,0 +8429,Caribbean Public Health Agency (CARPHA),Agencia regional de salud publica. Alertas e investigacion en salud.,https://carpha.org/Media/RSS-Feed,1,Ciencia,179,Trinidad y Tobago,en,False,30 +8430,Institute of Marine Affairs (IMA),Investigacion en ciencias marinas y costeras.,https://www.ima.gov.tt/rss,1,Ciencia,179,Trinidad y Tobago,en,False,30 +1302,NASA - News,Noticias de la agencia espacial estadounidense.,https://www.nasa.gov/rss/dyn/breaking_news.rss,1,Ciencia,179,Trinidad y Tobago,en,True,0 +8428,The University of the West Indies (UWI) St. Augustine - News,Principal institucion de investigacion del Caribe. Noticias cientificas.,https://www.uwi.edu/news/rss,1,Ciencia,179,Trinidad y Tobago,en,False,30 +8432,Trinidad and Tobago Astronomical Society (TTAS),Sociedad de astronomia. Eventos y noticias cientificas.,https://www.astrotnt.com/rss,1,Ciencia,179,Trinidad y Tobago,en,False,30 +8433,UNESCO - Caribbean,Noticias de la UNESCO sobre ciencia y educacion en la region.,https://www.unesco.org/en/countries/tt/rss,1,Ciencia,179,Trinidad y Tobago,en,False,30 +8421,American Chamber of Commerce of Trinidad and Tobago (AMCHAM T&T),Camara de comercio con EE.UU. Enfoque en inversion y comercio.,https://www.amchamtt.com/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +7514,Bloomberg - Latin America,Noticias economicas y financieras de la region.,https://www.bloomberg.com/latinamerica/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8425,Caribbean Development Bank (CDB),Banco de desarrollo regional. Proyectos y analisis.,https://www.caribank.org/news/rss-feed,4,Economía,179,Trinidad y Tobago,en,False,30 +8422,Energy Chamber of Trinidad and Tobago,Gremio del sector energetico. Noticias tecnicas y de negocios.,https://www.energy.tt/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8423,IMF - Trinidad and Tobago,Evaluaciones economicas y consultas del FMI.,https://www.imf.org/en/Countries/TTO/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8424,Inter-American Development Bank (IDB) - Trinidad and Tobago,Proyectos de financiacion y analisis del BID.,https://www.iadb.org/en/country/trinidad-and-tobago/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8427,Oxford Business Group - Trinidad and Tobago,Informes y analisis sobre el clima de negocios e inversion.,https://oxfordbusinessgroup.com/trinidad-and-tobago/rss,4,Economía,179,Trinidad y Tobago,en,True,0 +7835,Reuters - Business,Noticias financieras globales.,https://www.reuters.com/business/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8426,The Economist - The Americas,Analisis economico y politico de la region.,https://www.economist.com/the-americas/rss.xml,4,Economía,179,Trinidad y Tobago,en,True,0 +8416,Trinidad Express - Business,Principal diario. Seccion de negocios y economia.,https://trinidadexpress.com/news/business/feed/,4,Economía,179,Trinidad y Tobago,en,False,30 +8417,Trinidad Guardian - Business,Diario rival. Noticias economicas y financieras.,https://www.guardian.co.tt/business/feed,4,Economía,179,Trinidad y Tobago,en,False,30 +8420,Trinidad and Tobago Chamber of Industry and Commerce (TTCIC),Principal camara empresarial. Noticias y posiciones del sector privado.,https://chamber.org.tt/rss,4,Economía,179,Trinidad y Tobago,en,True,0 +8418,Trinidad and Tobago Newsday - Business,Tercer diario. Cobertura de negocios.,https://newsday.co.tt/category/business/feed/,4,Economía,179,Trinidad y Tobago,en,True,0 +8419,Trinidad and Tobago Stock Exchange (TTSE),Bolsa de valores. Datos del mercado y noticias corporativas.,https://www.stockex.co.tt/rss,4,Economía,179,Trinidad y Tobago,en,False,30 +8389,Al Jazeera - Caribbean,Ocasional cobertura de temas caribeños.,https://www.aljazeera.com/tag/caribbean/rss,11,Política,179,Trinidad y Tobago,en,False,30 +8388,BBC News - Caribbean,Seccion Caribe de la BBC. Contexto regional e internacional.,https://www.bbc.com/news/world/latin_america/rss,11,Política,179,Trinidad y Tobago,en,False,30 +8377,CNC3 Television,Principal canal de television privada. Noticias nacionales.,https://www.cnc3.co.tt/rss,11,Política,179,Trinidad y Tobago,en,True,0 +8386,Caribbean Media Corporation (CMC),Agencia de noticias del Caribe. Noticias regionales.,https://cmc.bb/feed/,11,Política,179,Trinidad y Tobago,en,False,30 +8385,Caribbean News Global,Portal de noticias regional. Cobertura politica del Caribe.,https://caribbeannewsglobal.com/category/caribbean/feed/,11,Política,179,Trinidad y Tobago,en,False,30 +8390,Carnegie Endowment for International Peace - Global,Analisis de expertos sobre democracia y gobernanza.,https://carnegieendowment.org/rss/global?lang=en,11,Política,179,Trinidad y Tobago,en,False,30 +8387,Jamaica Observer - Caribbean,Seccion Caribe de un diario regional. Incluye cobertura de T&T.,https://www.jamaicaobserver.com/category/caribbean/feed/,11,Política,179,Trinidad y Tobago,en,True,0 +8376,Loop Trinidad & Tobago - News,Portal de noticias del Caribe. Cobertura politica local.,https://tt.loopnews.com/category/politics/feed,11,Política,179,Trinidad y Tobago,en,False,30 +8382,Ministry of Foreign and CARICOM Affairs,Comunicados de politica exterior e integracion regional.,https://foreign.gov.tt/rss,11,Política,179,Trinidad y Tobago,en,False,30 +8380,Office of the Prime Minister,Comunicados oficiales y anuncios del Primer Ministro.,https://www.opm.gov.tt/rss,11,Política,179,Trinidad y Tobago,en,True,0 +8381,Parliament of Trinidad and Tobago,Noticias legislativas y actividad parlamentaria.,https://www.ttparliament.org/rss,11,Política,179,Trinidad y Tobago,en,False,30 +8383,People's National Movement (PNM),Partido de gobierno. Comunicados y noticias.,https://www.pnm.org.tt/rss,11,Política,179,Trinidad y Tobago,en,False,30 +3541,Reuters - World,Noticias mundiales con cobertura de la region.,https://www.reuters.com/world/rss,11,Política,179,Trinidad y Tobago,en,False,30 +3062,The Diplomat - Global Politics,Analisis de politica internacional que ocasionalmente cubre el Caribe.,https://thediplomat.com/feed/,11,Política,179,Trinidad y Tobago,en,True,0 +8373,Trinidad Express - Politics,Principal diario matutino. Cobertura politica nacional.,https://trinidadexpress.com/news/politics/feed/,11,Política,179,Trinidad y Tobago,en,False,30 +8374,Trinidad Guardian - Politics,Principal diario rival. Noticias y analisis politicos.,https://www.guardian.co.tt/politics/feed,11,Política,179,Trinidad y Tobago,en,False,30 +8375,Trinidad and Tobago Newsday - Politics,Tercer diario nacional. Enfoque en noticias politicas.,https://newsday.co.tt/category/news/politics/feed/,11,Política,179,Trinidad y Tobago,en,True,0 +8384,United National Congress (UNC),Principal partido de oposicion. Comunicados y noticias.,https://www.unc.org.tt/rss,11,Política,179,Trinidad y Tobago,en,False,30 +7949,BBC News - World,Noticias mundiales con cobertura de temas sociales globales.,https://www.bbc.com/news/world/rss,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8501,CAF - Development Bank of Latin America,Noticias sobre proyectos de desarrollo social e infraestructura en la region.,https://www.caf.com/en/rss/,13,Sociedad,179,Trinidad y Tobago,en,True,0 +8498,CNC3 Television - News,Canal de television. Noticias nacionales y reportajes sociales.,https://www.cnc3.co.tt/rss/news,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8497,Loop Trinidad & Tobago - Community,Portal de noticias. Seccion comunitaria y social.,https://tt.loopnews.com/category/community/feed,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8434,Pan American Health Organization (PAHO) - Trinidad and Tobago,Noticias sobre salud publica y bienestar.,https://www.paho.org/en/trinidad-and-tobago/rss,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8378,Power 102.1 FM - News,Radio de noticias. Cobertura de temas sociales y actualidad.,https://power102fm.com/category/news/feed/,13,Sociedad,179,Trinidad y Tobago,en,True,0 +7948,The Guardian - Global Development,Noticias internacionales sobre desarrollo y sociedad.,https://www.theguardian.com/global-development/rss,13,Sociedad,179,Trinidad y Tobago,en,True,0 +8500,The UWI St. Augustine - Community Outreach,Noticias de la universidad sobre proyectos de vinculacion con la sociedad.,https://sta.uwi.edu/news/rss/community,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8494,Trinidad Express - News,Seccion de noticias generales del principal diario.,https://trinidadexpress.com/news/feed/,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8495,Trinidad Guardian - News,Noticias nacionales y de interes general.,https://www.guardian.co.tt/news/feed,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8496,Trinidad and Tobago Newsday - News,Tercer diario. Noticias de actualidad y sociedad.,https://newsday.co.tt/category/news/feed/,13,Sociedad,179,Trinidad y Tobago,en,True,0 +8499,Trinidad and Tobago Police Service (TTPS),Comunicados oficiales sobre seguridad ciudadana y noticias policiales.,https://www.ttps.gov.tt/rss,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8502,UNDP Trinidad and Tobago,Noticias sobre proyectos de desarrollo humano y lucha contra la pobreza.,https://www.tt.undp.org/rss.xml,13,Sociedad,179,Trinidad y Tobago,en,False,30 +6288,UNICEF Eastern Caribbean Area,Noticias sobre programas de proteccion de la infancia y educacion en el Caribe Oriental.,https://www.unicef.org/easterncaribbean/rss.xml,13,Sociedad,179,Trinidad y Tobago,en,False,30 +8379,i95.5 FM - News,Radio con programacion de noticias y entrevistas.,https://www.i955fm.com/category/news/feed/,13,Sociedad,179,Trinidad y Tobago,en,False,30 +7888,Ars Technica,Noticias de tecnologia y ciencia en profundidad.,https://arstechnica.com/feed/,14,Tecnología,179,Trinidad y Tobago,en,True,0 +8493,Caribbean Export - Innovation,Noticias sobre innovacion y competitividad en empresas caribenhas.,https://www.carib-export.com/rss/innovation/,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8490,Caribbean Network Operators Group (CaribNOG),Comunidad tecnica de internet en el Caribe. Noticias y eventos.,https://www.caribnog.org/rss,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8488,Digicel Trinidad and Tobago,Operador movil y de servicios digitales.,https://www.digicelgroup.com/tt/en/news/rss.html,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8489,Flow Trinidad and Tobago,Operador de telecomunicaciones (Liberty Latin America).,https://www.discoverflow.co/trinidad/news/rss,14,Tecnología,179,Trinidad y Tobago,en,False,30 +3095,MIT Technology Review - News,Noticias sobre tecnologias emergentes y su impacto.,https://www.technologyreview.com/feed/,14,Tecnología,179,Trinidad y Tobago,en,True,0 +8486,National Information and Communications Technology Company Limited (iGovTT),Empresa estatal de servicios de gobierno electronico.,https://www.igov.tt/rss,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8487,TSTT (Telecommunications Services of Trinidad and Tobago),Principal operador de telecomunicaciones (parcialmente estatal).,https://www.tstt.co.tt/rss,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8492,Tech Beach Retreat - News,Comunidad y evento de tecnologia del Caribe. Noticias e insights.,https://techbeachretreat.com/feed/,14,Tecnología,179,Trinidad y Tobago,en,False,30 +1501,TechCrunch - Startups,Noticias globales sobre startups y capital de riesgo.,https://techcrunch.com/feed/,14,Tecnología,179,Trinidad y Tobago,en,True,0 +8485,Trinidad Express - Technology,Seccion de tecnologia del principal diario.,https://trinidadexpress.com/news/technology/feed/,14,Tecnología,179,Trinidad y Tobago,en,False,30 +8491,UWI FinTech Innovation Hub,Hub de innovacion en tecnologia financiera de la Universidad de West Indies.,https://fintechhub.uwi.edu/rss,14,Tecnología,179,Trinidad y Tobago,en,False,30 +1503,WIRED - Business,Noticias sobre el impacto de la tecnologia en los negocios y la sociedad.,https://www.wired.com/feed/rss,14,Tecnología,179,Trinidad y Tobago,en,True,0 +8188,Academy of Sciences of Turkmenistan,Sitio oficial de la academia estatal. Noticias sobre investigacion (si existe feed).,http://science.gov.tm/rss,1,Ciencia,181,Turkmenistán,tk,False,30 +8190,Chronicles of Turkmenistan - Society,Noticias independientes que ocasionalmente cubren temas de educacion y medio ambiente.,https://www.chrono-tm.org/ru/rss/society/,1,Ciencia,181,Turkmenistán,ru,False,30 +8189,TDH - Science & Education,Agencia estatal. Escasas noticias oficiales sobre logros cientificos o educativos.,http://tdh.gov.tm/rss/science,1,Ciencia,181,Turkmenistán,tk,False,30 +8067,World Health Organization (WHO) - Europe,Noticias de salud publica para la region europea.,https://www.who.int/europe/news/rss/en/,1,Ciencia,181,Turkmenistán,en,False,30 +8136,Asian Development Bank (ADB) - Turkmenistan,Informacion sobre proyectos de desarrollo (limitada).,https://www.adb.org/countries/turkmenistan/rss,4,Economía,181,Turkmenistán,en,False,30 +8137,BP Statistical Review of World Energy,Datos clave sobre reservas y produccion de gas/petroleo (Turkmenistan es clave).,https://www.bp.com/en/global/corporate/energy-economics/statistical-review-of-world-energy/rss.xml,4,Economía,181,Turkmenistán,en,False,30 +8130,Central Bank of Turkmenistan,Banco central. Comunicados oficiales (si existe feed RSS).,https://www.cbt.tm/rss,4,Economía,181,Turkmenistán,tk,False,30 +8133,Eurasianet - Economia,Analisis regional del impacto economico de los hidrocarburos.,https://eurasianet.org/turkmenistan/rss?tag=Economy,4,Economía,181,Turkmenistán,en,False,30 +8134,International Monetary Fund (IMF),Reportes y evaluaciones ocasionales sobre Turkmenistan.,https://www.imf.org/en/Countries/TKM/rss,4,Economía,181,Turkmenistán,en,False,30 +8132,Ministry of Finance,Economia publica y presupuesto (si existe feed).,https://www.minfin.gov.tm/rss,4,Economía,181,Turkmenistán,tk,False,30 +8129,Neutral Turkmenistan - Economy,Periodico oficial en ingles. Noticias economicas del gobierno.,http://turkmenistan.gov.tm/rss/economy,4,Economía,181,Turkmenistán,en,False,30 +8131,State Committee for Statistics,Indicadores economicos oficiales (si existe feed).,https://www.stat.gov.tm/rss,4,Economía,181,Turkmenistán,tk,False,30 +8128,TDH - Economia,Agencia estatal de noticias. Noticias oficiales sobre economia.,http://tdh.gov.tm/rss/economy,4,Economía,181,Turkmenistán,tk,False,30 +8135,World Bank - Europe & Central Asia,Proyectos y datos economicos regionales (poca cobertura especifica).,https://www.worldbank.org/en/region/eca/rss,4,Economía,181,Turkmenistán,en,False,30 +8123,Chronicles of Turkmenistan (ATN),Servicio de noticias en el exilio. Principal fuente de informacion independiente (bloqueada en el pais).,https://www.chrono-tm.org/ru/rss/,11,Política,181,Turkmenistán,ru,False,30 +8122,Neutral Turkmenistan Newspaper,Periodico oficial del gobierno. Version en ingles de las noticias estatales.,http://turkmenistan.gov.tm/rss,11,Política,181,Turkmenistán,en,False,30 +8126,OSCE Centre in Ashgabat,Noticias sobre actividades y reportes de la OSCE en Turkmenistan (enfasis en derechos humanos y seguridad).,https://www.osce.org/centre-in-ashgabat/rss,11,Política,181,Turkmenistán,en,False,30 +8124,RFE/RL's Turkmen Service (Azatlyk),Servicio en turkmeno de Radio Free Europe/Radio Liberty. Fuente independiente critica.,https://www.azathabar.com/rss,11,Política,181,Turkmenistán,tk,False,30 +8121,TDH - Turkmenistan State News Agency,"Agencia de noticias oficial del Estado. Unica fuente interna de noticias ""politicas"".",http://tdh.gov.tm/rss,11,Política,181,Turkmenistán,tk,False,30 +8125,The Diplomat - Central Asia,Analisis de politica y seguridad en la region.,https://thediplomat.com/tag/turkmenistan/feed/,11,Política,181,Turkmenistán,en,True,0 +8127,UN News - Asia Pacific,Noticias de la ONU que ocasionalmente cubren a Turkmenistan.,https://news.un.org/en/news/region/asia-pacific/rss,11,Política,181,Turkmenistán,en,False,30 +8223,Access Now - News,Noticias sobre derechos digitales y ciberseguridad a nivel global.,https://www.accessnow.org/rss/news/,14,Tecnología,181,Turkmenistán,en,False,30 +8219,Altyn Asyr Mobile,Operador movil estatal. Noticias (si existe feed).,http://www.altynasyr.tm/rss,14,Tecnología,181,Turkmenistán,tk,False,30 +8220,Chronicles of Turkmenistan - Technology,Noticias independientes sobre acceso a internet y control digital.,https://www.chrono-tm.org/ru/rss/technology/,14,Tecnología,181,Turkmenistán,ru,False,30 +8222,Eurasianet - Digital,Analisis sobre el entorno digital y las telecomunicaciones en Asia Central.,https://eurasianet.org/turkmenistan/rss?tag=Digital,14,Tecnología,181,Turkmenistán,en,False,30 +8224,Internet Shutdowns - Feed,Monitoreo global de cortes de internet (incluye potencialmente a Turkmenistan).,https://internetshutdowns.rss.acl.app/,14,Tecnología,181,Turkmenistán,en,False,30 +8217,Ministry of Communications,Tecnologias de la informacion y telecomunicaciones (si existe feed).,http://mincom.gov.tm/rss,14,Tecnología,181,Turkmenistán,tk,False,30 +8221,RFE/RL Turkmen Service - Digital Rights,Reportes sobre libertades digitales y censura en internet.,https://www.azathabar.com/rss/digital,14,Tecnología,181,Turkmenistán,tk,False,30 +8216,TDH - Technology,Agencia estatal. Noticias oficiales sobre tecnologia y comunicaciones.,http://tdh.gov.tm/rss/technology,14,Tecnología,181,Turkmenistán,tk,False,30 +1502,The Verge - Tech,Noticias y analisis de tecnologia.,https://www.theverge.com/rss/index.xml,14,Tecnología,181,Turkmenistán,en,True,0 +8218,Turkmentelekom,Operador estatal de telecomunicaciones. Noticias de servicios (si existe feed).,http://www.turkmentelecom.tm/rss,14,Tecnología,181,Turkmenistán,tk,False,30 +8225,Wired - Security,Noticias sobre ciberseguridad y vigilancia.,https://www.wired.com/feed/category/security/latest/rss,14,Tecnología,181,Turkmenistán,en,True,0 +8117,Avrupa Uzay Ajansi (ESA) Turkce,ESA'nin Turkce haber ve goruntuleri.,https://www.esa.int/esa-mmg/mmg.pl?type=I&language=Turkish,1,Ciencia,182,Turquía,tr,False,30 +8065,Bilim ve Gelecek Dergisi,Aylık populer bilim dergisi. Arastirma ve bilim haberleri.,https://www.bilimvegelecek.com.tr/index.php/rss,1,Ciencia,182,Turquía,tr,True,0 +8113,Bilimorg,Topluluk tabanli bilim haberciligi ve tartisma platformu.,https://bilim.org/feed,1,Ciencia,182,Turquía,tr,True,0 +8063,Bogazici Universitesi,Universite arastirma ve bilim haberleri.,https://www.boun.edu.tr/tr_TR/Content/Genel/RSS,1,Ciencia,182,Turquía,tr,False,30 +8114,Istanbul Universitesi,Universitenin bilimsel arastirma haberleri.,https://www.istanbul.edu.tr/tr/_/rss/rss-yayin/1,1,Ciencia,182,Turquía,tr,False,30 +8061,Istanbul University,Universidad mas antigua. Noticias de investigacion de sus facultades cientificas.,https://www.istanbul.edu.tr/tr/_/rss,1,Ciencia,182,Turquía,tr,False,30 +8116,NASA Turkce (NASA Earth Observatory),NASA Dunya Gozlemevi goruntuleri ve aciklamalari (Turkce).,https://earthobservatory.nasa.gov/feeds/rss?tl=turkish,1,Ciencia,182,Turquía,tr,False,30 +8062,Orta Dogu Teknik Universitesi (ODTU),Teknik ve bilimsel arastirma ciktilari.,https://www.metu.edu.tr/tr/rss,1,Ciencia,182,Turquía,tr,False,30 +8066,Popular Science Turkiye,Populer bilim ve teknoloji dergisinin Turkce edisyonu.,https://www.popsci.com.tr/feed/,1,Ciencia,182,Turquía,tr,True,1 +8064,Science Journals of Turkiye (Sciendo),Portal de revistas cientificas turcas.,https://content.sciendo.com/rss,1,Ciencia,182,Turquía,en,False,30 +8115,Scientific and Technical Research Council (TUBITAK) Journals,Tubitak dergilerindeki yeni yayinlar.,https://journals.tubitak.gov.tr/rss.php,1,Ciencia,182,Turquía,en,False,30 +8057,Scientific and Technological Research Council of Turkiye (TUBITAK),Principal institucion de investigacion y financiacion cientifica.,https://www.tubitak.gov.tr/tr/rss,1,Ciencia,182,Turquía,tr,False,30 +8112,Tubitak Bilim Genel,Turkiye Bilimsel ve Teknolojik Arastirma Kurumu haberleri.,https://www.tubitak.gov.tr/tr/rss/rss-yayin/113,1,Ciencia,182,Turquía,tr,False,30 +8058,Turkiye Bilimler Akademisi (TUBA),Akademinin etkinlik ve yayin duyurulari.,https://www.tuba.gov.tr/tr/rss,1,Ciencia,182,Turquía,tr,False,30 +8060,Turkiye Uzay Ajansi (TUA),Ulusal uzay programi ve arastirma projeleri.,https://www.tua.gov.tr/tr/rss,1,Ciencia,182,Turquía,tr,True,0 +8059,Yuksekogretim Kurulu (YOK),Universitelerdeki bilimsel arastirma politikalarina dair haberler.,https://www.yok.gov.tr/tr/rss,1,Ciencia,182,Turquía,tr,False,30 +8030,Al-Monitor - Turkey Economy,Analisis especializado en la economia turca.,https://www.al-monitor.com/originals/rss/turkey-economy.xml,4,Economía,182,Turquía,en,False,30 +8018,Anadolu Agency - Economy,Agencia estatal. Noticias economicas oficiales.,https://www.aa.com.tr/en/rss/default?cat=economy,4,Economía,182,Turquía,en,True,0 +8021,Bloomberg HT,Principal canal de television financiera y portal de noticias.,https://www.bloomberght.com/rss,4,Economía,182,Turquía,tr,True,0 +8024,Borsa Istanbul (BIST),Bolsa de valores de Estambul. Datos del mercado y noticias corporativas.,https://www.borsaistanbul.com/rss,4,Economía,182,Turquía,tr,False,30 +8020,Daily Sabah - Economy,Diario progubernamental en ingles. Cobertura economica.,https://www.dailysabah.com/business/economy/rss,4,Economía,182,Turquía,en,False,30 +8022,Dunya Gazetesi,Principal periodico economico de Turquia.,https://www.dunya.com/rss,4,Economía,182,Turquía,tr,True,0 +8023,Ekonomim,Portal de noticias economicas y financieras.,https://www.ekonomim.com/rss,4,Economía,182,Turquía,tr,True,0 +8029,European Bank for Reconstruction and Development (EBRD) - Turkiye,Financiacion para proyectos del sector privado.,https://www.ebrd.com/rss/where-we-work/turkiye.rss,4,Economía,182,Turquía,en,False,30 +8019,Hurriyet Daily News - Economy,Principal diario en ingles. Noticias economicas y de negocios.,https://www.hurriyetdailynews.com/rss.aspx?cat=Economy,4,Economía,182,Turquía,en,True,0 +8028,IMF - Turkiye,Evaluaciones economicas y consultas del Fondo Monetario Internacional.,https://www.imf.org/en/Countries/TUR/rss,4,Economía,182,Turquía,en,False,30 +8027,MUSIAD (Independent Industrialists and Businessmen Association),Asociacion de negocios de orientacion conservadora/islamica.,https://www.musiad.org.tr/tr/rss,4,Economía,182,Turquía,tr,False,30 +8026,TUSIAD (Turkish Industry and Business Association),Asociacion de grandes empresas. Analisis y reportes economicos.,https://tusiad.org/tr/rss,4,Economía,182,Turquía,tr,False,30 +8031,The Economist - Finance & Economics,Analisis economico y financiero global.,https://www.economist.com/finance-and-economics/rss.xml,4,Economía,182,Turquía,en,True,0 +8025,Union of Chambers and Commodity Exchanges of Turkiye (TOBB),Principal gremio empresarial. Noticias y posiciones del sector privado.,https://www.tobb.org.tr/rss,4,Economía,182,Turquía,tr,False,30 +8090,BBC News Turkce,BBC'nin Turkce servisi. Uluslararasi ve Turkiye haberleri.,https://www.bbc.com/turkce/rss.xml,7,Internacional,182,Turquía,tr,False,30 +8091,Deutsche Welle (DW) Turkce,Alman yayın kurulusunun Turkce servisi.,https://www.dw.com/tr/rdf/rss-tur-all,7,Internacional,182,Turquía,tr,False,30 +8089,Euro News Turkiye,Avrupa merkezli haber kanalinin Turkce servisi.,https://tr.euronews.com/rss,7,Internacional,182,Turquía,tr,True,0 +8088,Sputnik Turkiye,Rusya merkezli haber ajansinin Turkce servisi.,https://tr.sputniknews.com/export/rss2/archive/index.xml,7,Internacional,182,Turquía,tr,True,0 +7977,Anadolu Agency (AA),Agencia de noticias estatal de Turquia. Noticias oficiales y gubernamentales.,https://www.aa.com.tr/en/rss/default?cat=general,11,Política,182,Turquía,en,True,0 +8074,Anadolu Ajansi (AA),Devletin resmi haber ajansi. Tum haber kategorileri.,https://www.aa.com.tr/tr/rss/default?cat=gundem,11,Política,182,Turquía,tr,True,0 +7988,BBC News - Turkey,Seccion de Turquia de la BBC. Analisis y contexto internacional.,https://www.bbc.com/news/world/europe/turkey/rss,11,Política,182,Turquía,en,False,30 +7980,Bianet (Bagimsiz Iletisim Agi),Agencia de noticias independiente con enfoque en derechos humanos y sociedad civil.,https://bianet.org/english/rss,11,Política,182,Turquía,en,True,0 +7985,BirGun,Sol cizgide bagimsiz gazete. Gundem haberleri.,https://www.birgun.net/rss,11,Política,182,Turquía,tr,False,30 +8077,CNN Turk,Ulusal televizyon haber kanali.,https://www.cnnturk.com/feed/rss/all/news,11,Política,182,Turquía,tr,True,0 +7990,Carnegie Europe - Turkey,Analisis de expertos sobre politica turca y relaciones UE-Turquia.,https://carnegieeurope.eu/rss?topic=turkey,11,Política,182,Turquía,en,False,30 +8080,Cumhuriyet,Geleneksel laik ve sol cizgide gazete. Gundem haberleri.,https://www.cumhuriyet.com.tr/rss/gundem.xml,11,Política,182,Turquía,tr,True,0 +7984,Cumhuriyet,Gazete laik ve sol (opositor laico/republicano). Politika haberleri.,https://www.cumhuriyet.com.tr/rss/politika.xml,11,Política,182,Turquía,tr,False,30 +7978,Daily Sabah,Diario en ingles progubernamental. Cobertura politica desde la perspectiva oficial.,https://www.dailysabah.com/rss,11,Política,182,Turquía,en,False,30 +8084,Duvar,Bagimsiz internet gazetesi. Derinlikli haber ve analiz.,https://www.gazeteduvar.com.tr/rss,11,Política,182,Turquía,tr,False,30 +7981,Duvar English,Portal de noticias independiente y de oposicion. Analisis politico profundo.,https://www.duvarenglish.com/rss,11,Política,182,Turquía,en,False,30 +7989,Foreign Policy - Turkey,Analisis en profundidad de la politica exterior turca.,https://foreignpolicy.com/tag/turkey/feed/,11,Política,182,Turquía,en,True,0 +7991,German Institute for International and Security Affairs (SWP) - Turkey,Analisis academico y politico sobre Turquia (en ingles).,https://www.swp-berlin.org/en/rss/turkey,11,Política,182,Turquía,en,False,30 +7986,Grand National Assembly of Turkiye (TBMM),Sitio oficial del Parlamento. Noticias legislativas.,https://www.tbmm.gov.tr/rss/,11,Política,182,Turquía,tr,True,0 +8078,Haberturk,Buyuk ulusal gazete ve haber portalı.,https://www.haberturk.com/rss,11,Política,182,Turquía,tr,True,0 +7979,Hurriyet Daily News,Principal diario en ingles (centro-derecha). Noticias politicas y economicas.,https://www.hurriyetdailynews.com/rss.aspx,11,Política,182,Turquía,en,True,0 +8085,Internet Haber,Populer internet haber portalı.,https://www.internethaber.com/rss,11,Política,182,Turquía,tr,True,0 +7992,Medyascope,Plataforma independiente de video-noticias y analisis (Youtube/Twitter via RSSHub).,https://rsshub.app/youtube/user/medyascopetv,11,Política,182,Turquía,tr,False,30 +8081,Milliyet,Köklü merkez gazete. Son dakika haberleri.,https://www.milliyet.com.tr/rss/rssNew/gundemRss.xml,11,Política,182,Turquía,tr,True,0 +7987,Ministry of Foreign Affairs of Turkiye,Comunicados de politica exterior y diplomacia.,https://www.mfa.gov.tr/rss.xml,11,Política,182,Turquía,en,False,30 +8076,NTV,Populer ulusal televizyon haber kanali.,https://www.ntv.com.tr/gundem.rss,11,Política,182,Turquía,tr,True,0 +8086,OdaTV,Muhalif haber sitesi.,https://www.odatv4.com/rss,11,Política,182,Turquía,tr,True,0 +8082,Sabah,Buyuk pro-iktidar gazete. Son dakika haberleri.,https://www.sabah.com.tr/rss/gundem.xml,11,Política,182,Turquía,tr,True,0 +7983,Sozcu,Gazete laik ve milliyetci (opositor secular/nacionalista). Politika haberleri.,https://www.sozcu.com.tr/rss/politika.xml,11,Política,182,Turquía,tr,True,0 +8079,Sozcu,Muhalif ve laik cizgide gazete. Gundem ve politika haberleri.,https://www.sozcu.com.tr/rss/gundem.xml,11,Política,182,Turquía,tr,True,0 +7982,T24,Bagimsiz internet gazetesi. Gundem ve analiz.,https://t24.com.tr/rss,11,Política,182,Turquía,tr,True,0 +8075,TRT Haber,Devlet televizyonunun haber kanali. Genel haberler.,https://www.trthaber.com/rss/gundem.rss,11,Política,182,Turquía,tr,False,30 +8087,VeryansinTV,Muhalif haber ve yorum sitesi.,https://veryansintv.com/feed,11,Política,182,Turquía,tr,True,0 +8083,Yeni Safak,Pro-iktidar gazete. Gundem ve politika.,https://www.yenisafak.com/rss?type=gundem,11,Política,182,Turquía,tr,True,0 +8098,Bilgi Teknolojileri ve Iletisim Kurumu (BTK),Telekomunikasyon duzenleyicisi. Sektor haberleri.,https://www.btk.gov.tr/tr/rss,14,Tecnología,182,Turquía,tr,False,30 +8095,DonanimHaber,Genis teknoloji toplulugu ve haber sitesi.,https://www.donanimhaber.com/rss,14,Tecnología,182,Turquía,tr,False,30 +8097,Startups.watch,Turkiye girisim ekosistemini izleme platformu.,https://startups.watch/feed/,14,Tecnología,182,Turquía,tr,False,30 +8096,TechnoBlog,Teknoloji haberleri ve urun incelemeleri.,https://www.technoblog.com/feed/,14,Tecnología,182,Turquía,tr,False,30 +8099,Turk Telekom,Altyapı ve teknoloji haberleri.,https://www.turktelekom.com.tr/rss,14,Tecnología,182,Turquía,tr,False,30 +8101,Turkiye Teknoloji Takimi Vakfi (T3 Vakfi),Milli teknoloji hamlesi ve yetenek gelistirme haberleri.,https://t3vakfi.org/feed/,14,Tecnología,182,Turquía,tr,False,30 +8100,Vodafone Turkiye,Operatör haberleri ve teknoloji duyuruları.,https://www.vodafone.com.tr/rss/s/15,14,Tecnología,182,Turquía,tr,False,30 +7874,European Space Agency (ESA) - News,Noticias de la agencia espacial europea.,https://www.esa.int/rssfeed/Our_Activities,1,Ciencia,180,Túnez,en,True,0 +8342,National Center for Nuclear Sciences and Technologies (CNSTN),Investigacion en energia nuclear y sus aplicaciones.,https://www.cnstn.rnrt.tn/rss,1,Ciencia,180,Túnez,fr,False,30 +8343,National Institute of Agronomic Research of Tunisia (INRAT),Investigacion agricola y ciencias de la vida.,https://www.inrat.agrinet.tn/rss,1,Ciencia,180,Túnez,fr,False,30 +8341,Pasteur Institute of Tunis,Principal instituto de investigacion en salud y enfermedades infecciosas.,https://www.pasteur.tn/fr/rss,1,Ciencia,180,Túnez,fr,False,30 +8346,Tunisian Association of Scientific Information (ATIS),Asociacion para la promocion de la cultura cientifica.,https://www.atif.tn/rss,1,Ciencia,180,Túnez,fr,False,30 +8340,Tunisian Society of Medical Sciences (STSM),Sociedad cientifica. Noticias sobre congresos e investigacion medica.,https://www.stsm.org.tn/rss,1,Ciencia,180,Túnez,fr,False,30 +8339,Tunisie Medicale,Revista y portal de noticias medicas y de salud publica.,https://www.tunisieme.com/rss,1,Ciencia,180,Túnez,fr,False,30 +8345,University of Sfax,Importante universidad de investigacion.,https://www.usf.tn/fr/rss,1,Ciencia,180,Túnez,fr,False,30 +8344,University of Tunis El Manar (UTM),Principal universidad. Noticias de investigacion de sus facultades cientificas.,https://www.utm.rnu.tn/fr/rss,1,Ciencia,180,Túnez,fr,False,30 +8338,Webmanagercenter - Sciences,Portal de noticias. Seccion de ciencia e innovacion.,https://www.webmanagercenter.com/rss/sciences/,1,Ciencia,180,Túnez,fr,False,30 +8347,World Health Organization (WHO) - Eastern Mediterranean,Noticias de salud publica e investigacion para la region.,https://www.who.int/eastern-mediterranean/news/rss/en/,1,Ciencia,180,Túnez,en,False,30 +8258,African Development Bank (AfDB) - Tunisia,Proyectos de financiacion y analisis del BAD.,https://www.afdb.org/en/countries/north-africa/tunisia/rss,4,Economía,180,Túnez,en,False,30 +8253,Al Chourouk - Economie,Noticias de negocios y economia.,https://www.alchourouk.com/rss/economie,4,Economía,180,Túnez,ar,False,30 +8252,Assabah News - Economie,Seccion economica del diario popular.,https://www.assabahnews.tn/rss/economie,4,Economía,180,Túnez,ar,False,30 +8257,IMF - Tunisia,Evaluaciones economicas y consultas del FMI.,https://www.imf.org/en/Countries/TUN/rss,4,Economía,180,Túnez,en,False,30 +8255,IlBoursa.com,Portal especializado en mercados financieros y bolsa de Tunez.,https://www.ilboursa.com/rss,4,Economía,180,Túnez,fr,False,30 +8261,Jeune Afrique - Economie,Revista panafricana. Cobertura economica de la region.,https://www.jeuneafrique.com/rss/economie/,4,Economía,180,Túnez,fr,False,30 +8254,Le Temps - Economie,Periodico de referencia. Cobertura economica profunda.,https://www.letemps.com.tn/rss/economie,4,Economía,180,Túnez,fr,False,30 +8260,Oxford Business Group - Tunisia,Analisis e informes sobre el clima de negocios e inversion.,https://oxfordbusinessgroup.com/tunisia/rss,4,Economía,180,Túnez,en,True,0 +8251,TAP - Economie,Agencia oficial. Noticias economicas y financieras.,https://www.tap.info.tn/fr/rss/2/economie,4,Economía,180,Túnez,fr,False,30 +8259,The Economist - Middle East & Africa,Analisis economico y politico de la region.,https://www.economist.com/middle-east-and-africa/rss.xml,4,Economía,180,Túnez,en,True,0 +8256,Tunisian Stock Exchange (BVMT),Bolsa de valores. Datos del mercado y noticias corporativas.,https://www.bvmt.com.tn/rss,4,Economía,180,Túnez,fr,False,30 +8336,Al Jazeera - Tunisie (Societe),Cobertura social y cultural de Tunez.,https://www.aljazeera.net/tag/مجتمع/rss,13,Sociedad,180,Túnez,ar,False,30 +8332,Amnesty International Tunisia,Noticias y campañas sobre derechos humanos en Tunez.,https://www.amnesty.org/en/countries/middle-east-and-north-africa/tunisia/rss/,13,Sociedad,180,Túnez,en,True,0 +8324,Assabah News - Societe,Seccion de sociedad del diario popular.,https://www.assabahnews.tn/rss/societe,13,Sociedad,180,Túnez,ar,False,30 +8337,France 24 - Societe,Noticias sociales y culturales con cobertura magrebi.,https://www.france24.com/fr/societe/rss,13,Sociedad,180,Túnez,fr,False,30 +8333,Human Rights Watch - Tunisia,Informes sobre derechos humanos y justicia social.,https://www.hrw.org/africa/tunisia/rss,13,Sociedad,180,Túnez,en,False,30 +8326,Inkyfada - Societe,Periodismo de investigacion sobre temas sociales y derechos.,https://inkyfada.com/rss/fr/societe/,13,Sociedad,180,Túnez,fr,False,30 +8325,Le Temps - Societe,Periodico de referencia. Reportajes y analisis sociales.,https://www.letemps.com.tn/rss/societe,13,Sociedad,180,Túnez,fr,False,30 +8328,Mosaique FM - Societe,Principal radio privada. Noticias y programas sociales.,https://www.mosaiquefm.net/fr/rss/societe,13,Sociedad,180,Túnez,fr,False,30 +8330,National Institute of Statistics (INS),Datos y estudios sociales oficiales.,https://www.ins.tn/rss/societe,13,Sociedad,180,Túnez,fr,False,30 +8327,Nawaat - Societe,Blog colectivo. Analisis y debates sobre la sociedad tunecina.,https://nawaat.org/rss/category/societe/,13,Sociedad,180,Túnez,fr,False,30 +8329,Shems FM - Societe,Radio privada. Cobertura de temas sociales.,https://www.shemsfm.net/rss/societe,13,Sociedad,180,Túnez,ar,False,30 +8331,Tunisian Forum for Economic and Social Rights (FTDES),ONG de derechos humanos. Informes sobre protestas y condiciones sociales.,https://ftdes.net/rss/fr/,13,Sociedad,180,Túnez,fr,False,30 +8335,UNDP Tunisia,Noticias sobre proyectos de desarrollo humano y lucha contra la pobreza.,https://www.tn.undp.org/rss.xml,13,Sociedad,180,Túnez,fr,False,30 +8334,UNICEF Tunisia,Noticias sobre programas de proteccion de la infancia y educacion.,https://www.unicef.org/tunisia/rss.xml,13,Sociedad,180,Túnez,fr,False,30 +8298,Aljazeera Net - Technology,Seccion de tecnologia de Al Jazeera (en arabe).,https://www.aljazeera.net/tag/تكنولوجيا/rss,14,Tecnología,180,Túnez,ar,False,30 +8295,BIAT Labs,Hub de innovacion del banco BIAT. Noticias sobre fintech y startups.,https://biatlabs.tn/rss,14,Tecnología,180,Túnez,fr,False,30 +8296,GoMyCode,Academia de programacion y centro de innovacion. Noticias del sector tech.,https://gomycode.com/tn/fr/rss,14,Tecnología,180,Túnez,fr,True,0 +8297,IlBoursa.com - Technologie,Seccion de tecnologia del portal financiero.,https://www.ilboursa.com/rss/technologie,14,Tecnología,180,Túnez,fr,False,30 +8291,National Agency for Computer Security (ANSI),Agencia de ciberseguridad. Alertas y noticias.,https://www.ansi.tn/rss,14,Tecnología,180,Túnez,fr,False,30 +8292,National Digital Certification Agency (ANCE),Agencia de certificacion digital y firma electronica.,https://www.ance.tn/rss,14,Tecnología,180,Túnez,fr,False,30 +8289,Ooredoo Tunisie,Operador de telecomunicaciones. Noticias y lanzamientos.,https://www.ooredoo.tn/portail/rss,14,Tecnología,180,Túnez,fr,False,30 +8290,Orange Tunisie,Operador de telecomunicaciones. Noticias y novedades.,https://www.orange.tn/portail/rss,14,Tecnología,180,Túnez,fr,False,30 +8294,Startup Tunisia,Iniciativa nacional de apoyo al ecosistema startup.,https://startup.tn/rss,14,Tecnología,180,Túnez,fr,False,30 +8287,TAP - Sciences et Technologies,Agencia oficial. Noticias de ciencia y tecnologia.,https://www.tap.info.tn/fr/rss/10/sciences-et-technologies,14,Tecnología,180,Túnez,fr,False,30 +8293,Tunisian Internet Agency (ATI),Gestor del dominio .tn y actor de la infraestructura de internet.,https://www.ati.tn/rss,14,Tecnología,180,Túnez,fr,True,0 +8288,Tunisie Telecom,Operador historico de telecomunicaciones. Noticias corporativas y de servicios.,https://www.tunisietelecom.tn/rss,14,Tecnología,180,Túnez,fr,False,30 +7871,American Association for the Advancement of Science (AAAS) - News,Noticias de la mayor sociedad cientifica general.,https://www.eurekalert.org/news/rss,1,Ciencia,184,Ucrania,en,False,30 +7873,Horizon: the EU Research & Innovation magazine,Noticias sobre investigacion e innovacion en la UE.,https://projects.research-and-innovation.ec.europa.eu/en/horizon-magazine/rss,1,Ciencia,184,Ucrania,en,False,30 +7866,Institute for Nuclear Research (NASU),Investigacion en fisica nuclear y de particulas.,https://www.kinr.kiev.ua/rss/news,1,Ciencia,184,Ucrania,en,False,30 +7867,Institute of Molecular Biology and Genetics (NASU),Investigacion en biomedicina y genetica.,https://www.imbg.org.ua/rss/news,1,Ciencia,184,Ucrania,en,False,30 +7864,Interfax-Ukraine - Science,Agencia de noticias. Noticias sobre ciencia e innovacion.,https://en.interfax.com.ua/news/science/rss,1,Ciencia,184,Ucrania,en,False,30 +7876,International Science Council (ISC),Noticias sobre politica cientifica global y cooperacion.,https://council.science/rss/news,1,Ciencia,184,Ucrania,en,False,30 +7865,National Academy of Sciences of Ukraine (NASU),Principal institucion cientifica. Descubrimientos y noticias de investigacion.,https://www.nas.gov.ua/rss/news,1,Ciencia,184,Ucrania,uk,False,30 +7869,Science at Risk (iniciativa),Iniciativa para apoyar a cientificos ucranianos durante la guerra.,https://scienceatrisk.org/rss/news,1,Ciencia,184,Ucrania,en,False,30 +7868,"Ukrainian Antarctic Station ""Akademik Vernadsky""",Investigacion cientifica en la Antartida.,https://uac.gov.ua/rss/news,1,Ciencia,184,Ucrania,uk,False,30 +7863,Ukrainska Pravda - Science,Seccion de ciencia y tecnologia del principal medio digital.,https://www.pravda.com.ua/news/science/rss/,1,Ciencia,184,Ucrania,uk,False,30 +7875,World Health Organization (WHO) - News,Noticias sobre salud publica e investigacion medica.,https://www.who.int/news-room/releases/rss/en/,1,Ciencia,184,Ucrania,en,False,30 +7837,Atlantic Council - UkraineAlert,Analisis geopolitico y economico sobre Ucrania.,https://www.atlanticcouncil.org/blogs/ukrainealert/feed/,4,Economía,184,Ucrania,en,False,30 +7836,Bloomberg - Europe,Noticias economicas y financieras de la region.,https://www.bloomberg.com/europe/rss,4,Economía,184,Ucrania,en,False,30 +7833,Centre for Economic Strategy (CES),Think tank. Analisis de politicas economicas y reformas.,https://ces.org.ua/en/feed/,4,Economía,184,Ucrania,en,True,0 +7834,European Bank for Reconstruction and Development (EBRD) - Ukraine,Financiacion para reconstruccion y desarrollo del sector privado.,https://www.ebrd.com/rss/where-we-work/ukraine.rss,4,Economía,184,Ucrania,en,False,30 +7827,Interfax-Ukraine - Business,Agencia de noticias independiente. Noticias economicas y financieras.,https://en.interfax.com.ua/news/business/rss,4,Economía,184,Ucrania,en,False,30 +7829,Mind.ua,Portal de noticias sobre negocios y economia.,https://mind.ua/rss,4,Economía,184,Ucrania,uk,False,30 +7830,State Statistics Service of Ukraine,Indicadores economicos oficiales y datos macroeconomicos.,https://www.ukrstat.gov.ua/rss/news,4,Economía,184,Ucrania,uk,False,30 +7828,The Kyiv Independent - Business,Seccion de negocios y economia del principal medio en ingles.,https://kyivindependent.com/feed/?post_type=article&cat=36,4,Economía,184,Ucrania,en,False,30 +7831,UkraineInvest,Agencia estatal de promocion de inversiones.,https://ukraineinvest.gov.ua/rss/news/,4,Economía,184,Ucrania,en,False,30 +7832,Ukrainian Chamber of Commerce and Industry,Gremio empresarial. Noticias sobre comercio y negocios.,https://www.ucci.org.ua/rss/news,4,Economía,184,Ucrania,uk,False,30 +7826,Ukrainska Pravda - Economics,Seccion de economia del principal medio digital independiente.,https://www.epravda.com.ua/rss/,4,Economía,184,Ucrania,uk,True,0 +7798,BBC News - Ukraine,Seccion de Ucrania de la BBC. Cobertura amplia y analisis.,https://www.bbc.com/news/world/europe/ukraine/rss,11,Política,184,Ucrania,en,False,30 +7800,Institute for the Study of War (ISW),Analisis militar y geopolitico diario del conflicto.,https://www.understandingwar.org/rss.xml,11,Política,184,Ucrania,en,False,30 +7790,Interfax-Ukraine,Agencia de noticias independiente. Noticias politicas y economicas.,https://en.interfax.com.ua/rss,11,Política,184,Ucrania,en,False,30 +7799,Meduza (en ruso),Medio independiente con sede en Letonia. Cobertura profunda de Ucrania y Rusia.,https://meduza.io/rss/all,11,Política,184,Ucrania,ru,True,0 +7796,Ministry of Foreign Affairs of Ukraine,Comunicados de politica exterior y diplomacia.,https://mfa.gov.ua/en/rss/news,11,Política,184,Ucrania,en,False,30 +7794,President of Ukraine,Oficina del Presidente. Discursos oficiales y comunicados.,https://www.president.gov.ua/rss/news,11,Política,184,Ucrania,uk,False,30 +7797,The Guardian - Ukraine,Seccion de Ucrania del diario britanico. Cobertura internacional.,https://www.theguardian.com/world/ukraine/rss,11,Política,184,Ucrania,en,True,0 +7792,The Kyiv Independent,Principal medio en ingles. Analisis politico y de seguridad.,https://kyivindependent.com/feed/,11,Política,184,Ucrania,en,False,30 +7793,Ukraine Crisis Media Center,Centro de comunicacion para crisis. Briefings y analisis.,https://uacrisis.org/en/feed,11,Política,184,Ucrania,en,True,0 +7788,Ukrainska Pravda,Principal medio digital independiente. Cobertura politica y de guerra.,https://www.pravda.com.ua/rss/,11,Política,184,Ucrania,uk,True,0 +7789,Ukrinform,Agencia de noticias estatal de Ucrania. Noticias oficiales y politicas.,https://www.ukrinform.net/rss,11,Política,184,Ucrania,uk,True,0 +7795,Verkhovna Rada (Parlamento),Sitio oficial del parlamento. Noticias legislativas.,https://www.rada.gov.ua/news/rss/,11,Política,184,Ucrania,uk,False,30 +7950,Al Jazeera - Europe,Noticias de la region con enfoque en conflictos y sociedad.,https://www.aljazeera.com/europe/rss,13,Sociedad,184,Ucrania,en,False,30 +7947,Amnesty International - Ukraine,Noticias y campañas sobre derechos humanos.,https://www.amnesty.org/en/countries/europe-and-central-asia/ukraine/rss/,13,Sociedad,184,Ucrania,en,True,0 +7939,Hromadske,Medio de servicio publico. Reportajes sociales y de derechos humanos.,https://hromadske.ua/rss,13,Sociedad,184,Ucrania,uk,False,30 +7946,Human Rights Watch - Ukraine,Informes sobre violaciones de derechos humanos y abusos.,https://www.hrw.org/europe/central-asia/ukraine/rss,13,Sociedad,184,Ucrania,en,False,30 +7944,International Committee of the Red Cross (ICRC) - Ukraine,Noticias sobre ayuda humanitaria y derecho internacional.,https://www.icrc.org/en/rss/ukraine,13,Sociedad,184,Ucrania,en,False,30 +7941,Ministry of Reintegration of Temporarily Occupied Territories,Problemas de desplazados internos y territorios ocupados.,https://www.minre.gov.ua/rss/news,13,Sociedad,184,Ucrania,uk,False,30 +7942,National Social Service of Ukraine,Servicios sociales y apoyo a familias.,https://www.nss.gov.ua/rss/news,13,Sociedad,184,Ucrania,uk,False,30 +7791,Suspilne Novyny (Suspilne News),Servicio publico de radiodifusion. Noticias sociales y regionales.,https://www.suspilne.media/rss/,13,Sociedad,184,Ucrania,uk,False,30 +7951,The New Humanitarian,Periodismo especializado en crisis humanitarias y emergencias.,https://www.thenewhumanitarian.org/rss,13,Sociedad,184,Ucrania,en,False,30 +7943,UNHCR Ukraine,Noticias sobre refugiados y desplazados internos ucranianos.,https://www.unhcr.org/uk-ua/news/rss,13,Sociedad,184,Ucrania,uk,False,30 +7945,World Food Programme (WFP) - Ukraine,Noticias sobre seguridad alimentaria y ayuda nutricional.,https://www.wfp.org/countries/ukraine/rss,13,Sociedad,184,Ucrania,en,False,30 +7940,Zmina Human Rights Centre,Informes y noticias sobre derechos humanos en Ucrania.,https://zmina.info/feed/,13,Sociedad,184,Ucrania,uk,True,0 +7878,DOU (Developers.org.ua),Comunidad mas grande de desarrolladores de software. Noticias y articulos.,https://dou.ua/feed/,14,Tecnología,184,Ucrania,ru,True,0 +7883,Kyivstar,Principal operador de telecomunicaciones de Ucrania.,https://kyivstar.ua/ua/rss/news,14,Tecnología,184,Ucrania,uk,False,30 +7885,Lifecell,Operador de telecomunicaciones.,https://www.lifecell.ua/uk/rss/news/,14,Tecnología,184,Ucrania,uk,False,30 +7882,Lviv IT Cluster,Comunidad tecnologica de Lviv. Noticias e iniciativas del sector.,https://itcluster.lviv.ua/feed/,14,Tecnología,184,Ucrania,en,True,0 +7887,MacPaw,Compania de software ucraniana (CleanMyMac). Blog sobre tecnologia y cultura.,https://macpaw.com/uk/rss,14,Tecnología,184,Ucrania,uk,False,30 +7879,Na chasi (На часі),Portal de noticias sobre tecnologia y gadgets.,https://nau.ua/feed,14,Tecnología,184,Ucrania,uk,False,30 +7881,UNIT.City,Primer parque de innovacion de Ucrania. Noticias sobre startups y comunidad tech.,https://unit.city/feed/,14,Tecnología,184,Ucrania,uk,True,0 +7880,Ukraine IT Association (IT Ukraine),Gremio de la industria tecnologica. Noticias del sector y defensa.,https://itukraine.org.ua/rss/news,14,Tecnología,184,Ucrania,en,False,30 +7877,Ukrainska Pravda - Technology,Seccion de tecnologia e innovacion.,https://www.pravda.com.ua/news/tech/rss/,14,Tecnología,184,Ucrania,uk,False,30 +7886,Ukroboronprom,Conglomerado estatal de defensa. Tecnologia e innovacion belica.,https://ukroboronprom.com.ua/rss/news,14,Tecnología,184,Ucrania,uk,False,30 +7884,Vodafone Ukraine,Operador de telecomunicaciones.,https://www.vodafone.ua/ru/rss/news,14,Tecnología,184,Ucrania,ru,False,30 +7752,International Center for Tropical Agriculture (CIAT) - Uganda,Investigacion en agricultura tropical y cambio climatico.,https://ciat.cgiar.org/category/regions/uganda/feed/,1,Ciencia,185,Uganda,en,False,30 +7748,Makerere University,Noticias de investigacion de la principal universidad de Uganda.,https://news.mak.ac.ug/rss,1,Ciencia,185,Uganda,en,False,30 +7749,National Agricultural Research Organization (NARO),Investigacion agricola. Nuevas variedades y tecnicas.,https://www.naro.go.ug/rss,1,Ciencia,185,Uganda,en,True,0 +7746,New Vision - Science,Seccion de ciencia y medio ambiente del diario estatal.,https://www.newvision.co.ug/category/science/rss,1,Ciencia,185,Uganda,en,False,30 +7754,SciDev.Net - Sub-Saharan Africa,Noticias sobre ciencia y desarrollo en Africa subsahariana.,https://www.scidev.net/sub-saharan-africa/feed/,1,Ciencia,185,Uganda,en,False,30 +7747,Science Africa - Uganda,Noticias de ciencia e investigacion en el continente.,https://scienceafrica.co.ug/feed/,1,Ciencia,185,Uganda,en,False,30 +7751,The AIDS Support Organization (TASO) Uganda,Investigacion y noticias sobre VIH/SIDA.,https://tasouganda.org/rss,1,Ciencia,185,Uganda,en,True,0 +7750,Uganda Virus Research Institute (UVRI),Investigacion en virologia y salud publica.,https://www.uvri.go.ug/rss,1,Ciencia,185,Uganda,en,False,30 +7656,African Development Bank - Uganda,Proyectos de financiacion y analisis del Banco Africano de Desarrollo.,https://www.afdb.org/en/countries/east-africa/uganda/rss,4,Economía,185,Uganda,en,False,30 +7653,Capital Markets Authority,Regulacion del mercado de valores y noticias de capitales.,https://www.cmauganda.co.ug/rss,4,Economía,185,Uganda,en,True,0 +7651,ChimpReports - Business,Noticias de negocios y economia.,https://chimpreports.com/category/business/feed/,4,Economía,185,Uganda,en,False,30 +7648,Daily Monitor - Business,Seccion de negocios y economia del principal diario independiente.,https://www.monitor.co.ug/business/rss,4,Economía,185,Uganda,en,False,30 +7655,IMF - Uganda,Evaluaciones y consultas del Fondo Monetario Internacional.,https://www.imf.org/en/Countries/UGA/rss,4,Economía,185,Uganda,en,False,30 +7649,New Vision - Business,Noticias economicas del principal diario estatal.,https://www.newvision.co.ug/business/rss,4,Economía,185,Uganda,en,False,30 +7654,Private Sector Foundation Uganda,Posiciones y noticias del principal gremio empresarial.,https://www.psfuganda.org/rss,4,Economía,185,Uganda,en,False,30 +7657,The EastAfrican - Business,Analisis economico regional de Africa del Este.,https://www.theeastafrican.co.ke/business/rss,4,Economía,185,Uganda,en,False,30 +7650,The Independent - Economy,Analisis economico de la revista semanal.,https://www.independent.co.ug/category/business/feed/,4,Economía,185,Uganda,en,True,0 +7652,Uganda Investment Authority,Noticias sobre oportunidades y clima de inversiones.,https://www.ugandainvest.go.ug/rss,4,Economía,185,Uganda,en,True,0 +7761,ActionAid Uganda,Noticias de la ONG sobre reduccion de la pobreza y justicia social.,https://uganda.actionaid.org/rss,13,Sociedad,185,Uganda,en,False,30 +7762,Amref Health Africa - Uganda,Noticias sobre salud comunitaria y capacitacion sanitaria.,https://amref.org/uganda/rss/,13,Sociedad,185,Uganda,en,True,0 +7758,Bukedde (New Vision en Luganda),Edicion en idioma luganda del diario estatal. Noticias locales y sociales.,https://www.bukedde.co.ug/rss,13,Sociedad,185,Uganda,lg,False,30 +7755,Daily Monitor - News,Noticias generales y sociales del principal diario independiente.,https://www.monitor.co.ug/uganda/news/national/rss,13,Sociedad,185,Uganda,en,False,30 +7756,New Vision - News,Noticias nacionales y sociales del principal diario estatal.,https://www.newvision.co.ug/news/rss,13,Sociedad,185,Uganda,en,False,30 +7757,The Independent - News,Noticias y analisis sociales de la revista semanal.,https://www.independent.co.ug/feed/,13,Sociedad,185,Uganda,en,True,0 +7759,Uganda Police Force - News,Comunicados oficiales y noticias sobre seguridad ciudadana.,https://www.upf.go.ug/rss,13,Sociedad,185,Uganda,en,True,0 +7760,World Food Programme - Uganda,Noticias sobre seguridad alimentaria y asistencia nutricional.,https://www.wfp.org/countries/uganda/rss,13,Sociedad,185,Uganda,en,False,30 +7715,AfriLabs - Uganda,Red de hubs de innovacion. Noticias sobre el ecosistema startup tech.,https://www.afrilabs.com/country/uganda/feed/,14,Tecnología,185,Uganda,en,False,30 +7714,Airtel Uganda,Operador de telecomunicaciones. Noticias de red y productos.,https://www.airtel.ug/rss,14,Tecnología,185,Uganda,en,False,30 +7708,Daily Monitor - Technology,Seccion de tecnologia del principal diario independiente.,https://www.monitor.co.ug/uganda/oped/technology/rss,14,Tecnología,185,Uganda,en,False,30 +7718,Disrupt Africa,Noticias sobre startups e inversiones tech en el continente.,https://disrupt-africa.com/feed/,14,Tecnología,185,Uganda,en,True,0 +7717,GSMA - Mobile for Development,Informes sobre el impacto de la movilidad en el desarrollo (incluye Uganda).,https://www.gsma.com/mobilefordevelopment/feed/,14,Tecnología,185,Uganda,en,False,30 +7710,HiPipo,Plataforma de inclusion digital y fintech. Noticias sobre pagos digitales y startups.,https://www.hipipo.com/feed/,14,Tecnología,185,Uganda,en,False,30 +7713,MTN Uganda,Principal operador de telecomunicaciones. Lanzamientos y noticias corporativas.,https://www.mtn.co.ug/rss,14,Tecnología,185,Uganda,en,False,30 +7711,National Information Technology Authority (NITA-U),Autoridad nacional de TI. Politicas de gobierno digital y proyectos.,https://www.nita.go.ug/rss,14,Tecnología,185,Uganda,en,False,30 +7709,New Vision - Technology,Noticias de tecnologia e innovacion del diario estatal.,https://www.newvision.co.ug/category/technology/rss,14,Tecnología,185,Uganda,en,False,30 +7716,UNICEF Uganda - Innovation,Noticias sobre innovacion tecnologica para el desarrollo infantil.,https://www.unicef.org/uganda/innovation/rss.xml,14,Tecnología,185,Uganda,en,False,30 +7712,Uganda Communications Commission (UCC),Regulador de telecomunicaciones. Noticias del sector y espectro.,https://www.ucc.co.ug/rss,14,Tecnología,185,Uganda,en,True,0 +7719,Ventures Africa,Noticias de negocios e innovacion en Africa.,https://venturesafrica.com/feed/,14,Tecnología,185,Uganda,en,False,30 +7720,Weetracker,Noticias sobre startups y venture capital en mercados emergentes.,https://weetracker.com/feed/,14,Tecnología,185,Uganda,en,True,0 +7540,El Pais Uruguay - Ciencia,Seccion de ciencia y tecnologia del diario de mayor circulacion.,https://www.elpais.com.uy/rss/ciencia.xml,1,Ciencia,186,Uruguay,es,False,30 +7545,Facultad de Ciencias (UdelaR),Noticias de investigacion de la principal facultad de ciencias del pais.,https://www.fcien.edu.uy/rss/noticias,1,Ciencia,186,Uruguay,es,False,30 +7543,Institut Pasteur de Montevideo,Investigacion en biomedicina. Noticias sobre descubrimientos y proyectos.,https://www.pasteur.edu.uy/rss/noticias,1,Ciencia,186,Uruguay,es,False,30 +7548,Instituto Antartico Uruguayo (IAU),Investigacion cientifica en la Antartida. Campanas y logros.,https://www.iau.gub.uy/rss/noticias,1,Ciencia,186,Uruguay,es,False,30 +7544,Instituto de Investigaciones Biologicas Clemente Estable (IIBCE),Investigacion en biologias. Noticias y hallazgos.,https://www.iibce.edu.uy/rss/noticias,1,Ciencia,186,Uruguay,es,False,30 +7541,La Diaria - Ciencia,Seccion de ciencia del diario digital cooperativo. Analisis profundo.,https://ladiaria.com.uy/ciencia/feed/,1,Ciencia,186,Uruguay,es,False,30 +7553,MIT Technology Review - Espanol,Noticias de tecnologia e innovacion de vanguardia.,https://www.technologyreview.es/feed,1,Ciencia,186,Uruguay,es,False,30 +7546,Ministerio de Educacion y Cultura - Cultura Cientifica,Noticias sobre politicas y cultura cientifica nacional.,https://www.gub.uy/ministerio-educacion-cultura/rss/cultura-cientifica,1,Ciencia,186,Uruguay,es,False,30 +7549,NASA en Espanol (Cuenta Oficial),Traducciones oficiales de descubrimientos de la NASA.,https://www.nasa.gov/rss/dyn/NASAenEspanol.rss,1,Ciencia,186,Uruguay,es,False,30 +7547,Programa de Desarrollo de las Ciencias Basicas (PEDECIBA),Noticias de la red de investigadores en ciencias basicas.,https://www.pedeciba.edu.uy/rss/noticias,1,Ciencia,186,Uruguay,es,False,30 +7551,Revista Nature en Espanol,Seleccion de articulos de Nature traducidos al espanol.,https://www.nature.com/espanol.rss,1,Ciencia,186,Uruguay,es,False,30 +7552,Scientific American - Spanish,Edicion en espanol de la prestigiosa revista de divulgacion.,https://www.scientificamerican.com/espanol/feed/,1,Ciencia,186,Uruguay,es,False,30 +7542,Uruguay Ciencia,Portal de divulgacion cientifica del periodista Nicolas Lima.,https://uruguayciencia.com/feed,1,Ciencia,186,Uruguay,es,False,30 +7507,Agencia de Desarrollo (ANDE) & Inversiones (URUGUAY XXI),Noticias sobre promocion de inversiones y desarrollo.,https://www.uruguayxxi.gub.uy/es/news/rss/,4,Economía,186,Uruguay,es,False,30 +7508,Banco Republica (BROU),Principal banco estatal. Noticias financieras y de servicios.,https://www.brou.com.uy/rss/noticias,4,Economía,186,Uruguay,es,False,30 +7504,Busqueda,Semanario especializado en analisis economico y politico.,https://www.busqueda.com.uy/rss/economia.xml,4,Economía,186,Uruguay,es,False,30 +7509,Camara de Industrias del Uruguay (CIU),Noticias y posiciones del sector industrial.,https://www.ciu.com.uy/rss/noticias,4,Economía,186,Uruguay,es,False,30 +7511,Cepal - Sede Subregional en Montevideo,Analisis y estudios economicos sobre Uruguay y la region.,https://www.cepal.org/es/rss/noticias/uruguay,4,Economía,186,Uruguay,es,False,30 +7506,Cinco8,Medio digital de investigacion y analisis economico-politico.,https://www.cinco8.com/category/economia/feed/,4,Economía,186,Uruguay,es,False,30 +7510,Comision Sectorial para el MERCOSUR,Noticias sobre comercio exterior e integracion regional.,https://www.mercosur.gub.uy/rss/noticias,4,Economía,186,Uruguay,es,False,30 +7502,El Observador - Economia,Seccion de economia y negocios del principal diario digital.,https://www.elobservador.com.uy/elobservador/rss/economia.xml,4,Economía,186,Uruguay,es,False,30 +7501,El Pais Uruguay - Economia,Seccion de economia del diario de mayor circulacion.,https://www.elpais.com.uy/rss/economia.xml,4,Economía,186,Uruguay,es,False,30 +7505,EnPerspectiva.net,Portal de analisis con enfasis en economia y coyuntura.,https://www.enperspectiva.net/category/economia/feed/,4,Economía,186,Uruguay,es,False,30 +7513,IMF - Uruguay,Evaluaciones y consultas del FMI sobre Uruguay.,https://www.imf.org/en/Countries/URY/rss,4,Economía,186,Uruguay,en,False,30 +7503,Montevideo Portal - Economia,Noticias economicas del portal de noticias lider.,https://www.montevideo.com.uy/au/rss/economia.xml,4,Economía,186,Uruguay,es,False,30 +7512,World Bank - Uruguay,Informes y datos del Banco Mundial sobre la economia uruguaya.,https://www.worldbank.org/en/country/uruguay/rss,4,Economía,186,Uruguay,en,False,30 +7639,Administracion de los Servicios de Salud del Estado (ASSE),Noticias del principal prestador de salud publica.,https://www.asse.com.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7645,Amnistia Internacional Uruguay,Noticias sobre campañas y defensa de derechos humanos.,https://www.amnistia.org.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7644,Gurises Unidos,Noticias de la ONG sobre derechos de la ninez y adolescencia.,https://www.gurisesunidos.org.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7642,Instituto Nacional de las Mujeres (INMUJERES),Noticias sobre politicas de genero e igualdad.,https://www.inmujeres.gub.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7641,Instituto del Nino y Adolescente del Uruguay (INAU),Noticias sobre politicas de ninez y adolescencia.,https://www.inau.gub.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7646,ONU Uruguay,Noticias de las agencias de Naciones Unidas sobre desarrollo social en Uruguay.,https://www.onu.org.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7643,Observatorio Universitario de Violencia y Criminalidad,Analisis y datos sobre seguridad ciudadana y violencia.,https://observatorioviolencia.uy/rss/noticias,13,Sociedad,186,Uruguay,es,False,30 +7638,Teledoce - Sociedad,Reportajes y noticias sobre temas sociales y de comunidad.,https://www.teledoce.com/sociedad/feed/,13,Sociedad,186,Uruguay,es,False,30 +7647,UNICEF Uruguay,Noticias sobre programas y derechos de la ninez y adolescencia.,https://www.unicef.org/uruguay/rss.xml,13,Sociedad,186,Uruguay,es,False,30 +7640,Universidad de la Republica (UdelaR) - Extension,Noticias sobre proyectos de vinculacion con la comunidad y cultura.,https://www.udelar.edu.uy/rss/extension,13,Sociedad,186,Uruguay,es,False,30 +7606,ANTEL - Noticias,Empresa estatal de telecomunicaciones. Novedades de infraestructura y servicios.,https://www.antel.com.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7609,CUTI (Camara Uruguaya de Tecnologias de la Informacion),Noticias del gremio empresarial del sector de software y servicios IT.,https://www.cuti.org.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7612,Claro Uruguay,Operador de telecomunicaciones. Noticias corporativas y lanzamientos.,https://www.claro.com.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7604,El Pais Uruguay - Tecnologia,Seccion de tecnologia del diario de mayor circulacion.,https://www.elpais.com.uy/rss/tecnologia.xml,14,Tecnología,186,Uruguay,es,False,30 +7314,Genbeta,Noticias y guias sobre software y aplicaciones.,https://www.genbeta.com/feed,14,Tecnología,186,Uruguay,es,False,30 +7607,Ingenieria de Montevideo (Fing - UdelaR),Noticias sobre investigacion y desarrollo en ingenieria y tecnologia.,https://www.fing.edu.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7611,Movistar Uruguay,Operador de telecomunicaciones. Novedades de redes y productos.,https://www.movistar.com.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7608,Polo Tecnologico de Pando,Noticias del parque cientifico-tecnologico. Investigacion aplicada.,https://www.polotecnologico.pando.edu.uy/rss/noticias,14,Tecnología,186,Uruguay,es,False,30 +7605,Startup.uy,Portal de la Asociacion de Startups del Uruguay. Noticias del ecosistema.,https://startup.uy/feed,14,Tecnología,186,Uruguay,es,False,30 +7610,Universidad de la Empresa (UDE) - Tecnologia,Noticias sobre emprendimiento e innovacion tecnologica.,https://www.ude.edu.uy/rss/tecnologia,14,Tecnología,186,Uruguay,es,False,30 +7437,Asian Development Bank (ADB) - Uzbekistan,Proyectos de financiacion y analisis economico del ADB.,https://www.adb.org/countries/uzbekistan/rss,4,Economía,187,Uzbekistán,en,False,30 +7439,Eurasian Development Bank (EDB),Analisis y noticias sobre proyectos de integracion economica en la region.,https://eabr.org/en/analytics/rss/,4,Economía,187,Uzbekistán,en,False,30 +7434,Gazeta.uz - Economy,Noticias economicas del periodico digital Gazeta.uz.,https://www.gazeta.uz/oz/ekonomika/rss/,4,Economía,187,Uzbekistán,uz,False,30 +7438,International Monetary Fund (IMF) - Uzbekistan,Consultas y evaluaciones economicas del FMI.,https://www.imf.org/en/Countries/UZB/rss,4,Economía,187,Uzbekistán,en,False,30 +7433,Kun.uz - Economy,Seccion de economia del principal medio digital independiente.,https://kun.uz/en/news/category/ekonomika/rss,4,Economía,187,Uzbekistán,en,False,30 +7432,Spot.uz,Principal medio especializado en economia y negocios de Uzbekistan.,https://spot.uz/ru/rss/,4,Economía,187,Uzbekistán,ru,True,0 +7440,The Tashkent Times,Periodico en ingles con cobertura de negocios y economia.,https://tashkenttimes.uz/business/feed,4,Economía,187,Uzbekistán,en,False,30 +7436,UNDP Uzbekistan - Economic Development,Proyectos de desarrollo economico y analisis de la ONU.,https://www.uz.undp.org/rss.xml,4,Economía,187,Uzbekistán,en,False,30 +7435,Uzbekistan Stock Exchange (UZSE),Datos del mercado de valores y anuncios corporativos.,https://uzse.uz/ru/news/rss,4,Economía,187,Uzbekistán,ru,False,30 +7394,Daryo.uz,Medio digital popular. Noticias nacionales e internacionales.,https://daryo.uz/feed/,11,Política,187,Uzbekistán,uz,False,30 +7392,Gazeta.uz,Periodico digital en uzbeko y ruso. Cobertura politica nacional.,https://www.gazeta.uz/oz/rss/,11,Política,187,Uzbekistán,uz,True,0 +7391,Kun.uz,Principal medio digital independiente. Noticias politicas y de actualidad.,https://kun.uz/en/news/rss,11,Política,187,Uzbekistán,en,True,0 +7396,Ministry of Foreign Affairs Uzbekistan,Noticias sobre politica exterior y relaciones internacionales.,https://mfa.uz/en/press/news/rss,11,Política,187,Uzbekistán,en,False,30 +7393,Podrobno.uz,Agencia de noticias. Cobertura de politica y sociedad.,https://podrobno.uz/rss/,11,Política,187,Uzbekistán,ru,True,0 +7395,President of Uzbekistan (Official),Web oficial de la Presidencia. Discursos y visitas oficiales.,https://president.uz/en/lists/news/rss,11,Política,187,Uzbekistán,en,False,30 +7398,Radio Free Europe/Radio Liberty - Uzbek,Servicio en uzbeko de RFE/RL. Cobertura politica y de derechos.,https://www.rferl.org/api/zq$epieyqtet,11,Política,187,Uzbekistán,uz,False,30 +7397,Senate of Uzbekistan,Camara alta del parlamento. Noticias legislativas.,https://senat.uz/en/lists/news/rss,11,Política,187,Uzbekistán,en,False,30 +7399,The Diplomat - Central Asia,Analisis de politica y seguridad en Asia Central.,https://thediplomat.com/tag/uzbekistan/feed/,11,Política,187,Uzbekistán,en,True,0 +7390,UZA - Uzbekistan National News Agency,Agencia de noticias estatal. Noticias oficiales y politicas.,https://uza.uz/en/rss,11,Política,187,Uzbekistán,en,True,0 +7469,Daryo.uz - Technology,Seccion de tecnologia del medio popular Daryo.uz.,https://daryo.uz/category/texnologiya/feed/,14,Tecnología,187,Uzbekistán,uz,False,30 +7475,DigiTimes Asia,Noticias sobre la industria de hardware y semiconductores en Asia.,https://www.digitimes.com/news/rss.html,14,Tecnología,187,Uzbekistán,en,False,30 +7468,Gazeta.uz - Technology,Noticias tecnologicas del periodico digital Gazeta.uz.,https://www.gazeta.uz/oz/texnologiya/rss/,14,Tecnología,187,Uzbekistán,uz,False,30 +7470,IT Park Uzbekistan,Noticias oficiales del parque tecnologico estatal. Startups y IT.,https://it-park.uz/ru/news/rss,14,Tecnología,187,Uzbekistán,ru,False,30 +7467,Kun.uz - Technology,Seccion de tecnologia del medio digital mas popular.,https://kun.uz/en/news/category/texnologiya/rss,14,Tecnología,187,Uzbekistán,en,False,30 +7473,Mobiuz (Uzmobile),Operador movil estatal. Noticias y anuncios.,https://mobi.uz/ru/news/rss,14,Tecnología,187,Uzbekistán,ru,True,0 +7466,Spot.uz - Technology,Seccion de tecnologia del principal medio economico.,https://spot.uz/ru/texnologiya/rss/,14,Tecnología,187,Uzbekistán,ru,False,30 +7474,The Tashkent Times - Tech,Noticias de tecnologia y negocios tecnologicos en ingles.,https://tashkenttimes.uz/tech/feed,14,Tecnología,187,Uzbekistán,en,False,30 +7472,Ucell (Tele2 Uzbekistan),Operador movil. Noticias corporativas y de red.,https://ucell.uz/ru/news/rss,14,Tecnología,187,Uzbekistán,ru,False,30 +7471,Uzbektelecom,Operador nacional de telecomunicaciones. Noticias sobre servicios e infraestructura.,https://www.uztelecom.uz/ru/news/rss,14,Tecnología,187,Uzbekistán,ru,False,30 +7403,ABC Pacific - Vanuatu,Noticias sobre Vanuatu de la Australian Broadcasting Corp.,https://www.abc.net.au/pacific/vanuatu/feed,11,Política,188,Vanuatu,en,False,30 +7404,PINA (Pacific Islands News Association),Agregador de noticias del Pacifico. Filtrado por Vanuatu.,https://www.pina.com.fj/category/vanuatu/feed/,11,Política,188,Vanuatu,en,True,0 +7402,RNZ Pacific - Vanuatu,Seccion de Vanuatu de Radio Nueva Zelanda. Cobertura regional.,https://www.rnz.co.nz/international/pacific-news/vanuatu/feed,11,Política,188,Vanuatu,en,False,30 +7400,The Vanuatu Independent,Semanal independiente. Analisis politico y noticias nacionales.,https://vanuatuindependent.com/feed/,11,Política,188,Vanuatu,en,True,0 +7401,VBTC Vanuatu Broadcasting,Servicio publico de radio y television. Noticias nacionales.,https://www.vbtc.vu/news/feed,11,Política,188,Vanuatu,en,False,30 +7406,Vanuatu Parliament,Noticias parlamentarias y legislativas (si existe feed RSS).,https://www.parliament.gov.vu/feed,11,Política,188,Vanuatu,en,False,30 +7405,Vanuatu Prime Minister's Office,Comunicados oficiales del gobierno (si existe feed RSS).,https://www.gov.vu/feed,11,Política,188,Vanuatu,en,False,30 +7386,Bollettino Sala Stampa Santa Sede,Comunicados oficiales de la Sala de Prensa del Vaticano.,https://press.vatican.va/content/salastampa/es/bollettino.rss,11,Política,189,Vaticano,es,False,30 +7387,Osservatore Romano - Politica (ES),Edicion en espanol del diario oficial. Analisis politico y eclesial.,https://www.osservatoreromano.va/es.rss.xml,11,Política,189,Vaticano,es,True,0 +7385,Vatican News - English,Official news portal of the Holy See in English.,https://www.vaticannews.va/en.rss.xml,11,Política,189,Vaticano,en,True,0 +7384,Vatican News - Espanol,Portal oficial de noticias de la Santa Sede en espanol.,https://www.vaticannews.va/es.rss.xml,11,Política,189,Vaticano,es,True,0 +7389,Vatican News - Vatican City,Noticias especificas sobre el gobierno de la Ciudad del Vaticano.,https://www.vaticannews.va/en/vatican-city.rss.xml,11,Política,189,Vaticano,en,True,0 +7388,Papa Francisco (Twitter via RSSHub),Actividad publica del Papa (usando puente RSSHub para Twitter).,https://rsshub.app/twitter/user/Pontifex_es,13,Sociedad,189,Vaticano,es,False,30 +7248,ANIF - Asociacion Nacional de Instituciones Financieras,Noticias del sector financiero.,https://www.anif.org.ve/feed/,4,Economía,190,Venezuela,es,False,30 +7250,Al Navio - Economia,Analisis de temas economicos.,https://alnavio.com/category/economia/feed/,4,Economía,190,Venezuela,es,True,0 +7234,Banca y Negocios,Principal medio especializado en economia y finanzas de Venezuela.,https://www.bancaynegocios.com/feed/,4,Economía,190,Venezuela,es,False,30 +7245,Bolsa de Valores de Caracas,Informacion del mercado de capitales.,https://www.bolsadecaracas.com/feed/,4,Economía,190,Venezuela,es,True,0 +7246,CONINDUSTRIA - Conindustria,Noticias del sector industrial venezolano.,https://conindustria.org/feed/,4,Economía,190,Venezuela,es,True,0 +7241,Caraota Digital - Economia,Noticias sobre economia y finanzas.,https://caraotadigital.net/category/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7251,Contrapunto - Economia,Seccion de economia y finanzas.,https://contrapunto.com/category/economia/feed/,4,Economía,190,Venezuela,es,True,0 +7240,Efecto Cocuyo - Economia,Impacto economico en la vida cotidiana y derechos economicos.,https://efectococuyo.com/category/economia/feed/,4,Economía,190,Venezuela,es,True,0 +7235,El Nacional - Economia,Seccion economica del diario El Nacional.,https://www.elnacional.com/economia/feed/,4,Economía,190,Venezuela,es,True,0 +7242,El Pitazo - Economia,Noticias economicas con enfoque comunitario.,https://elpitazo.net/category/economia/feed/,4,Economía,190,Venezuela,es,True,0 +7236,El Universal - Economia,Noticias economicas y financieras.,https://www.eluniversal.com/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7247,FEDECAMARAS - Federacion de Camaras y Asociaciones de Comercio y Produccion de Venezuela,Comunicados del principal gremio empresarial.,https://www.fedecamaras.org.ve/feed/,4,Economía,190,Venezuela,es,True,0 +7238,La Patilla - Economia,Noticias economicas en el portal mas popular.,https://www.lapatilla.com/category/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7249,Primicias - Economia,Analisis de actualidad economica.,https://primicias.com.ve/category/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7239,Runrunes - Economia,Analisis economico y financiero.,https://runrun.es/category/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7243,SENIAT - Servicio Nacional Integrado de Administracion Aduanera y Tributaria,Noticias tributarias y aduaneras.,https://www.seniat.gob.ve/feed/,4,Economía,190,Venezuela,es,False,30 +7244,SUDEBAN - Superintendencia de las Instituciones del Sector Bancario,Regulacion y noticias del sector bancario.,https://www.sudeban.gob.ve/feed/,4,Economía,190,Venezuela,es,False,30 +7237,TalCual - Economia,Cobertura de temas economicos y su impacto social.,https://talcualdigital.com/category/economia/feed/,4,Economía,190,Venezuela,es,False,30 +7313,Al Navio - Tecnologia,Noticias y tendencias del mundo tech.,https://alnavio.com/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7303,CANTV - Noticias,Noticias de la empresa nacional de telecomunicaciones.,https://www.cantv.com.ve/seccion.php?sid=183,14,Tecnología,190,Venezuela,es,False,30 +7304,Conatel - Noticias,Noticias del ente regulador de telecomunicaciones.,https://www.conatel.gob.ve/feed/,14,Tecnología,190,Venezuela,es,False,30 +7311,Contrapunto - Tecnologia,Seccion de ciencia y tecnologia.,https://contrapunto.com/category/ciencia-y-tecnologia/feed/,14,Tecnología,190,Venezuela,es,True,0 +7310,Descifrado - Tecnologia,Noticias sobre emprendimiento tecnológico e innovación.,https://descifrado.com/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,True,0 +7305,El Nacional - Tecnologia,Seccion de tecnologia del diario El Nacional.,https://www.elnacional.com/tecnologia/feed/,14,Tecnología,190,Venezuela,es,True,0 +7306,El Universal - Tecnologia,Noticias sobre innovacion y mundo digital.,https://www.eluniversal.com/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7308,La Patilla - Tecnologia,Noticias de tecnologia en el portal popular.,https://www.lapatilla.com/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7302,Movilnet - Noticias,Noticias y lanzamientos de la operadora movil estatal.,https://www.movilnet.com.ve/noticias/feed/,14,Tecnología,190,Venezuela,es,False,30 +7312,Primicias - Tecnologia,Actualidad sobre tecnologia e innovacion.,https://primicias.com.ve/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7309,Runrunes - Tecnologia,Noticias y analisis sobre tecnologia.,https://runrun.es/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7307,TalCual - Tecnologia,Cobertura de temas tecnologicos y su impacto.,https://talcualdigital.com/category/tecnologia/feed/,14,Tecnología,190,Venezuela,es,False,30 +7068,26 September Yemen,Periodico del gobierno yemeni.,http://www.26sep.net/rss/politics,11,Política,192,Yemen,ar,False,30 +7073,AP News Yemen,Noticias de Associated Press sobre Yemen.,http://apnews.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7079,Al-Arabiya Yemen,Noticias de Al-Arabiya sobre Yemen.,http://www.alarabiya.net/rss/yemen,11,Política,192,Yemen,ar,True,0 +7069,Al-Ayyam Yemen,Periodico independiente yemeni.,http://www.al-ayyam.com/rss/politics,11,Política,192,Yemen,ar,False,30 +7080,Al-Hayat Yemen,Noticias de Al-Hayat sobre Yemen.,http://www.alhayat.com/rss/yemen,11,Política,192,Yemen,ar,False,30 +7071,Al-Jazeera Yemen,Noticias de Al-Jazeera sobre Yemen.,http://www.aljazeera.com/rss/yemen,11,Política,192,Yemen,ar,False,30 +7065,Al-Masdar Online Yemen,Noticias politicas y de actualidad yemeni.,http://www.almasdaronline.com/rss/politics,11,Política,192,Yemen,ar,False,30 +7084,Al-Monitor Yemen,Analisis politico sobre Yemen.,http://www.al-monitor.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7067,Al-Thawra Yemen,Periodico oficial del gobierno yemeni.,http://www.althawra.gov.ye/rss/politics,11,Política,192,Yemen,ar,False,30 +7081,Asharq Al-Awsat Yemen,Noticias de Asharq Al-Awsat sobre Yemen.,http://www.aawsat.com/rss/yemen,11,Política,192,Yemen,ar,False,30 +7074,BBC Arabic Yemen,BBC noticias sobre Yemen en arabe.,http://www.bbc.com/arabic/rss/yemen,11,Política,192,Yemen,ar,False,30 +7075,BBC News Yemen,BBC noticias sobre Yemen en ingles.,http://www.bbc.com/news/rss/yemen,11,Política,192,Yemen,en,False,30 +7086,CNN Yemen,Noticias de CNN sobre Yemen.,http://www.cnn.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7078,DW Arabic Yemen,DW noticias sobre Yemen en arabe.,http://www.dw.com/ar/rss/yemen,11,Política,192,Yemen,ar,False,30 +7076,France 24 Arabic Yemen,France 24 noticias sobre Yemen en arabe.,http://www.france24.com/ar/rss/yemen,11,Política,192,Yemen,ar,False,30 +7077,France 24 English Yemen,France 24 noticias sobre Yemen en ingles.,http://www.france24.com/en/rss/yemen,11,Política,192,Yemen,en,False,30 +7082,Middle East Eye Yemen,Noticias del Medio Oriente sobre Yemen.,http://www.middleeasteye.net/rss/yemen,11,Política,192,Yemen,en,False,30 +7087,New York Times Yemen,Noticias del New York Times sobre Yemen.,http://www.nytimes.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7072,Reuters Yemen,Noticias de Reuters sobre Yemen.,http://www.reuters.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7064,Saba News Agency Yemen,Noticias oficiales de la agencia de noticias de Yemen.,http://www.saba.ye/rss/politics,11,Política,192,Yemen,ar,False,30 +7085,The Guardian Yemen,Noticias de The Guardian sobre Yemen.,http://www.theguardian.com/rss/yemen,11,Política,192,Yemen,en,True,0 +7083,The National Yemen,Periodico de los Emiratos sobre Yemen.,http://www.thenationalnews.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7088,Washington Post Yemen,Noticias del Washington Post sobre Yemen.,http://www.washingtonpost.com/rss/yemen,11,Política,192,Yemen,en,False,30 +7070,Yemen Post,Noticias de Yemen en ingles.,http://www.yemenpost.net/rss/politics,11,Política,192,Yemen,en,False,30 +7066,Yemen Times,Periodico en ingles sobre noticias de Yemen.,http://www.yementimes.com/rss/politics,11,Política,192,Yemen,en,False,30 +7116,Al-Arabiya Technology Yemen,Noticias de tecnologia de Al-Arabiya.,http://www.alarabiya.net/rss/yemen-technology,14,Tecnología,192,Yemen,ar,True,0 +7115,Al-Jazeera Technology Yemen,Noticias de tecnologia de Al-Jazeera.,http://www.aljazeera.com/rss/yemen-technology,14,Tecnología,192,Yemen,ar,False,30 +7103,Al-Masdar Technology Yemen,Noticias de tecnologia de Al-Masdar.,http://www.almasdaronline.com/rss/technology,14,Tecnología,192,Yemen,ar,False,30 +7119,Arabian Business Technology,Negocios y tecnologia en el mundo arabe.,http://www.arabianbusiness.com/rss/technology/yemen,14,Tecnología,192,Yemen,en,False,30 +7117,Asharq Al-Awsat Technology,Noticias de tecnologia de Asharq Al-Awsat.,http://www.aawsat.com/rss/yemen-technology,14,Tecnología,192,Yemen,ar,False,30 +7112,Cisco Middle East Yemen,Tecnologia de redes en Yemen.,http://www.cisco.com/rss/middle-east/yemen,14,Tecnología,192,Yemen,en,False,30 +7108,GSMA Middle East Yemen,Asociacion de operadores moviles.,http://www.gsma.com/rss/middle-east/yemen,14,Tecnología,192,Yemen,en,False,30 +7114,Google News Yemen,Noticias de Google sobre Yemen.,http://news.google.com/rss/yemen-technology,14,Tecnología,192,Yemen,en,True,0 +7120,Gulf News Technology Yemen,Noticias de tecnologia del Golfo.,http://www.gulfnews.com/rss/technology/yemen,14,Tecnología,192,Yemen,en,False,30 +7107,ITU Yemen,Union Internacional de Telecomunicaciones.,http://www.itu.int/rss/yemen,14,Tecnología,192,Yemen,en,False,30 +7113,Microsoft Middle East Yemen,Tecnologia Microsoft en Yemen.,http://www.microsoft.com/rss/middle-east/yemen,14,Tecnología,192,Yemen,en,False,30 +7118,Middle East Tech Review,Revision de tecnologia del Medio Oriente.,http://www.me-techreview.com/rss/yemen,14,Tecnología,192,Yemen,en,False,30 +7105,Ministry of Communications Yemen,Ministerio de Comunicaciones y Tecnologia.,http://www.moct.gov.ye/rss,14,Tecnología,192,Yemen,ar,False,30 +7101,Saba Net,Proveedor de servicios de internet.,http://www.sabanet.net/rss,14,Tecnología,192,Yemen,ar,False,30 +7122,TechRadar Middle East Yemen,Noticias de tecnologia de TechRadar.,http://www.techradar.com/rss/middle-east/yemen,14,Tecnología,192,Yemen,en,False,30 +7100,TeleYemen,Compania de telecomunicaciones estatal.,http://www.teleyemen.com/rss,14,Tecnología,192,Yemen,ar,False,30 +7110,UNDP Yemen Technology,Desarrollo tecnologico de la ONU.,http://www.undp.org/rss/yemen/technology,14,Tecnología,192,Yemen,en,False,30 +7111,UNESCO Yemen Technology,Tecnologia en educacion y cultura.,http://www.unesco.org/rss/yemen/technology,14,Tecnología,192,Yemen,en,False,30 +7121,Wamda Yemen,Emprendimiento tecnologico en el mundo arabe.,http://www.wamda.com/rss/yemen,14,Tecnología,192,Yemen,en,False,30 +7109,World Bank Digital Yemen,Banco Mundial proyectos digitales.,http://www.worldbank.org/rss/yemen/digital,14,Tecnología,192,Yemen,en,False,30 +7106,Yemen IT Association,Asociacion de tecnologia de la informacion.,http://www.yita.org/rss,14,Tecnología,192,Yemen,ar,False,30 +7099,Yemen Mobile,Operador de telefonía móvil en Yemen.,http://www.yemenmobile.com/rss,14,Tecnología,192,Yemen,ar,False,30 +7098,Yemen Net,Proveedor de servicios de internet en Yemen.,http://www.yemennet.net/rss,14,Tecnología,192,Yemen,ar,False,30 +7104,Yemen Post Technology,Noticias de tecnologia del Yemen Post.,http://www.yemenpost.net/rss/technology,14,Tecnología,192,Yemen,en,False,30 +7102,Yemen Times Technology,Noticias de tecnologia en ingles.,http://www.yementimes.com/rss/technology,14,Tecnología,192,Yemen,en,False,30 +6981,ADI Economia,Agencia Djibutiana de Informacion economia.,http://www.adi.dj/rss/economie,4,Economía,193,Yibuti,fr,False,30 +6993,Africa Report Yibuti,Informes economicos de Africa Report.,http://www.theafricareport.com/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6977,Agencia de Inversiones Yibuti,Promocion de inversiones en Yibuti.,http://www.invest.dj/rss,4,Economía,193,Yibuti,en,False,30 +6997,All Africa Economia Yibuti,Noticias economicas en AllAfrica.,http://allafrica.com/djibouti/economy/rss,4,Economía,193,Yibuti,en,False,30 +6976,Autoridad Portuaria Yibuti,Autoridad Portuaria y de Zona Franca.,http://www.portdedjibouti.com/rss,4,Economía,193,Yibuti,fr,True,0 +6985,Banco Africano Desarrollo Yibuti,Proyectos de desarrollo en Yibuti.,http://www.afdb.org/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6975,Banco Central de Yibuti,Banque Centrale de Djibouti.,http://www.bcd.dj/rss,4,Economía,193,Yibuti,fr,False,30 +6983,Banco Mundial Yibuti,Informes economicos del Banco Mundial.,http://www.worldbank.org/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6990,Bloomberg Africa Yibuti,Noticias financieras sobre Yibuti.,http://www.bloomberg.com/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6978,Camara de Comercio Yibuti,Camara de Comercio e Industria.,http://www.ccid.dj/rss,4,Economía,193,Yibuti,fr,False,30 +6996,Commodafrica Yibuti,Noticias sobre commodities en Africa.,http://www.commodafrica.com/rss/djibouti,4,Economía,193,Yibuti,fr,False,30 +6998,East African Business Week Yibuti,Negocios en Africa Oriental.,http://www.busiweek.com/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6995,Ecofin Agencia Yibuti,Agencia de noticias economicas africanas.,http://www.agenceecofin.com/rss/djibouti,4,Economía,193,Yibuti,fr,False,30 +6984,FMI Yibuti,Informes del Fondo Monetario Internacional.,http://www.imf.org/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6991,Financial Times Africa Yibuti,Analisis economico de FT.,http://www.ft.com/rss/djibouti-economy,4,Economía,193,Yibuti,en,False,30 +6988,IGAD Economia Yibuti,Desarrollo economico regional.,http://www.igad.int/rss/djibouti,4,Economía,193,Yibuti,en,True,0 +6994,Jeune Afrique Economie Yibuti,Analisis economico de Jeune Afrique.,http://www.jeuneafrique.com/rss/djibouti-economie,4,Economía,193,Yibuti,fr,False,30 +6980,La Nation Economia,Seccion economia del periodico La Nation.,http://www.lanation.dj/rss/economie,4,Economía,193,Yibuti,fr,False,30 +6974,Ministerio de Economia Yibuti,Ministerio de Economia y Finanzas.,http://www.economie.gouv.dj/rss,4,Economía,193,Yibuti,fr,False,30 +6979,Ministerio de Energia Yibuti,Recursos energeticos e infraestructura.,http://www.energie.gouv.dj/rss,4,Economía,193,Yibuti,fr,False,30 +6986,ONU Desarrollo Yibuti,Programa de desarrollo de la ONU.,http://www.undp.org/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6982,RTD Economia,Radio Television de Djibouti economia.,http://www.rtd.dj/rss/economie,4,Economía,193,Yibuti,fr,False,30 +6989,Reuters Africa Economia Yibuti,Noticias economicas de Reuters.,http://www.reuters.com/rss/djibouti-economy,4,Economía,193,Yibuti,en,False,30 +6992,The Economist Africa Yibuti,Analisis economico de The Economist.,http://www.economist.com/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +6987,Unión Africana Economia Yibuti,Perspectivas economicas regionales.,http://www.au.int/rss/djibouti,4,Economía,193,Yibuti,en,False,30 +7001,ADI Djibouti,Agencia Djibutiana de Informacion.,http://www.adi.dj/rss/politique,11,Política,193,Yibuti,fr,False,30 +7015,AP News Djibouti,Associated Press noticias sobre Yibuti.,http://apnews.com/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7013,Al Jazeera Djibouti,Noticias de Al Jazeera sobre Yibuti.,http://www.aljazeera.com/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7008,All Africa Djibouti,Noticias de Yibuti en AllAfrica.,http://allafrica.com/djibouti/rss,11,Política,193,Yibuti,en,False,30 +7017,Arab News Djibouti,Noticias del mundo arabe sobre Yibuti.,http://www.arabnews.com/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7004,Asamblea Nacional Djibouti,Parlamento de Yibuti.,http://www.assemblee-nationale.dj/rss,11,Política,193,Yibuti,fr,False,30 +7011,BBC Africa Djibouti,BBC noticias sobre Yibuti.,http://www.bbc.com/afrique/rss/djibouti,11,Política,193,Yibuti,fr,False,30 +7007,Djibouti Tribune,Noticias de Yibuti en ingles.,http://www.djiboutitribune.com/rss/politics,11,Política,193,Yibuti,en,False,30 +7012,France 24 Afrique Djibouti,Noticias de France 24 sobre Yibuti.,http://www.france24.com/afrique/djibouti/rss,11,Política,193,Yibuti,fr,False,30 +7006,Horn of Africa News,Noticias del Cuerno de Africa.,http://www.hornofafrica.news/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7009,Jeune Afrique Djibouti,Noticias de Yibuti en Jeune Afrique.,http://www.jeuneafrique.com/rss/djibouti,11,Política,193,Yibuti,fr,False,30 +6999,La Nation Djibouti,Periodico oficial de Yibuti.,http://www.lanation.dj/rss/politique,11,Política,193,Yibuti,fr,False,30 +7018,Middle East Monitor Djibouti,Monitor de Medio Oriente sobre Yibuti.,http://www.middleeastmonitor.com/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7005,Ministerio de Relaciones Exteriores,Politica exterior de Yibuti.,http://www.diplomatie.gouv.dj/rss,11,Política,193,Yibuti,fr,False,30 +7002,Presidencia Djibouti,Presidencia de la Republica de Yibuti.,http://www.presidence.dj/rss,11,Política,193,Yibuti,fr,False,30 +7003,Primer Ministro Djibouti,Oficina del Primer Ministro.,http://www.premier-ministre.dj/rss,11,Política,193,Yibuti,fr,False,30 +7010,RFI Afrique Djibouti,Radio Francia Internacional sobre Yibuti.,http://www.rfi.fr/afrique/djibouti/rss,11,Política,193,Yibuti,fr,False,30 +7000,RTD Djibouti,Radio Television de Djibouti.,http://www.rtd.dj/rss/politique,11,Política,193,Yibuti,fr,False,30 +7014,Reuters Africa Djibouti,Reuters noticias sobre Yibuti.,http://www.reuters.com/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7016,The East African Djibouti,Noticias de Africa Oriental.,http://www.theeastafrican.co.ke/rss/djibouti,11,Política,193,Yibuti,en,False,30 +7033,AFRINIC Djibouti,Registro de direcciones de Internet Africa.,http://www.afrinic.net/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7042,Africa Tech Review,Revision de tecnologia africana.,http://www.africatechreview.com/rss/djibouti,14,Tecnología,193,Yibuti,en,True,0 +7020,Autoridad Reguladora TIC Yibuti,Autoridad Reguladora de Telecomunicaciones.,http://www.art.dj/rss,14,Tecnología,193,Yibuti,fr,False,30 +7043,Bitcoin Africa Djibouti,Criptomonedas y blockchain en Africa.,http://www.bitcoinafrica.io/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7028,Cable Submarino Djibouti,Infraestructura de cables submarinos.,http://www.submarinecable.dj/rss,14,Tecnología,193,Yibuti,en,False,30 +7035,Cisco Africa Djibouti,Tecnologia de redes y conectividad.,http://www.cisco.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7027,Data Center Djibouti,Centro de datos y servicios en la nube.,http://www.datacenter.dj/rss,14,Tecnología,193,Yibuti,en,False,30 +7041,Disrupt Africa,Innovacion y tecnologia en Africa.,http://www.disrupt-africa.com/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7021,Djibouti Telecom,Compania nacional de telecomunicaciones.,http://www.djiboutitelecom.dj/rss,14,Tecnología,193,Yibuti,fr,False,30 +7032,GSMA Africa Djibouti,Asociacion de operadores moviles.,http://www.gsma.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7024,Golis Telecom Somalia,Operador de telecomunicaciones regional.,http://www.golistelecom.com/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7037,Google Africa Djibouti,Noticias de Google sobre Africa.,http://www.google.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7023,Hormuud Telecom Somalia,Operador de telecomunicaciones en el Cuerno.,http://www.hormuud.com/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7038,Huawei Africa Djibouti,Tecnologia y telecomunicaciones.,http://www.huawei.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7031,ITU Africa Djibouti,Union Internacional de Telecomunicaciones.,http://www.itu.int/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7034,Internet Society Africa,Desarrollo de Internet en Africa.,http://www.internetsociety.org/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7036,Microsoft Africa Djibouti,Tecnologia y transformacion digital.,http://www.microsoft.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7019,Ministerio de Comunicaciones Yibuti,Ministerio de Comunicaciones y TIC.,http://www.communications.gouv.dj/rss,14,Tecnología,193,Yibuti,fr,False,30 +7025,Port de Djibouti Digital,Digitalizacion del puerto de Yibuti.,http://www.portdedjibouti.com/rss/digital,14,Tecnología,193,Yibuti,fr,False,30 +7026,Smart City Djibouti,Proyectos de ciudad inteligente.,http://www.smartcity.dj/rss,14,Tecnología,193,Yibuti,fr,False,30 +7022,Somali Telecom Group,Operador de telecomunicaciones regional.,http://www.somalitelecom.com/rss/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7039,TechCrunch Africa,Noticias de tecnologia en Africa.,http://www.techcrunch.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7029,UNDP Digital Djibouti,Programa de desarrollo digital de la ONU.,http://www.undp.org/rss/djibouti/digital,14,Tecnología,193,Yibuti,en,False,30 +7040,Ventureburn Africa,Emprendimiento tecnologico en Africa.,http://www.ventureburn.com/rss/africa/djibouti,14,Tecnología,193,Yibuti,en,False,30 +7030,World Bank Digital Djibouti,Banco Mundial proyectos digitales.,http://www.worldbank.org/rss/djibouti/digital,14,Tecnología,193,Yibuti,en,False,30 +6972,Bemba Dictionary Online,Indigenous language preservation and learning.,https://bemba.org/feed/,2,Cultura,194,Zambia,en,False,30 +6970,Lusaka Times - Lifestyle,Cultural and lifestyle articles from leading news site.,https://www.lusakatimes.com/category/lifestyle/feed/,2,Cultura,194,Zambia,en,True,0 +6971,National Heritage Commission,Cultural preservation and heritage site management.,https://www.nhcc.zm/rss/,2,Cultura,194,Zambia,en,False,30 +6969,Times of Zambia - Culture,Cultural coverage from state-owned newspaper.,https://www.times.co.zm/category/culture/feed/,2,Cultura,194,Zambia,en,False,30 +6973,UNESCO Zambia - Culture,Cultural diversity and heritage protection programs.,https://www.unesco.org/en/countries/zm/rss?theme=culture,2,Cultura,194,Zambia,en,False,30 +6968,Zambia Daily Mail - Culture,Cultural news and features from national newspaper.,https://www.daily-mail.co.zm/category/culture/feed/,2,Cultura,194,Zambia,en,True,0 +6934,African Development Bank - Zambia,Development projects and economic analysis.,https://www.afdb.org/en/countries/southern-africa/zambia/rss,4,Economía,194,Zambia,en,False,30 +6930,Business Daily Zambia,Business and economic news from Zambia.,https://businessdailyzambia.com/feed/,4,Economía,194,Zambia,en,False,30 +6936,IMF - Zambia,Macroeconomic assessments and fiscal monitoring.,https://www.imf.org/en/Countries/ZMB/rss,4,Economía,194,Zambia,en,False,30 +6931,Lusaka Securities Exchange,Stock market data and listed company announcements.,https://www.luse.co.zm/rss/,4,Economía,194,Zambia,en,False,30 +6935,World Bank - Zambia,Development indicators and economic reports.,https://www.worldbank.org/en/country/zambia/rss,4,Economía,194,Zambia,en,False,30 +6933,Zambia Chamber of Commerce,Business sector news and economic advocacy.,https://www.zambiachamber.org/rss/,4,Economía,194,Zambia,en,False,30 +6932,Zambia Development Agency,Investment news and business opportunities.,https://www.zda.org.zm/news/rss,4,Economía,194,Zambia,en,True,0 +6921,Daily Nation Zambia,National newspaper with political coverage and analysis.,https://daily-nation-zambia.com/rss/,11,Política,194,Zambia,en,False,30 +6919,Lusaka Times,Zambia's leading online news site covering politics and current affairs.,https://www.lusakatimes.com/feed/,11,Política,194,Zambia,en,True,0 +6929,News Diggers,Independent Zambian newspaper with political reporting.,https://diggers.news/feed/,11,Política,194,Zambia,en,True,0 +6927,Parliament of Zambia,Official parliamentary proceedings and legislative updates.,https://www.parliament.gov.zm/rss/,11,Política,194,Zambia,en,False,30 +6922,The Mast Online,Independent Zambian newspaper covering politics and society.,https://www.themastonline.com/feed/,11,Política,194,Zambia,en,False,30 +6928,The Zambian Observer,Political commentary and analysis on Zambian affairs.,https://zambianobserver.com/feed/,11,Política,194,Zambia,en,True,0 +6924,Times of Zambia,State-owned newspaper covering political developments.,https://www.times.co.zm/feed/,11,Política,194,Zambia,en,False,30 +6926,ZNBC News,Zambia National Broadcasting Corporation news portal.,http://www.znbc.co.zm/news/feed/,11,Política,194,Zambia,en,False,30 +6923,Zambia Daily Mail,Government-owned newspaper with official political news.,https://www.daily-mail.co.zm/feed/,11,Política,194,Zambia,en,False,30 +6925,Zambia Monitor,News website focusing on Zambian politics and governance.,https://zambiamonitor.com/feed/,11,Política,194,Zambia,en,True,0 +6920,Zambia Reports,Latest Zambian news including politics and government.,https://zambiareports.com/feed/,11,Política,194,Zambia,en,False,30 +6940,Airtel Zambia,Mobile network updates and digital innovation.,https://www.airtel.com.zm/news/feed/,14,Tecnología,194,Zambia,en,False,30 +6939,Liquid Intelligent Technologies Zambia,Business technology solutions and connectivity news.,https://www.liquid.tech/zm-en/news/rss,14,Tecnología,194,Zambia,en,False,30 +6937,Smart Zambia Institute,E-government initiatives and national ICT infrastructure.,https://www.smartzambia.io/feed/,14,Tecnología,194,Zambia,en,False,30 +6942,UNESCO Zambia ICT,ICT in education and digital literacy initiatives.,https://www.unesco.org/en/countries/zm/rss,14,Tecnología,194,Zambia,en,False,30 +6941,Zambia Computer Society,IT professional news and technology events.,https://www.zcs.org.zm/rss/,14,Tecnología,194,Zambia,en,False,30 +6938,Zamtel News,Telecommunications company news and network developments.,https://www.zamtel.co.zm/news/rss,14,Tecnología,194,Zambia,en,False,30 +6896,263Chat,Award winning media organisation amplifying the voices of Zimbabwean people.,https://263chat.com/,11,Política,195,Zimbabue,en,False,30 +6890,My Zimbabwe News,Zimbabwean Internet Newspaper with latest breaking political and community news.,https://myzimbabwe.co.zw/feed/,11,Política,195,Zimbabue,en,True,0 +6897,Newsday Zimbabwe,Zimbabwe's biggest independent daily newspaper.,https://newsday.co.zw/,11,Política,195,Zimbabue,en,False,30 +6894,The Financial Gazette,Southern Africa's leading business and political newspaper.,https://fingaz.co.zw/feed/,11,Política,195,Zimbabue,en,False,30 +6895,The Insider,Zimbabwe-focussed online publication.,https://insiderzim.com/feed/,11,Política,195,Zimbabue,en,True,0 +6898,The Zimbabwe Independent,The leading business weekly.,https://theindependent.co.zw/,11,Política,195,Zimbabue,en,False,30 +6892,Zim Morning Post,Online news with a bias towards investigative journalism across all spheres including politics.,https://zimmorningpost.com/feed/,11,Política,195,Zimbabue,en,True,0 +6891,ZimEye,Zimbabwe news website covering local news and more.,https://zimeye.net/feed/,11,Política,195,Zimbabue,en,True,0 +6893,ZimLive,Trusted name in the Zimbabwe news newsdesk.,https://zimlive.com/feed/,11,Política,195,Zimbabue,en,False,30 +6889,iHarare News,Latest Zimbabwe news including politics and international breaking news.,https://iharare.com/feed/,11,Política,195,Zimbabue,en,True,0 +1535,Ambergris Caye – Travel Blog,"Viajes, turismo, estilo de vida costeño en Belice.",15,,,,,tr,False,0 +999,Brand New Feed,,http://brand-new.com,,,,,es,False,30 +1525,Caribbean Culture – Belize Section,"Cultura caribeña, patrimonio y sociedad.",2,,,,,tr,False,0 +1526,Caribbean Religion & Society – Belize Section,"Religión, cultura y sociedad.",13,,,,,tr,False,0 +2555,Fresh News Asia,Medio online en ingles con noticias rapidas de Camboya y region ASEAN,7,,,,,tr,False,0 +1002,Invalid Feed,,http://v2.com,,,,,,False,30 +1527,NEMO – Belize – News & Alerts,"Gestión de desastres, medio ambiente, sociedad.",8,,,,,tr,False,0 +5896,New Feed Conflict,,http://new-conflict.com,,,,,es,False,30 +998,Old Feed,,http://old.com,,,,,,False,30 +1495,Oracle Belize – Corporate RSS,"Tecnología, negocios y empresa.",14,,,,,tr,False,0 +2432,Parliament of Bulgaria – RSS Noticias RSS oficial del Parlamento búlgaro,https://old.parliament.bg/en/rss,11,,,,,tr,False,0 +1530,UNDP Belize – Procurement & Development Notices,"Desarrollo económico, proyectos, sociedad.",4,,,,,tr,False,0 +1523,UWI Cave Hill etc – Regional Science & Research News Caribbean (incluye Belice),"Ciencia, educación e investigación regional.",1,,,,,tr,False,0 +1001,Valid Feed 1,,http://v1.com,,,,,,False,30 +1003,Valid Feed 2,,http://v3.com,,,,,,False,30 diff --git a/frontend/src/pages/AdminSettings.tsx b/frontend/src/pages/AdminSettings.tsx index d71c24c..d0febde 100644 --- a/frontend/src/pages/AdminSettings.tsx +++ b/frontend/src/pages/AdminSettings.tsx @@ -1,9 +1,11 @@ import { useState } from 'react' import { apiService } from '../services/api' -import { AlertTriangle, Database, RefreshCw, Download, FileArchive } from 'lucide-react' +import { AlertTriangle, Database, RefreshCw, Download, FileArchive, Upload } from 'lucide-react' export function AdminSettings() { const [resetting, setResetting] = useState(false) + const [restoring, setRestoring] = useState(false) + const [restoreFile, setRestoreFile] = useState(null) const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) const handleReset = async () => { @@ -35,6 +37,31 @@ export function AdminSettings() { } } + const handleRestore = async () => { + if (!restoreFile) { + setMessage({ type: 'error', text: 'Selecciona un archivo de copia de seguridad (.sql o .zip)' }) + return + } + + if (!confirm('¿Restaurar la base de datos?\n\nEsta acción sobrescribirá los datos existentes con el contenido del archivo de copia de seguridad. No se puede deshacer.')) return + + setRestoring(true) + setMessage(null) + + try { + const result = await apiService.restoreDatabase(restoreFile) + setMessage({ type: 'success', text: result.message }) + setRestoreFile(null) + } catch (err: any) { + setMessage({ + type: 'error', + text: err?.response?.data?.details || err?.response?.data?.error || 'Error al restaurar la base de datos', + }) + } finally { + setRestoring(false) + } + } + return (

    @@ -42,6 +69,16 @@ export function AdminSettings() { Configuración del Sistema

    + {message && ( +
    + {message.text} +
    + )} +

    @@ -75,16 +112,6 @@ export function AdminSettings() { )} - - {message && ( -
    - {message.text} -
    - )}

    @@ -112,6 +139,47 @@ export function AdminSettings() { Descargar Backup .zip
    + +
    +

    + + Restaurar Base de Datos +

    +

    + Sube un archivo de copia de seguridad (.sql o .zip) para restaurar la base de datos. + La restauración sobrescribe los datos existentes. Se recomienda restaurar después de un reset. +

    +
    + setRestoreFile(e.target.files?.[0] ?? null)} + className="block w-full max-w-sm text-sm text-gray-500 dark:text-gray-400 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:bg-blue-600 file:text-white file:cursor-pointer hover:file:bg-blue-700" + /> + +
    + {restoreFile && ( +

    + Archivo seleccionado: {restoreFile.name} +

    + )} +
    diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 61e77b8..ec1c1d5 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -132,9 +132,7 @@ export const apiService = { importFeeds: async (file: File): Promise<{ imported: number; skipped: number; failed: number; message: string }> => { const formData = new FormData() formData.append('file', file) - const { data } = await api.post('/feeds/import', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - }) + const { data } = await api.post('/feeds/import', formData) return data }, @@ -192,4 +190,11 @@ export const apiService = { alert('Error al descargar la copia de seguridad. Verifica tu conexión o permisos.'); } }, + + restoreDatabase: async (file: File): Promise<{ message: string; filename: string; output?: string }> => { + const formData = new FormData() + formData.append('file', file) + const { data } = await api.post('/admin/restore', formData) + return data + }, } diff --git a/init-db/36-add_eventos_cluster_columns.sql b/init-db/36-add_eventos_cluster_columns.sql new file mode 100644 index 0000000..0f0796e --- /dev/null +++ b/init-db/36-add_eventos_cluster_columns.sql @@ -0,0 +1,11 @@ +BEGIN; + +-- Columnas para el clustering de noticias (cluster_worker.py) +ALTER TABLE eventos + ADD COLUMN IF NOT EXISTS centroid JSONB, + ADD COLUMN IF NOT EXISTS total_traducciones INTEGER DEFAULT 1, + ADD COLUMN IF NOT EXISTS fecha_inicio TIMESTAMP, + ADD COLUMN IF NOT EXISTS fecha_fin TIMESTAMP, + ADD COLUMN IF NOT EXISTS n_noticias INTEGER DEFAULT 1; + +COMMIT; diff --git a/init-db/37-topics-seed.sql b/init-db/37-topics-seed.sql new file mode 100644 index 0000000..2ddc5c7 --- /dev/null +++ b/init-db/37-topics-seed.sql @@ -0,0 +1,18 @@ +BEGIN; + +INSERT INTO topics (nombre, descripcion, weight, keywords) VALUES +('Política', 'Noticias políticas y de gobierno', 3, 'política,político,gobierno,presidente,ministro,parlamento,elecciones,partido,senado,diputado'), +('Economía', 'Economía y finanzas', 3, 'economía,económico,finanzas,mercado,inflación,banco,bancos,empresa,empresas,negocio,bolsa,inversión,presupuesto,impuestos'), +('Seguridad', 'Seguridad y defensa', 3, 'seguridad,ataque,ataques,explosión,terrorismo,terrorista,ejército,militar,patrulla,armas,policía'), +('Afganistán', 'Asuntos de Afganistán', 4, 'afganistán,afgano,afgana,kabul,candahar,kandahar,herat,mazar,talibán,talibanes,emirato'), +('Conflicto', 'Conflictos y guerra', 3, 'conflicto,guerra,enfrentamiento,enfrentamientos,crisis,víctimas,frontera,invasIón,ocupación'), +('Internacional', 'Relaciones internacionales', 2, 'internacional,diplomacia,diplomático,acuerdo,acuerdos,naciones unidas,embajador,tratado,extranjero'), +('Deportes', 'Deportes y competiciones', 2, 'deportes,deporte,fútbol,futbol,partido,equipo,liga,campeonato,tenis,baloncesto,cricket,selección'), +('Salud', 'Salud y medicina', 2, 'salud,hospital,médico,médicos,enfermedad,vacuna,tratamiento,pandemia,clínica,medicina'), +('Tecnología', 'Tecnología y digital', 2, 'tecnología,internet,digital,telefonía,telecomunicaciones,software,aplicación,aplicaciones,internet,tecnológico'), +('Educación', 'Educación y enseñanza', 2, 'educación,escuela,escuelas,universidad,universidades,estudiantes,profesor,enseñanza,academia,alumnos'), +('Cultura', 'Cultura y arte', 1, 'cultura,arte,literatura,cine,música,música,patrimonio,historia,museo,libro,libros'), +('Medio Ambiente', 'Clima y medio ambiente', 2, 'medio ambiente,clima,cambio climático,contaminación,sequía,inundación,agua,ecología,desastre natural') +ON CONFLICT (nombre) DO NOTHING; + +COMMIT; diff --git a/init-db/38-add_news_topics_score.sql b/init-db/38-add_news_topics_score.sql new file mode 100644 index 0000000..10b0bbe --- /dev/null +++ b/init-db/38-add_news_topics_score.sql @@ -0,0 +1,7 @@ +BEGIN; + +-- El worker topics escribe en score (topics/main.go usa news_topics.score) +ALTER TABLE news_topics + ADD COLUMN IF NOT EXISTS score INTEGER DEFAULT 0; + +COMMIT; diff --git a/init-db/39-add_related_traduccion_id.sql b/init-db/39-add_related_traduccion_id.sql new file mode 100644 index 0000000..fad7ba7 --- /dev/null +++ b/init-db/39-add_related_traduccion_id.sql @@ -0,0 +1,10 @@ +BEGIN; + +-- El worker related (cmd/related/main.go) trabaja con traducciones +ALTER TABLE related_noticias + ADD COLUMN IF NOT EXISTS related_traduccion_id INTEGER REFERENCES traducciones(id) ON DELETE CASCADE; + +CREATE UNIQUE INDEX IF NOT EXISTS idx_related_tr_pair + ON related_noticias(traduccion_id, related_traduccion_id); + +COMMIT; diff --git a/init-db/40-add_vectorization_columns.sql b/init-db/40-add_vectorization_columns.sql new file mode 100644 index 0000000..f504d86 --- /dev/null +++ b/init-db/40-add_vectorization_columns.sql @@ -0,0 +1,2 @@ +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS vectorization_date TIMESTAMP; +ALTER TABLE traducciones ADD COLUMN IF NOT EXISTS qdrant_point_id TEXT; diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 1000186..3e45878 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -73,6 +73,8 @@ CT2_MODEL_PATH = _env_str("CT2_MODEL_PATH", "/app/models/nllb-ct2") CT2_DEVICE = _env_str("CT2_DEVICE", "cpu") CT2_COMPUTE_TYPE = _env_str("CT2_COMPUTE_TYPE", "int8") UNIVERSAL_MODEL = _env_str("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M") +CT2_INTRA_THREADS = _env_int("CT2_INTRA_THREADS", 0) +CT2_INTER_THREADS = _env_int("CT2_INTER_THREADS", 1) BODY_CHARS_CHUNK = _env_int("BODY_CHARS_CHUNK", 900) LANG_CODE_MAP = { @@ -128,7 +130,7 @@ def ensure_model(): all_files_ok = True for f in required_files: fpath = os.path.join(model_path, f) - if not os.path.exists(fpath) or os.path.getsize(fpath) < 1000: + if not os.path.exists(fpath) or os.path.getsize(fpath) < 100: all_files_ok = False break @@ -157,6 +159,8 @@ def ensure_model(): model_path, device=device, compute_type=CT2_COMPUTE_TYPE, + inter_threads=CT2_INTER_THREADS, + intra_threads=CT2_INTRA_THREADS, ) _tokenizer = AutoTokenizer.from_pretrained(UNIVERSAL_MODEL) @@ -314,11 +318,7 @@ def translate_body_long(src: str, tgt: str, body: str) -> str: if len(chunks) == 1: return translate_texts(src, tgt, [body])[0] - translated_chunks = [] - for ch in chunks: - tr = translate_texts(src, tgt, [ch])[0] - translated_chunks.append(tr) - + translated_chunks = translate_texts(src, tgt, chunks) return " ".join(translated_chunks) @@ -406,18 +406,34 @@ def process_batch(conn, rows): titles = [i["titulo"] for i in items] translated_titles = translate_texts(lang_from, lang_to, titles) - for item, tt in zip(items, translated_titles): + # Collect all body chunks across all items for a single batched call + flat_chunks = [] + flat_keys = [] + for item in items: body = (item["resumen"] or "").strip() - tb = "" if body: - try: - tb = translate_body_long(lang_from, lang_to, body) - except Exception as e: - LOG.error(f"Body translation error for ID {item['tr_id']}: {e}") - tb = item["resumen"] + chunks = split_body_into_chunks(body) + flat_chunks.extend(chunks) + flat_keys.extend([(item["tr_id"], i) for i in range(len(chunks))]) - tt = clean_text((tt or "").strip()) - tb = clean_text((tb or "").strip()) + translated_bodies = [] + if flat_chunks: + try: + translated_bodies = translate_texts(lang_from, lang_to, flat_chunks) + except Exception as e: + LOG.error(f"Batch body translation error: {e}") + translated_bodies = flat_chunks + + body_parts = defaultdict(list) + for (tr_id, _), tr in zip(flat_keys, translated_bodies): + if tr is None: + continue + body_parts[tr_id].append(tr) + + for idx, item in enumerate(items): + tt = clean_text((translated_titles[idx] or "").strip()) + parts = body_parts.get(item["tr_id"]) + tb = clean_text(" ".join(parts).strip()) if parts else "" if not tt: tt = item["titulo"] From 71d345ec350a90ea09d1051ec1d9e8adfeeaacad Mon Sep 17 00:00:00 2001 From: jlimolina Date: Mon, 10 Aug 2026 16:11:42 +0200 Subject: [PATCH 17/21] cambios --- rss-ingestor-go/main.go | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/rss-ingestor-go/main.go b/rss-ingestor-go/main.go index 9d94833..66f9f7d 100644 --- a/rss-ingestor-go/main.go +++ b/rss-ingestor-go/main.go @@ -14,7 +14,7 @@ import ( "time" "github.com/PuerkitoBio/goquery" - "github.com/lib/pq" + _ "github.com/lib/pq" "github.com/mmcdole/gofeed" ) @@ -302,35 +302,10 @@ func processFeed(fp *gofeed.Parser, feed Feed, results chan<- int) { } func insertNoticias(noticias []Noticia) int { - if len(noticias) == 0 { - return 0 - } - - // Using COPY for bulk insert is most efficient, but complexity with handling conflicts - // "ON CONFLICT DO NOTHING" works best with normal INSERT. - // For simplicity and correctness with "ON CONFLICT", we use transaction and prepared statement. - // For very high performance, we could channel all to a batch writer. - // Given 1400 feeds, batch of 10-20 items per feed, standard Insert is okay if parallelized. - - txn, err := db.Begin() - if err != nil { - log.Printf("Error beginning txn: %v", err) - return 0 - } - defer txn.Rollback() - - stmt, err := txn.Prepare(pq.CopyIn("noticias", "id", "titulo", "resumen", "url", "fecha", "imagen_url", "fuente_nombre", "categoria_id", "pais_id")) - if err != nil { - // Fallback to individual inserts if CopyIn is too complex with ON CONFLICT (CopyIn doesn't support ON CONFLICT natively easily without temp tables) - // Let's use multi-row INSERT with ON CONFLICT. - return insertNoticiasWithConflict(noticias) - } - defer stmt.Close() - - // WAIT. lib/pq CopyIn does NOT support ON CONFLICT DO NOTHING. - // It will fail if duplicates exist. Since we expect duplicates (RSS feeds repeat items), - // CopyIn is risky directly into main table. - // Strategy: Use INSERT ... ON CONFLICT DO NOTHING with unnest or VALUES. + // Bulk insert with INSERT ... ON CONFLICT DO NOTHING. + // NOTE: do NOT use pq.CopyIn here: preparing a CopyIn leaves the backend in + // COPY mode waiting for data, which can hold an ACCESS EXCLUSIVE lock on the + // table and block the whole database if the stream is never completed. return insertNoticiasWithConflict(noticias) } From e3936fd4cfe481999dbae0cf42f81ae9b62b3dd3 Mon Sep 17 00:00:00 2001 From: jlimolina Date: Mon, 10 Aug 2026 18:27:34 +0200 Subject: [PATCH 18/21] =?UTF-8?q?mejora:=20rendimiento=20captura=20RSS=20y?= =?UTF-8?q?=20velocidad=20de=20traducci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ingestor: reactivar ETag/Last-Modified (304), http.Client compartido, rate-limit por host y Retry-After - Traducción: batch 128, cache en Redis con TTL (efímera), commit por lote - Scraper: worker pool paralelo (SCRAPER_WORKERS) - Config: compose lee RSS_/TRANSLATOR_ variables del .env (fuente única) --- .gitignore | 1 + Dockerfile.translator | 3 +- backend/cmd/scraper/main.go | 54 +++++++++++---- docker-compose.yml | 12 ++-- rss-ingestor-go/main.go | 91 ++++++++++++++++++++---- workers/ctranslator_worker.py | 126 +++++++++++++++++++++++++++++----- 6 files changed, 239 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 028c9fb..61b736a 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ models/nllb-ct2-1.3b/ # Wikipedia images - downloaded by wiki_worker data/wiki_images/ data/backups/ +backups/ # Database storage (PostgreSQL) data/pgdata/ diff --git a/Dockerfile.translator b/Dockerfile.translator index e6a96be..0f8fdcc 100644 --- a/Dockerfile.translator +++ b/Dockerfile.translator @@ -23,7 +23,8 @@ RUN pip install --no-cache-dir \ protobuf==3.20.3 \ "numpy<2" \ psycopg2-binary \ - langdetect + langdetect \ + redis # === ARREGLAR EL EXECUTABLE STACK === RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \ diff --git a/backend/cmd/scraper/main.go b/backend/cmd/scraper/main.go index bfb04e3..d8bdce6 100644 --- a/backend/cmd/scraper/main.go +++ b/backend/cmd/scraper/main.go @@ -10,6 +10,7 @@ import ( "os/signal" "strconv" "strings" + "sync" "syscall" "time" @@ -25,6 +26,7 @@ var ( sleepInterval = 60 batchSize = 10 enrichLimit = 20 + scraperWorkers = 5 ) type URLSource struct { @@ -63,6 +65,7 @@ func loadConfig() { sleepInterval = getEnvInt("SCRAPER_SLEEP", 60) batchSize = getEnvInt("SCRAPER_BATCH", 10) enrichLimit = getEnvInt("SCRAPER_ENRICH_LIMIT", 20) + scraperWorkers = getEnvInt("SCRAPER_WORKERS", 5) } func getEnvInt(key string, defaultValue int) int { @@ -406,6 +409,41 @@ func processSource(ctx context.Context, source URLSource) { } } +// enrichConcurrent enriches noticias in parallel with a bounded worker pool. +func enrichConcurrent(ctx context.Context, noticias []Noticia) { + limiter := make(chan struct{}, scraperWorkers) + var wg sync.WaitGroup + for _, noticia := range noticias { + wg.Add(1) + limiter <- struct{}{} + go func(n Noticia) { + defer wg.Done() + defer func() { <-limiter }() + if processEnrichment(ctx, n) { + time.Sleep(1 * time.Second) + } + }(noticia) + } + wg.Wait() +} + +// scrapeSources processes fuentes_url in parallel with a bounded worker pool. +func scrapeSources(ctx context.Context, sources []URLSource) { + limiter := make(chan struct{}, scraperWorkers) + var wg sync.WaitGroup + for _, source := range sources { + wg.Add(1) + limiter <- struct{}{} + go func(src URLSource) { + defer wg.Done() + defer func() { <-limiter }() + time.Sleep(1 * time.Second) // polite pacing across workers + processSource(ctx, src) + }(source) + } + wg.Wait() +} + func main() { loadConfig() logger.Println("Starting Scraper Worker") @@ -443,12 +481,8 @@ func main() { if err != nil { logger.Printf("Error fetching noticias to enrich: %v", err) } else if len(noticias) > 0 { - logger.Printf("Enriching %d noticias", len(noticias)) - for _, noticia := range noticias { - if processEnrichment(ctx, noticia) { - time.Sleep(3 * time.Second) - } - } + logger.Printf("Enriching %d noticias (%d workers)", len(noticias), scraperWorkers) + enrichConcurrent(ctx, noticias) } sources, err := getActiveURLs(ctx) @@ -462,12 +496,8 @@ func main() { continue } - logger.Printf("Processing %d sources", len(sources)) - - for _, source := range sources { - processSource(ctx, source) - time.Sleep(2 * time.Second) // Rate limiting - } + logger.Printf("Processing %d sources (%d workers)", len(sources), scraperWorkers) + scrapeSources(ctx, sources) } } } diff --git a/docker-compose.yml b/docker-compose.yml index a6f2eb4..ebc4e70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,8 +92,9 @@ services: DB_NAME: ${DB_NAME:-rss} DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} - RSS_MAX_WORKERS: 100 - RSS_POKE_INTERVAL_MIN: 60 + RSS_MAX_WORKERS: ${RSS_MAX_WORKERS:-100} + RSS_POKE_INTERVAL_MIN: ${RSS_POKE_INTERVAL_MIN:-60} + RSS_HOST_DELAY_MS: ${RSS_HOST_DELAY_MS:-500} TZ: Europe/Madrid networks: - backend @@ -155,6 +156,7 @@ services: SCRAPER_SLEEP: 60 SCRAPER_BATCH: 10 SCRAPER_ENRICH_LIMIT: 20 + SCRAPER_WORKERS: ${SCRAPER_WORKERS:-5} TZ: Europe/Madrid networks: - backend @@ -304,7 +306,7 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} TARGET_LANGS: es - TRANSLATOR_BATCH: 32 + TRANSLATOR_BATCH: ${TRANSLATOR_BATCH:-128} CT2_MODEL_PATH: /app/models/nllb-ct2 CT2_DEVICE: cpu CT2_COMPUTE_TYPE: int8 @@ -313,6 +315,7 @@ services: HF_HOME: /app/hf_cache TZ: Europe/Madrid TRANSLATOR_ID: ${TRANSLATOR_ID:-} + REDIS_URL: redis://:${REDIS_PASSWORD:-rss_redis_pass_2024}@redis:6379 PYTORCH_ENABLE_MPS_FALLBACK: 1 volumes: - ./workers:/app/workers @@ -341,7 +344,7 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} TARGET_LANGS: es - TRANSLATOR_BATCH: 32 + TRANSLATOR_BATCH: ${TRANSLATOR_BATCH:-128} CT2_MODEL_PATH: /app/models/nllb-ct2 CT2_DEVICE: cpu CT2_COMPUTE_TYPE: int8 @@ -350,6 +353,7 @@ services: HF_HOME: /app/hf_cache TZ: Europe/Madrid TRANSLATOR_ID: ${TRANSLATOR_ID:-} + REDIS_URL: redis://:${REDIS_PASSWORD:-rss_redis_pass_2024}@redis:6379 PYTORCH_ENABLE_MPS_FALLBACK: 1 volumes: - ./workers:/app/workers diff --git a/rss-ingestor-go/main.go b/rss-ingestor-go/main.go index 66f9f7d..4c49826 100644 --- a/rss-ingestor-go/main.go +++ b/rss-ingestor-go/main.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "net/http" + "net/url" "os" "strconv" "strings" @@ -29,6 +30,7 @@ type Config struct { MaxFailures int PokeInterval time.Duration FeedTimeout int + HostDelay time.Duration } // Feed represents a row in the feeds table @@ -57,10 +59,31 @@ type Noticia struct { } var ( - db *sql.DB - config Config + db *sql.DB + config Config + httpClient *http.Client ) +// Per-host politeness limiter so parallel workers don't hammer one domain. +var ( + rateMu sync.Mutex + lastRequest = make(map[string]time.Time) +) + +func politeDelay(host string) { + if config.HostDelay <= 0 || host == "" { + return + } + rateMu.Lock() + now := time.Now() + next := lastRequest[host].Add(config.HostDelay) + lastRequest[host] = next + rateMu.Unlock() + if wait := next.Sub(now); wait > 0 { + time.Sleep(wait) + } +} + func loadConfig() { config = Config{ DBHost: getEnv("DB_HOST", "localhost"), @@ -72,6 +95,19 @@ func loadConfig() { MaxFailures: getEnvInt("RSS_MAX_FAILURES", 10), PokeInterval: time.Duration(getEnvInt("RSS_POKE_INTERVAL_MIN", 8)) * time.Minute, FeedTimeout: getEnvInt("RSS_FEED_TIMEOUT", 60), + HostDelay: time.Duration(getEnvInt("RSS_HOST_DELAY_MS", 500)) * time.Millisecond, + } +} + +func initHTTPClient() { + httpClient = &http.Client{ + Timeout: time.Duration(config.FeedTimeout) * time.Second, + Transport: &http.Transport{ + MaxIdleConns: config.MaxWorkers * 2, + MaxIdleConnsPerHost: 2, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + }, } } @@ -200,11 +236,9 @@ func extractImage(item *gofeed.Item) string { } func processFeed(fp *gofeed.Parser, feed Feed, results chan<- int) { - // Configure custom HTTP client with timeout and User-Agent - client := &http.Client{ - Timeout: time.Duration(config.FeedTimeout) * time.Second, - } - + // Shared HTTP client with connection pooling across workers + client := httpClient + // Create request to set User-Agent req, err := http.NewRequest("GET", feed.URL, nil) if err != nil { @@ -215,10 +249,21 @@ func processFeed(fp *gofeed.Parser, feed Feed, results chan<- int) { } req.Header.Set("User-Agent", "RSS2-Ingestor-Go/1.0") - // NOTE: We INTENTIONALLY SKIP ETag/Last-Modified headers based on user issues - // If needed in future, uncomment: - // if feed.LastEtag.Valid { req.Header.Set("If-None-Match", feed.LastEtag.String) } - // if feed.LastModified.Valid { req.Header.Set("If-Modified-Since", feed.LastModified.String) } + // Reuse ETag/Last-Modified so servers can reply 304 Not Modified, + // avoiding re-downloading unchanged feeds in every cycle. + if feed.LastEtag.Valid && feed.LastEtag.String != "" { + req.Header.Set("If-None-Match", feed.LastEtag.String) + } + if feed.LastModified.Valid && feed.LastModified.String != "" { + req.Header.Set("If-Modified-Since", feed.LastModified.String) + } + + // Per-host politeness: pace parallel fetches to the same domain + host := feed.URL + if u, perr := url.Parse(feed.URL); perr == nil && u.Host != "" { + host = u.Host + } + politeDelay(host) resp, err := client.Do(req) if err != nil { @@ -229,10 +274,29 @@ func processFeed(fp *gofeed.Parser, feed Feed, results chan<- int) { } defer resp.Body.Close() + // Honor Retry-After on rate limits without punishing the feed with failures + if resp.StatusCode == 429 || resp.StatusCode == 503 { + ra, raErr := strconv.Atoi(resp.Header.Get("Retry-After")) + if raErr == nil && ra > 0 && ra <= 3600 { + log.Printf("[Feed %d] Rate limited (%d), sleeping %ds (Retry-After)", feed.ID, resp.StatusCode, ra) + time.Sleep(time.Duration(ra) * time.Second) + } + results <- 0 + return + } + + // On 304, keep the stored validators; update only if the server sends new ones. if resp.StatusCode == 304 { log.Printf("[Feed %d] Not Modified (304)", feed.ID) - // Update timestamp only? Or keep as is. - updateFeedStatus(feed.ID, feed.LastEtag.String, feed.LastModified.String, true, "") + newEtag := feed.LastEtag.String + newModified := feed.LastModified.String + if h := resp.Header.Get("ETag"); h != "" { + newEtag = h + } + if h := resp.Header.Get("Last-Modified"); h != "" { + newModified = h + } + updateFeedStatus(feed.ID, newEtag, newModified, true, "") results <- 0 return } @@ -433,6 +497,7 @@ func ingestCycle() { func main() { loadConfig() + initHTTPClient() initDB() // Run immediately on start diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 3e45878..b49df13 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -1,8 +1,9 @@ import os +import re import time import logging -import re import fcntl +import hashlib from typing import List, Optional import psycopg2 @@ -20,6 +21,62 @@ LOG = logging.getLogger("translator_ct2") TRANSLATOR_ID = os.environ.get("TRANSLATOR_ID", "") TRANSLATOR_TOTAL = int(os.environ.get("TRANSLATOR_TOTAL", "1")) +CACHE_TTL = int(os.environ.get("TRANSLATION_CACHE_TTL", str(30 * 24 * 3600))) +_redis_client = None + + +def get_redis(): + """Return a Redis client or None. Cache is best-effort: never blocks translation.""" + global _redis_client + if _redis_client is not None: + return _redis_client if _redis_client is not False else None + url = os.environ.get("REDIS_URL", "redis://localhost:6379") + try: + import redis + client = redis.Redis.from_url( + url, decode_responses=True, + socket_connect_timeout=2, socket_timeout=2, + ) + client.ping() + _redis_client = client + LOG.info("Redis translation cache enabled") + return client + except Exception as e: + LOG.warning(f"Redis cache unavailable, translating without cache: {e}") + _redis_client = False + return None + + +def cache_key(lang_from: str, lang_to: str, text: str) -> str: + digest = hashlib.md5((text or "").encode("utf-8", "ignore")).hexdigest() + return f"tr:{lang_from}:{lang_to}:{digest}" + + +def lookup_cache(keys: List[str]) -> dict: + r = get_redis() + if not r or not keys: + return {} + try: + vals = r.mget(keys) + return {k: v for k, v in zip(keys, vals) if v} + except Exception as e: + LOG.warning(f"Redis mget error: {e}") + return {} + + +def store_cache(pairs, ttl: int = CACHE_TTL): + r = get_redis() + if not r or not pairs: + return + try: + pipe = r.pipeline(transaction=False) + for k, v in pairs: + if v: + pipe.setex(k, ttl, v) + pipe.execute() + except Exception as e: + LOG.warning(f"Redis cache store error: {e}") + def clean_text(text: str) -> str: if not text: @@ -403,33 +460,64 @@ def process_batch(conn, rows): LOG.info(f"Translating {lang_from} -> {lang_to} ({len(items)} items)") try: + # --- TITLES (served from Redis cache when possible) --- titles = [i["titulo"] for i in items] - translated_titles = translate_texts(lang_from, lang_to, titles) + title_keys = [cache_key(lang_from, lang_to, t) for t in titles] + title_cache = lookup_cache(title_keys) + if title_cache: + LOG.info(f"Title cache hits: {len(title_cache)}/{len(titles)}") - # Collect all body chunks across all items for a single batched call + translated_titles = [title_cache.get(k) for k in title_keys] + title_miss = [i for i, k in enumerate(title_keys) if k not in title_cache] + if title_miss: + miss_texts = [titles[i] for i in title_miss] + miss_translated = translate_texts(lang_from, lang_to, miss_texts) + store_cache([(title_keys[i], tr) for i, tr in zip(title_miss, miss_translated)]) + for i, tr in zip(title_miss, miss_translated): + translated_titles[i] = tr + + # --- BODY chunks (single batched call for cache misses) --- flat_chunks = [] flat_keys = [] + flat_ck = [] for item in items: body = (item["resumen"] or "").strip() if body: chunks = split_body_into_chunks(body) flat_chunks.extend(chunks) flat_keys.extend([(item["tr_id"], i) for i in range(len(chunks))]) + flat_ck.extend([cache_key(lang_from, lang_to, c) for c in chunks]) - translated_bodies = [] - if flat_chunks: - try: - translated_bodies = translate_texts(lang_from, lang_to, flat_chunks) - except Exception as e: - LOG.error(f"Batch body translation error: {e}") - translated_bodies = flat_chunks + chunk_cache = lookup_cache(flat_ck) + if chunk_cache: + LOG.info(f"Body cache hits: {len(chunk_cache)}/{len(flat_ck)}") body_parts = defaultdict(list) - for (tr_id, _), tr in zip(flat_keys, translated_bodies): - if tr is None: - continue - body_parts[tr_id].append(tr) + miss_idx = [] + miss_texts = [] + miss_keys = [] + for idx, ck in enumerate(flat_ck): + tr_id, _ = flat_keys[idx] + if ck in chunk_cache: + body_parts[tr_id].append(chunk_cache[ck]) + else: + miss_idx.append(idx) + miss_texts.append(flat_chunks[idx]) + miss_keys.append(ck) + if miss_texts: + try: + miss_translated = translate_texts(lang_from, lang_to, miss_texts) + store_cache(list(zip(miss_keys, miss_translated))) + except Exception as e: + LOG.error(f"Batch body translation error: {e}") + miss_translated = miss_texts + for j, tr in enumerate(miss_translated): + if tr: + body_parts[flat_keys[miss_idx[j]][0]].append(tr) + + # --- BATCH COMMIT: single transaction per group --- + updates = [] for idx, item in enumerate(items): tt = clean_text((translated_titles[idx] or "").strip()) parts = body_parts.get(item["tr_id"]) @@ -440,21 +528,23 @@ def process_batch(conn, rows): if not tb: tb = item["resumen"] - # 2. INDIVIDUAL COMMIT: Save each item as it's done + updates.append((tt, tb, item["tr_id"])) + + if updates: try: cursor = conn.cursor() - cursor.execute( + cursor.executemany( """ UPDATE traducciones SET titulo_trad = %s, resumen_trad = %s, status = 'done', locked_at = NULL WHERE id = %s """, - (tt, tb, item["tr_id"]), + updates, ) conn.commit() cursor.close() except Exception as e: - LOG.error(f"Update error for ID {item['tr_id']}: {e}") + LOG.error(f"Batch update error (items stay pending for retry): {e}") conn.rollback() LOG.info(f"Finished group {lang_from} -> {lang_to}") From eff656adeabc1982e12c4105ee970bc9e30b316e Mon Sep 17 00:00:00 2001 From: jlimolina Date: Tue, 11 Aug 2026 02:37:48 +0200 Subject: [PATCH 19/21] muchas cosas --- backend/cmd/scraper/main.go | 6 +- backend/cmd/server/main.go | 4 + backend/cmd/topics/main.go | 48 ++++-- backend/cmd/wiki_worker/main.go | 19 ++- backend/internal/handlers/admin.go | 46 ++++++ backend/internal/handlers/news.go | 119 ++++++++++++++- backend/internal/handlers/search.go | 69 +++++++++ docker-compose.yml | 29 +++- frontend/src/pages/AdminWorkers.tsx | 42 +++++- frontend/src/pages/Feeds.tsx | 35 ++++- frontend/src/pages/Home.tsx | 21 ++- frontend/src/pages/Login.tsx | 9 ++ frontend/src/pages/Populares.tsx | 222 +++++++++++++++++++++++++++- frontend/src/pages/Search.tsx | 23 ++- frontend/src/services/api.ts | 19 +++ init-db/41-translation-metrics.sql | 4 + init-db/42-user-search-tags.sql | 11 ++ workers/cluster_worker.py | 2 +- workers/ctranslator_worker.py | 18 ++- workers/translation_scheduler.py | 2 +- 20 files changed, 697 insertions(+), 51 deletions(-) create mode 100644 init-db/41-translation-metrics.sql create mode 100644 init-db/42-user-search-tags.sql diff --git a/backend/cmd/scraper/main.go b/backend/cmd/scraper/main.go index d8bdce6..8d372ea 100644 --- a/backend/cmd/scraper/main.go +++ b/backend/cmd/scraper/main.go @@ -4,6 +4,7 @@ import ( "context" "crypto/md5" "fmt" + "io" "log" "net/http" "os" @@ -27,6 +28,7 @@ var ( batchSize = 10 enrichLimit = 20 scraperWorkers = 5 + maxBodyBytes = int64(512 << 10) ) type URLSource struct { @@ -169,7 +171,7 @@ func extractArticle(source URLSource) (*Article, error) { return nil, fmt.Errorf("HTTP %d", resp.StatusCode) } - doc, err := goquery.NewDocumentFromReader(resp.Body) + doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxBodyBytes)) if err != nil { return nil, err } @@ -256,7 +258,7 @@ func extractContentFromURL(url string) (string, error) { return "", fmt.Errorf("HTTP %d", resp.StatusCode) } - doc, err := goquery.NewDocumentFromReader(resp.Body) + doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxBodyBytes)) if err != nil { return "", err } diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index bfa59bd..bc0b77f 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -170,8 +170,11 @@ func main() { } api.GET("/search", handlers.SearchNews) + api.GET("/search/suggestions", middleware.AuthRequired(), handlers.SearchSuggestions) + api.POST("/searchlog", middleware.AuthRequired(), handlers.LogSearch) api.GET("/entities", handlers.GetEntities) + api.GET("/entities/news", handlers.GetEntityNews) api.GET("/stats", handlers.GetStats) @@ -193,6 +196,7 @@ func main() { admin.POST("/users/:id/demote", handlers.DemoteUser) admin.POST("/reset-db", handlers.ResetDatabase) admin.GET("/workers/status", handlers.GetWorkerStatus) + admin.GET("/workers/stats", handlers.GetTranslationStats) admin.POST("/workers/config", handlers.SetWorkerConfig) admin.POST("/workers/start", handlers.StartWorkers) admin.POST("/workers/stop", handlers.StopWorkers) diff --git a/backend/cmd/topics/main.go b/backend/cmd/topics/main.go index 3ab3ee8..a9e83c0 100644 --- a/backend/cmd/topics/main.go +++ b/backend/cmd/topics/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "log" "os" "os/signal" @@ -276,29 +277,46 @@ func processBatch(ctx context.Context, topics []Topic, countries []Country) (int processedIDs = append(processedIDs, item.ID) } - // Insert topic relations + // Insert topic relations (batched, upsert) if len(topicMatches) > 0 { - for _, tm := range topicMatches { - _, err := dbPool.Exec(ctx, ` + const chunkSize = 500 + for i := 0; i < len(topicMatches); i += chunkSize { + end := i + chunkSize + if end > len(topicMatches) { + end = len(topicMatches) + } + chunk := topicMatches[i:end] + values := make([]string, 0, len(chunk)) + args := make([]interface{}, 0, len(chunk)*3) + for j, tm := range chunk { + values = append(values, fmt.Sprintf("($%d, $%d, $%d)", j*3+1, j*3+2, j*3+3)) + args = append(args, tm.NoticiaID, tm.TopicID, tm.Score) + } + query := fmt.Sprintf(` INSERT INTO news_topics (noticia_id, topic_id, score) - VALUES ($1, $2, $3) + VALUES %s ON CONFLICT (noticia_id, topic_id) DO UPDATE SET score = EXCLUDED.score - `, tm.NoticiaID, tm.TopicID, tm.Score) - if err != nil { - logger.Printf("Error inserting topic: %v", err) + `, strings.Join(values, ",")) + if _, err := dbPool.Exec(ctx, query, args...); err != nil { + logger.Printf("Error batch inserting topics: %v", err) } } } - // Update country + // Update country (single bulk UPDATE) if len(countryUpdates) > 0 { - for _, cu := range countryUpdates { - _, err := dbPool.Exec(ctx, ` - UPDATE noticias SET pais_id = $1 WHERE id = $2 - `, cu.PaisID, cu.NoticiaID) - if err != nil { - logger.Printf("Error updating country: %v", err) - } + paisSlice := make([]int32, len(countryUpdates)) + idSlice := make([]string, len(countryUpdates)) + for i, cu := range countryUpdates { + paisSlice[i] = int32(cu.PaisID) + idSlice[i] = cu.NoticiaID + } + if _, err := dbPool.Exec(ctx, ` + UPDATE noticias n SET pais_id = u.pais + FROM unnest($1::int[], $2::varchar[]) AS u(pais, noticia_id) + WHERE n.id = u.noticia_id + `, paisSlice, idSlice); err != nil { + logger.Printf("Error bulk updating countries: %v", err) } } diff --git a/backend/cmd/wiki_worker/main.go b/backend/cmd/wiki_worker/main.go index c58e07c..f6298b1 100644 --- a/backend/cmd/wiki_worker/main.go +++ b/backend/cmd/wiki_worker/main.go @@ -23,8 +23,9 @@ var ( logger *log.Logger pool *pgxpool.Pool sleepInterval = 30 - batchSize = 50 + batchSize = 20 imagesDir = "/app/data/wiki_images" + maxImageBytes = int64(2 << 20) ) type WikiSummary struct { @@ -101,13 +102,17 @@ func downloadImage(imgURL, destPath string) error { return fmt.Errorf("HTTP %d", resp.StatusCode) } + if resp.ContentLength > maxImageBytes { + return fmt.Errorf("image too large (%d bytes)", resp.ContentLength) + } + out, err := os.Create(destPath) if err != nil { return err } defer out.Close() - _, err = io.Copy(out, resp.Body) + _, err = io.Copy(out, io.LimitReader(resp.Body, maxImageBytes)) return err } @@ -210,9 +215,15 @@ func processTag(ctx context.Context, tag Tag) { } func main() { + var sleepVal, batchVal int if val := os.Getenv("WIKI_SLEEP"); val != "" { - if sleep, err := fmt.Sscanf(val, "%d", &sleepInterval); err == nil && sleep > 0 { - sleepInterval = sleep + if _, err := fmt.Sscanf(val, "%d", &sleepVal); err == nil && sleepVal > 0 { + sleepInterval = sleepVal + } + } + if val := os.Getenv("WIKI_BATCH"); val != "" { + if _, err := fmt.Sscanf(val, "%d", &batchVal); err == nil && batchVal > 0 { + batchSize = batchVal } } diff --git a/backend/internal/handlers/admin.go b/backend/internal/handlers/admin.go index 48c9e9f..538cf5c 100644 --- a/backend/internal/handlers/admin.go +++ b/backend/internal/handlers/admin.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "log" + "math" "net/http" "net/url" "os" @@ -386,6 +387,51 @@ func GetWorkerStatus(c *gin.Context) { }) } +// GetTranslationStats returns real translation throughput and live worker count. +func GetTranslationStats(c *gin.Context) { + ctx := c.Request.Context() + var last1min, last5min, activeWorkers int64 + var elapsed1min, elapsed5min float64 + + db.GetPool().QueryRow(ctx, ` + SELECT COALESCE(SUM(items_translated),0), COALESCE(SUM(elapsed_ms),0) + FROM translation_stats WHERE created_at >= NOW() - INTERVAL '1 minute' + `).Scan(&last1min, &elapsed1min) + + db.GetPool().QueryRow(ctx, ` + SELECT COALESCE(SUM(items_translated),0), COALESCE(SUM(elapsed_ms),0) + FROM translation_stats WHERE created_at >= NOW() - INTERVAL '5 minutes' + `).Scan(&last5min, &elapsed5min) + + // Live workers = distinct hosts that recorded stats in the last 2 minutes + db.GetPool().QueryRow(ctx, ` + SELECT COUNT(DISTINCT hostname) FROM translation_stats + WHERE created_at >= NOW() - INTERVAL '2 minutes' AND hostname IS NOT NULL + `).Scan(&activeWorkers) + + // Also count WS remote workers currently online + remoteOnline := 0 + if err := db.GetPool().QueryRow(ctx, ` + SELECT COUNT(*) FROM remote_workers WHERE status = 'online' + `).Scan(&remoteOnline); err != nil { + remoteOnline = 0 + } + + rateSec := 0.0 + if elapsed1min > 0 { + rateSec = float64(last1min) / (elapsed1min / 1000.0) + } + + c.JSON(http.StatusOK, gin.H{ + "translations_last_1min": last1min, + "translations_last_5min": last5min, + "rate_per_second": math.Round(rateSec*100) / 100, + "rate_per_minute": math.Round(rateSec*60*100) / 100, + "active_workers": activeWorkers + int64(remoteOnline), + "remote_workers_online": remoteOnline, + }) +} + func SetWorkerConfig(c *gin.Context) { var req WorkerConfig if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/internal/handlers/news.go b/backend/internal/handlers/news.go index 188d87c..f21c189 100644 --- a/backend/internal/handlers/news.go +++ b/backend/internal/handlers/news.go @@ -49,7 +49,13 @@ func GetNews(c *gin.Context) { argNum := 1 if query != "" { - where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d)", argNum, argNum) + if translatedOnly { + // With translated_only, search the translated fields too so users can + // find translated news by the Spanish (translated) wording. + where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d OR t.titulo_trad ILIKE $%d OR t.resumen_trad ILIKE $%d)", argNum, argNum, argNum, argNum) + } else { + where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d)", argNum, argNum) + } args = append(args, "%"+query+"%") argNum++ } @@ -149,6 +155,117 @@ func GetNews(c *gin.Context) { }) } +// GetEntityNews returns the (translated) news that mention a given entity/alias value. +func GetEntityNews(c *gin.Context) { + valor := c.Query("valor") + tipo := c.DefaultQuery("tipo", "persona") + pageStr := c.DefaultQuery("page", "1") + perPageStr := c.DefaultQuery("per_page", "20") + + page, _ := strconv.Atoi(pageStr) + perPage, _ := strconv.Atoi(perPageStr) + if page < 1 { + page = 1 + } + if perPage < 1 || perPage > 50 { + perPage = 20 + } + offset := (page - 1) * perPage + + daysStr := c.Query("days") + days := 0 + if daysStr != "" { + days, _ = strconv.Atoi(daysStr) + if days < 1 { + days = 0 + } + } + today := c.Query("today") == "true" + offsetStr := c.Query("day_offset") + dayOffset := 0 + useDayOffset := false + if offsetStr != "" { + dayOffset, _ = strconv.Atoi(offsetStr) + if dayOffset < 0 { + dayOffset = 0 + } + useDayOffset = true + } + + params := []interface{}{valor, tipo} + next := 3 + timeCond := "" + switch { + case useDayOffset: + // Día exacto: desde el inicio del día (hoy - offset) hasta el inicio del día siguiente + timeCond = fmt.Sprintf( + " AND n.fecha >= (CURRENT_DATE - %d)::timestamp AND n.fecha < (CURRENT_DATE - %d + 1)::timestamp", + dayOffset, dayOffset) + case today: + timeCond = " AND n.fecha >= date_trunc('day', NOW())" + case days > 0: + timeCond = fmt.Sprintf(" AND n.fecha >= NOW() - make_interval(days => $%d)", next) + params = append(params, days) + next++ + } + + base := fmt.Sprintf(` + FROM tags_noticia tn + JOIN tags t ON tn.tag_id = t.id + JOIN traducciones tr ON tn.traduccion_id = tr.id + JOIN noticias n ON tr.noticia_id = n.id + LEFT JOIN entity_aliases ea ON LOWER(ea.alias) = LOWER(t.valor) AND ea.tipo = t.tipo + WHERE LOWER(COALESCE(ea.canonical_name, t.valor)) = LOWER($1) AND t.tipo = $2%s`, timeCond) + + var total int + if err := db.GetPool().QueryRow(c.Request.Context(), + fmt.Sprintf("SELECT COUNT(DISTINCT tn.noticia_id) %s", base), params...).Scan(&total); err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to count entity news", Message: err.Error()}) + return + } + + query := fmt.Sprintf(` + SELECT DISTINCT n.id, n.titulo, COALESCE(n.resumen,''), n.url, n.fecha, n.imagen_url, + n.fuente_nombre, tr.titulo_trad, tr.resumen_trad + %s + ORDER BY n.fecha DESC + LIMIT $%d OFFSET $%d`, base, next, next+1) + + rows, err := db.GetPool().Query(c.Request.Context(), query, append(params, perPage, offset)...) + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to get entity news", Message: err.Error()}) + return + } + defer rows.Close() + + var news []NewsResponse + for rows.Next() { + var n NewsResponse + var img *string + if err := rows.Scan(&n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &img, + &n.FuenteNombre, &n.TitleTranslated, &n.SummaryTranslated); err != nil { + continue + } + if img != nil { + n.ImagenURL = img + } + news = append(news, n) + } + if news == nil { + news = []NewsResponse{} + } + + totalPages := (total + perPage - 1) / perPage + + c.JSON(http.StatusOK, gin.H{ + "news": news, + "total": total, + "page": page, + "per_page": perPage, + "total_pages": totalPages, + }) +} + func GetNewsByID(c *gin.Context) { id := c.Param("id") diff --git a/backend/internal/handlers/search.go b/backend/internal/handlers/search.go index 23b2999..ef5314f 100644 --- a/backend/internal/handlers/search.go +++ b/backend/internal/handlers/search.go @@ -3,13 +3,82 @@ package handlers import ( "net/http" "strconv" + "strings" "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/auth" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" "github.com/rss2/backend/internal/services" ) +// LogSearch records a logged-in user's search term so frequent searches gain priority. +func LogSearch(c *gin.Context) { + user := c.MustGet("user").(*auth.Claims) + var req struct { + Query string `json:"q"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "Invalid request"}) + return + } + term := strings.ToLower(strings.TrimSpace(req.Query)) + if term == "" { + c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "q requerido"}) + return + } + if len(term) > 100 { + term = term[:100] + } + + _, err := db.GetPool().Exec(c.Request.Context(), ` + INSERT INTO user_search_tags (user_id, term, count, last_used) + VALUES ($1, $2, 1, NOW()) + ON CONFLICT (user_id, term) + DO UPDATE SET count = user_search_tags.count + 1, last_used = NOW() + `, user.UserID, term) + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to save search", Message: err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"ok": true}) +} + +// SearchSuggestions returns the user's most used search terms (dynamic tags). +func SearchSuggestions(c *gin.Context) { + user := c.MustGet("user").(*auth.Claims) + q := strings.TrimSpace(c.Query("q")) + + query := ` + SELECT term FROM user_search_tags + WHERE user_id = $1` + args := []interface{}{user.UserID} + if q != "" { + query += ` AND term ILIKE $2` + args = append(args, "%"+q+"%") + } + query += ` ORDER BY count DESC, last_used DESC LIMIT 10` + + rows, err := db.GetPool().Query(c.Request.Context(), query, args...) + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to get suggestions", Message: err.Error()}) + return + } + defer rows.Close() + + terms := []string{} + for rows.Next() { + var t string + if err := rows.Scan(&t); err != nil { + continue + } + terms = append(terms, t) + } + + c.JSON(http.StatusOK, gin.H{"terms": terms}) +} + func SearchNews(c *gin.Context) { query := c.Query("q") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) diff --git a/docker-compose.yml b/docker-compose.yml index ebc4e70..ef59ad1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,6 +46,7 @@ services: deploy: resources: limits: + cpus: '2' memory: 8G reservations: memory: 4G @@ -94,7 +95,7 @@ services: DB_PASS: ${DB_PASS} RSS_MAX_WORKERS: ${RSS_MAX_WORKERS:-100} RSS_POKE_INTERVAL_MIN: ${RSS_POKE_INTERVAL_MIN:-60} - RSS_HOST_DELAY_MS: ${RSS_HOST_DELAY_MS:-500} + RSS_HOST_DELAY_MS: ${RSS_HOST_DELAY_MS:-800} TZ: Europe/Madrid networks: - backend @@ -153,10 +154,10 @@ services: DB_NAME: ${DB_NAME:-rss} DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} - SCRAPER_SLEEP: 60 + SCRAPER_SLEEP: 120 SCRAPER_BATCH: 10 - SCRAPER_ENRICH_LIMIT: 20 - SCRAPER_WORKERS: ${SCRAPER_WORKERS:-5} + SCRAPER_ENRICH_LIMIT: 10 + SCRAPER_WORKERS: ${SCRAPER_WORKERS:-2} TZ: Europe/Madrid networks: - backend @@ -215,6 +216,7 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} WIKI_SLEEP: 10 + WIKI_BATCH: 20 TZ: Europe/Madrid volumes: - ./data/wiki_images:/app/data/wiki_images @@ -255,6 +257,11 @@ services: redis: condition: service_healthy restart: unless-stopped + deploy: + resources: + limits: + cpus: '2' + memory: 512M # ================================================================================== # FRONTEND REACT @@ -272,6 +279,11 @@ services: depends_on: - backend-go restart: unless-stopped + deploy: + resources: + limits: + cpus: '1' + memory: 512M # ================================================================================== # NGINX (Puerto 8001 - sirve React + proxy API) @@ -382,7 +394,8 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} TARGET_LANGS: es - SCHEDULER_BATCH: 1000 + SCHEDULER_BATCH: 200 + SCHEDULER_AGE_DAYS: 30 SCHEDULER_SLEEP: 30 TZ: Europe/Madrid volumes: @@ -412,10 +425,10 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} EMB_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 - EMB_BATCH: 64 + EMB_BATCH: 32 EMB_SLEEP_IDLE: 5 EMB_LANGS: es - EMB_LIMIT: 1000 + EMB_LIMIT: 300 DEVICE: cpu HF_HOME: /app/hf_cache TZ: Europe/Madrid @@ -565,7 +578,7 @@ services: DB_USER: ${DB_USER:-rss} DB_PASS: ${DB_PASS} NER_LANG: es - NER_BATCH: 64 + NER_BATCH: 24 HF_HOME: /app/hf_cache TZ: Europe/Madrid volumes: diff --git a/frontend/src/pages/AdminWorkers.tsx b/frontend/src/pages/AdminWorkers.tsx index cd5e0f2..5f2aa5d 100644 --- a/frontend/src/pages/AdminWorkers.tsx +++ b/frontend/src/pages/AdminWorkers.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { api } from '../services/api' -import { Cpu, Play, Square, Settings, RefreshCw, Loader2, Server, Wifi, Plus, Trash2, Key } from 'lucide-react' +import { Cpu, Play, Square, Settings, RefreshCw, Loader2, Server, Wifi, Plus, Trash2, Key, TrendingUp } from 'lucide-react' interface WorkerStatus { type: string @@ -9,6 +9,15 @@ interface WorkerStatus { running: number } +interface TranslationStats { + translations_last_1min: number + translations_last_5min: number + rate_per_second: number + rate_per_minute: number + active_workers: number + remote_workers_online: number +} + interface RemoteWorker { id: number name: string @@ -21,6 +30,7 @@ interface RemoteWorker { export function AdminWorkers() { const [status, setStatus] = useState(null) + const [tStats, setTStats] = useState(null) const [loading, setLoading] = useState(true) const [actionLoading, setActionLoading] = useState(null) const [configType, setConfigType] = useState<'cpu' | 'gpu'>('cpu') @@ -40,6 +50,9 @@ export function AdminWorkers() { setStatus(res.data) setConfigType(res.data.type) setConfigWorkers(res.data.workers) + + const statsRes = await api.get('/admin/workers/stats') + setTStats(statsRes.data) } catch (err: any) { console.error('Error fetching status:', err) } finally { @@ -294,7 +307,32 @@ export function AdminWorkers() {
    Workers activos - {status?.running || 0} + {tStats?.active_workers ?? status?.running ?? 0} +
    + +
    +

    + + Rendimiento de Traducción +

    +
    +
    +
    {tStats?.rate_per_second ?? 0}
    +
    noticias/seg
    +
    +
    +
    {tStats?.rate_per_minute ?? 0}
    +
    noticias/min
    +
    +
    +
    {tStats?.translations_last_1min ?? 0}
    +
    últ. 1 min
    +
    +
    +
    + {tStats?.translations_last_5min ?? 0} traducidas en los últimos 5 min ·{' '} + {tStats?.remote_workers_online ?? 0} remotos online +
    diff --git a/frontend/src/pages/Feeds.tsx b/frontend/src/pages/Feeds.tsx index 5228c21..e782966 100644 --- a/frontend/src/pages/Feeds.tsx +++ b/frontend/src/pages/Feeds.tsx @@ -10,15 +10,18 @@ export function Feeds() { const [filtroActivo, setFiltroActivo] = useState('') const [filtroCategoria, setFiltroCategoria] = useState('') const [filtroPais, setFiltroPais] = useState('') + const [page, setPage] = useState(1) const fileInputRef = useRef(null) const [importResult, setImportResult] = useState<{ imported: number; skipped: number; failed: number; message: string } | null>(null) const { data: feedsData, isLoading } = useQuery({ - queryKey: ['feeds', filtroActivo, filtroCategoria, filtroPais], + queryKey: ['feeds', filtroActivo, filtroCategoria, filtroPais, page], queryFn: () => apiService.getFeeds({ activo: filtroActivo || undefined, categoria_id: filtroCategoria || undefined, pais_id: filtroPais || undefined, + page, + per_page: 50, }), }) @@ -165,7 +168,7 @@ export function Feeds() {
    setTipo(e.target.value)} className="input w-auto"> + {TIPOS.map((t) => ( + + ))} + +
    +
    + +
    + + { + setQuery(e.target.value) + setShowResults(true) + }} + onFocus={() => setShowResults(true)} + placeholder="Busca un concepto…" + className="input pl-9" + /> +
    + {showResults && query.trim() && ( +
      + {results.length === 0 && ( +
    • Sin resultados
    • + )} + {results.map((r) => ( +
    • + +
    • + ))} +
    + )} +
    +
    + +
    + {DIAS.map((d) => ( + + ))} +
    +
    +
    + + {selected.length > 0 && ( +
    + {selected.map((s) => ( + + {s.valor} + + + ))} +
    + )} + + + {selected.length === 0 ? ( +
    + Añade al menos un concepto para ver su evolución por día. +
    + ) : loading ? ( +
    +
    +
    + ) : ( +
    + +
    + )} +
    + ) +} \ No newline at end of file diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 7228238..929fdf4 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -95,6 +95,41 @@ export interface Country { continente: string } +export interface MentionPoint { + fecha: string + count: number +} + +export interface MentionSeries { + valor: string + tipo: string + count: number + data: MentionPoint[] +} + +export interface MentionsResponse { + days: number + series: MentionSeries[] +} + +export interface EntitySuggestion { + valor: string + tipo: string + cnt: number +} + +export interface Alerta { + id: number + valor: string + tipo: string + periodo: string + hits: number + baseline: number + ratio: number + status: string + created_at: string +} + export interface Stats { total_news: number total_feeds: number @@ -175,6 +210,31 @@ export const apiService = { return data }, + getEntityMentions: async (params: { values: string[]; days?: number }): Promise => { + const { data } = await api.get('/entities/mentions', { + params: { values: params.values.join(','), days: params.days || 30 }, + }) + return data + }, + + searchEntities: async (params: { tipo: string; q?: string; page?: number; per_page?: number }): Promise<{ entities: EntitySuggestion[]; total: number }> => { + const { data } = await api.get('/entities', { params }) + return data + }, + + getAlertas: async (params?: { status?: string; limit?: number }): Promise<{ alertas: Alerta[]; total: number; nuevas: number }> => { + const { data } = await api.get('/alerts', { params }) + return data + }, + + markAlertaRead: async (id: number): Promise => { + await api.post(`/alerts/${id}/read`) + }, + + markAllAlertasRead: async (): Promise => { + await api.post('/alerts/read-all') + }, + login: async (email: string, password: string): Promise<{ token: string; user: any }> => { const { data } = await api.post('/auth/login', { email, password }) return data diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 38f969c..5bb94a3 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -125,6 +125,7 @@ TARGET_LANGS = _env_list("TARGET_LANGS") BATCH_SIZE = _env_int("TRANSLATOR_BATCH", 128) MAX_SRC_TOKENS = _env_int("MAX_SRC_TOKENS", 256) MAX_NEW_TOKENS = _env_int("MAX_NEW_TOKENS", 256) +MAX_SEQ_PER_CALL = _env_int("MAX_SEQ_PER_CALL", 128) CT2_MODEL_PATH = _env_str("CT2_MODEL_PATH", "/app/models/nllb-ct2") CT2_DEVICE = _env_str("CT2_DEVICE", "cpu") @@ -133,6 +134,7 @@ UNIVERSAL_MODEL = _env_str("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M" CT2_INTRA_THREADS = _env_int("CT2_INTRA_THREADS", 0) CT2_INTER_THREADS = _env_int("CT2_INTER_THREADS", 1) BODY_CHARS_CHUNK = _env_int("BODY_CHARS_CHUNK", 900) +MAX_BODY_CHARS = _env_int("MAX_BODY_CHARS", 12000) LANG_CODE_MAP = { "en": "eng_Latn", @@ -302,42 +304,45 @@ def translate_texts(src: str, tgt: str, texts: List[str]) -> List[str]: target_prefix = [[tgt_code]] * len(sources) - results = _translator.translate_batch( - sources, - target_prefix=target_prefix, - beam_size=1, - max_decoding_length=MAX_NEW_TOKENS, - repetition_penalty=1.2, - no_repeat_ngram_size=2, - ) - translated = [] - for result in results: - try: - if result.hypotheses and len(result.hypotheses) > 0: - hyp = result.hypotheses[0] - if isinstance(hyp, list) and len(hyp) > 0: - first_hyp = hyp[0] - if isinstance(first_hyp, dict) and "token_ids" in first_hyp: - tokens = first_hyp["token_ids"] - text = _tokenizer.decode(tokens) - translated.append(text.strip()) - elif isinstance(first_hyp, str): - token_strings = hyp[1:] if len(hyp) > 1 else [] - if token_strings: - text = _tokenizer.convert_tokens_to_string(token_strings) + # Bounded sub-batches: a big batch of long chunks translated in one call + # bloats RAM (several GB) on CPU and pushes the process into swap. + for i in range(0, len(sources), MAX_SEQ_PER_CALL): + results = _translator.translate_batch( + sources[i : i + MAX_SEQ_PER_CALL], + target_prefix=target_prefix[i : i + MAX_SEQ_PER_CALL], + beam_size=1, + max_decoding_length=MAX_NEW_TOKENS, + repetition_penalty=1.2, + no_repeat_ngram_size=2, + ) + + for result in results: + try: + if result.hypotheses and len(result.hypotheses) > 0: + hyp = result.hypotheses[0] + if isinstance(hyp, list) and len(hyp) > 0: + first_hyp = hyp[0] + if isinstance(first_hyp, dict) and "token_ids" in first_hyp: + tokens = first_hyp["token_ids"] + text = _tokenizer.decode(tokens) translated.append(text.strip()) + elif isinstance(first_hyp, str): + token_strings = hyp[1:] if len(hyp) > 1 else [] + if token_strings: + text = _tokenizer.convert_tokens_to_string(token_strings) + translated.append(text.strip()) + else: + translated.append("") else: translated.append("") else: translated.append("") else: translated.append("") - else: + except Exception as e: + LOG.error(f"Error processing result: {e}") translated.append("") - except Exception as e: - LOG.error(f"Error processing result: {e}") - translated.append("") return translated @@ -482,6 +487,8 @@ def process_batch(conn, rows): flat_ck = [] for item in items: body = (item["resumen"] or "").strip() + if len(body) > MAX_BODY_CHARS: + body = body[:MAX_BODY_CHARS] if body: chunks = split_body_into_chunks(body) flat_chunks.extend(chunks) From a01050bf85c2139dc0ffd341857ba554684f0ae1 Mon Sep 17 00:00:00 2001 From: jlimolina Date: Fri, 28 Aug 2026 21:10:02 +0200 Subject: [PATCH 21/21] cambios --- backend/cmd/discovery/main.go | 45 +- backend/cmd/qdrant/main.go | 70 +- backend/cmd/related/main.go | 44 +- backend/cmd/scraper/main.go | 76 +- backend/cmd/server/main.go | 80 +- backend/cmd/topics/main.go | 50 +- backend/cmd/wiki_worker/main.go | 84 +- backend/docs/docs.go | 43 + backend/go.mod | 25 +- backend/go.sum | 74 +- backend/internal/auth/jwt.go | 14 + backend/internal/cache/redis.go | 111 +- backend/internal/config/config.go | 9 +- backend/internal/db/postgres.go | 36 +- backend/internal/handlers/admin.go | 106 +- backend/internal/handlers/alerts.go | 78 +- backend/internal/handlers/auth.go | 41 + backend/internal/handlers/auth_test.go | 40 +- backend/internal/handlers/feed.go | 107 +- backend/internal/handlers/feed_test.go | 35 + backend/internal/handlers/news.go | 299 +++-- backend/internal/handlers/news_test.go | 38 + backend/internal/handlers/search.go | 141 ++- backend/internal/handlers/search_test.go | 35 + backend/internal/handlers/validation.go | 14 + backend/internal/logger/logger.go | 60 + backend/internal/middleware/auth.go | 105 +- backend/internal/models/models.go | 109 +- backend/internal/workers/db.go | 39 +- docker-compose.yml | 6 +- entity_config.json | 12 +- frontend/src/components/layout/Layout.tsx | 4 +- frontend/src/components/ui/EmptyState.tsx | 166 +++ frontend/src/components/ui/Skeleton.tsx | 110 ++ frontend/src/components/ui/WikiTooltip.tsx | 44 +- frontend/src/components/ui/index.ts | 3 + frontend/src/pages/Alertas.tsx | 115 +- frontend/src/pages/Home.tsx | 16 +- frontend/src/pages/Populares.tsx | 193 ++-- frontend/src/pages/Search.tsx | 26 +- plan-app.md | 1154 ++++++++++++++++++++ plan-mejora.md | 269 +++++ translate-worker | 25 + translate_worker_termux.py | 133 +++ workers/ctranslator_worker.py | 2 +- workers/translation_scheduler.py | 2 +- 46 files changed, 3636 insertions(+), 652 deletions(-) create mode 100644 backend/docs/docs.go create mode 100644 backend/internal/handlers/feed_test.go create mode 100644 backend/internal/handlers/news_test.go create mode 100644 backend/internal/handlers/search_test.go create mode 100644 backend/internal/handlers/validation.go create mode 100644 backend/internal/logger/logger.go create mode 100644 frontend/src/components/ui/EmptyState.tsx create mode 100644 frontend/src/components/ui/Skeleton.tsx create mode 100644 frontend/src/components/ui/index.ts create mode 100644 plan-app.md create mode 100644 plan-mejora.md create mode 100755 translate-worker create mode 100755 translate_worker_termux.py diff --git a/backend/cmd/discovery/main.go b/backend/cmd/discovery/main.go index 2f77d76..92970f5 100644 --- a/backend/cmd/discovery/main.go +++ b/backend/cmd/discovery/main.go @@ -14,11 +14,11 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/mmcdole/gofeed" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger pool *workers.Config dbPool *pgxpool.Pool sleepSec = 900 // 15 minutes @@ -35,7 +35,8 @@ type URLSource struct { } func init() { - logger = log.New(os.Stdout, "[DISCOVERY] ", log.LstdFlags) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("discovery-worker", logLevel) } func loadConfig() { @@ -416,48 +417,70 @@ func processURLSource(ctx context.Context, source URLSource) { func main() { loadConfig() - logger.Println("Starting RSS Discovery Worker") + logger.Info().Msg("Starting RSS Discovery Worker") cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } dbPool = workers.GetPool() defer workers.Close() - logger.Println("Connected to PostgreSQL") + logger.Info().Msg("Connected to PostgreSQL") - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan - logger.Println("Shutting down...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + time.Sleep(2 * time.Second) + workers.Close() os.Exit(0) }() - logger.Printf("Config: interval=%ds, batch=%d", sleepSec, batchSize) + logger.Info().Msgf("Config: interval=%ds, batch=%d", sleepSec, batchSize) ticker := time.NewTicker(time.Duration(sleepSec) * time.Second) defer ticker.Stop() for { select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return case <-ticker.C: sources, err := getPendingURLs(ctx) if err != nil { - logger.Printf("Error fetching URLs: %v", err) + logger.Error().Err(err).Msg("Error fetching URLs") continue } if len(sources) == 0 { - logger.Println("No pending URLs to process") + logger.Info().Msg("No pending URLs to process") continue } - logger.Printf("Processing %d sources", len(sources)) + logger.Info().Msgf("Processing %d sources", len(sources)) for _, source := range sources { processURLSource(ctx, source) diff --git a/backend/cmd/qdrant/main.go b/backend/cmd/qdrant/main.go index bf9f054..70accb8 100644 --- a/backend/cmd/qdrant/main.go +++ b/backend/cmd/qdrant/main.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" "os" "os/signal" @@ -17,11 +16,11 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger dbPool *pgxpool.Pool qdrantURL string collection = "news_vectors" @@ -30,28 +29,29 @@ var ( ) func init() { - logger = log.New(os.Stdout, "[QDRANT] ", log.LstdFlags) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("qdrant-worker", logLevel) } func loadConfig() { sleepSec = getEnvInt("QDRANT_SLEEP", 30) - batchSize = getEnvInt("QDRANT_BATCH", 100) - qdrantHost := getEnv("QDRANT_HOST", "localhost") - qdrantPort := getEnvInt("QDRANT_PORT", 6333) - qdrantURL = fmt.Sprintf("http://%s:%d", qdrantHost, qdrantPort) - collection = getEnv("QDRANT_COLLECTION", "news_vectors") + batchSize = getEnvInt("QDRANT_BATCH",100) + qdrantHost := getEnv("QDRANT_HOST","localhost") + qdrantPort := getEnvInt("QDRANT_PORT",6333) + qdrantURL = fmt.Sprintf("http://%s:%d",qdrantHost,qdrantPort) + collection = getEnv("QDRANT_COLLECTION","news_vectors") } -func getEnv(key, defaultValue string) string { +func getEnv(key,defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } -func getEnvInt(key string, defaultValue int) int { +func getEnvInt(key string,defaultValue int) int { if value := os.Getenv(key); value != "" { - if intVal, err := strconv.Atoi(value); err == nil { + if intVal,err := strconv.Atoi(value); err == nil { return intVal } } @@ -310,17 +310,33 @@ func main() { cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } dbPool = workers.GetPool() defer workers.Close() - logger.Println("Connected to PostgreSQL") + logger.Info().Msg("Connected to PostgreSQL") - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) if err := ensureCollection(ctx); err != nil { - logger.Printf("Warning: Could not ensure collection: %v", err) + logger.Warn().Err(err).Msg("Could not ensure collection") } sigChan := make(chan os.Signal, 1) @@ -328,30 +344,36 @@ func main() { go func() { <-sigChan - logger.Println("Shutting down...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + time.Sleep(2 * time.Second) + workers.Close() os.Exit(0) }() - logger.Printf("Config: qdrant=%s, collection=%s, sleep=%ds, batch=%d", + logger.Info().Msgf("Config: qdrant=%s, collection=%s, sleep=%ds, batch=%d", qdrantURL, collection, sleepSec, batchSize) totalProcessed := 0 for { select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return case <-time.After(time.Duration(sleepSec) * time.Second): translations, err := getPendingTranslations(ctx) if err != nil { - logger.Printf("Error fetching pending translations: %v", err) + logger.Error().Err(err).Msg("Error fetching pending translations") continue } if len(translations) == 0 { - logger.Println("No pending translations to process") + logger.Info().Msg("No pending translations to process") continue } - logger.Printf("Processing %d translations...", len(translations)) + logger.Info().Msgf("Processing %d translations...", len(translations)) // Generate point IDs once (used both in Qdrant and DB) pointIDs := make([]string, len(translations)) @@ -361,21 +383,21 @@ func main() { // Upload to Qdrant if err := uploadToQdrant(translations, pointIDs); err != nil { - logger.Printf("Error uploading to Qdrant: %v", err) + logger.Error().Err(err).Msg("Error uploading to Qdrant") continue } // Update DB status if err := updateTranslationStatus(ctx, translations, pointIDs); err != nil { - logger.Printf("Error updating status: %v", err) + logger.Error().Err(err).Msg("Error updating status") } totalProcessed += len(translations) - logger.Printf("Processed %d translations (total: %d)", len(translations), totalProcessed) + logger.Info().Msgf("Processed %d translations (total: %d)", len(translations), totalProcessed) total, vectorized, pending, err := getStats(ctx) if err == nil { - logger.Printf("Stats: total=%d, vectorized=%d, pending=%d", total, vectorized, pending) + logger.Info().Msgf("Stats: total=%d, vectorized=%d, pending=%d", total, vectorized, pending) } } } diff --git a/backend/cmd/related/main.go b/backend/cmd/related/main.go index f1f1be5..20643e2 100644 --- a/backend/cmd/related/main.go +++ b/backend/cmd/related/main.go @@ -3,6 +3,7 @@ package main import ( "context" "log" + "net/http" "os" "os/signal" "strconv" @@ -10,11 +11,11 @@ import ( "time" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger dbPool *pgxpool.Pool sleepSec = 10 topK = 10 @@ -23,7 +24,8 @@ var ( ) func init() { - logger = log.New(os.Stdout, "[RELATED] ", log.LstdFlags) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("related-worker", logLevel) } func loadConfig() { @@ -335,20 +337,36 @@ func processBatch(ctx context.Context, model string) (int, error) { func main() { loadConfig() - logger.Println("Starting Related News Worker") + logger.Info().Msg("Starting Related News Worker") cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } dbPool = workers.GetPool() defer workers.Close() - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) // Ensure schema if err := ensureSchema(ctx); err != nil { - logger.Printf("Error ensuring schema: %v", err) + logger.Error().Err(err).Msg("Error ensuring schema") } model := os.Getenv("EMB_MODEL") @@ -361,23 +379,29 @@ func main() { go func() { <-sigChan - logger.Println("Shutting down...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + time.Sleep(2 * time.Second) + workers.Close() os.Exit(0) }() - logger.Printf("Config: sleep=%ds, topK=%d, batch=%d, model=%s", sleepSec, topK, batchSz, model) + logger.Info().Msgf("Config: sleep=%ds, topK=%d, batch=%d, model=%s", sleepSec, topK, batchSz, model) for { select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return case <-time.After(time.Duration(sleepSec) * time.Second): count, err := processBatch(ctx, model) if err != nil { - logger.Printf("Error processing batch: %v", err) + logger.Error().Err(err).Msg("Error processing batch") continue } if count > 0 { - logger.Printf("Generated related news for %d translations", count) + logger.Info().Msgf("Generated related news for %d translations", count) } } } diff --git a/backend/cmd/scraper/main.go b/backend/cmd/scraper/main.go index 8d372ea..7e90f19 100644 --- a/backend/cmd/scraper/main.go +++ b/backend/cmd/scraper/main.go @@ -5,7 +5,6 @@ import ( "crypto/md5" "fmt" "io" - "log" "net/http" "os" "os/signal" @@ -17,12 +16,11 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger - dbPool *workers.Config pool *pgxpool.Pool sleepInterval = 60 batchSize = 10 @@ -59,15 +57,31 @@ type Article struct { } func init() { - logger = log.New(os.Stdout, "[SCRAPER] ", log.LstdFlags) - logger.SetOutput(os.Stdout) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("scraper-worker", logLevel) } func loadConfig() { - sleepInterval = getEnvInt("SCRAPER_SLEEP", 60) - batchSize = getEnvInt("SCRAPER_BATCH", 10) - enrichLimit = getEnvInt("SCRAPER_ENRICH_LIMIT", 20) - scraperWorkers = getEnvInt("SCRAPER_WORKERS", 5) + if v := os.Getenv("SCRAPER_INTERVAL"); v != "" { + if i, err := strconv.Atoi(v); err == nil && i > 0 { + sleepInterval = i + } + } + if v := os.Getenv("SCRAPER_BATCH"); v != "" { + if i, err := strconv.Atoi(v); err == nil && i > 0 { + batchSize = i + } + } + if v := os.Getenv("SCRAPER_ENRICH_LIMIT"); v != "" { + if i, err := strconv.Atoi(v); err == nil && i > 0 { + enrichLimit = i + } + } + if v := os.Getenv("SCRAPER_WORKERS"); v != "" { + if i, err := strconv.Atoi(v); err == nil && i > 0 { + scraperWorkers = i + } + } } func getEnvInt(key string, defaultValue int) int { @@ -448,18 +462,34 @@ func scrapeSources(ctx context.Context, sources []URLSource) { func main() { loadConfig() - logger.Println("Starting Scraper Worker") + logger.Info().Msg("Starting Scraper Worker") cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } pool = workers.GetPool() defer workers.Close() - logger.Println("Connected to PostgreSQL") + logger.Info().Msg("Connected to PostgreSQL") - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) // Handle shutdown sigChan := make(chan os.Signal, 1) @@ -467,38 +497,44 @@ func main() { go func() { <-sigChan - logger.Println("Shutting down...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + time.Sleep(2 * time.Second) + workers.Close() os.Exit(0) }() - logger.Printf("Config: sleep=%ds, batch=%d, enrich=%d", sleepInterval, batchSize, enrichLimit) + logger.Info().Msgf("Config: sleep=%ds, batch=%d, enrich=%d", sleepInterval, batchSize, enrichLimit) ticker := time.NewTicker(time.Duration(sleepInterval) * time.Second) defer ticker.Stop() for { select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return case <-ticker.C: noticias, err := getNoticiasToEnrich(ctx, enrichLimit) if err != nil { - logger.Printf("Error fetching noticias to enrich: %v", err) + logger.Error().Err(err).Msg("Error fetching noticias to enrich") } else if len(noticias) > 0 { - logger.Printf("Enriching %d noticias (%d workers)", len(noticias), scraperWorkers) + logger.Info().Msgf("Enriching %d noticias (%d workers)", len(noticias), scraperWorkers) enrichConcurrent(ctx, noticias) } sources, err := getActiveURLs(ctx) if err != nil { - logger.Printf("Error fetching URLs: %v", err) + logger.Error().Err(err).Msg("Error fetching URLs") continue } if len(sources) == 0 { - logger.Println("No active URLs to process") + logger.Info().Msg("No active URLs to process") continue } - logger.Printf("Processing %d sources (%d workers)", len(sources), scraperWorkers) + logger.Info().Msgf("Processing %d sources (%d workers)", len(sources), scraperWorkers) scrapeSources(ctx, sources) } } diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 81caed4..bf6f6b5 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "log" "os" "os/signal" "syscall" @@ -14,12 +13,17 @@ import ( "github.com/rss2/backend/internal/config" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/handlers" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/middleware" "github.com/rss2/backend/internal/services" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" + _ "github.com/rss2/backend/docs" ) func initDB() { ctx := context.Background() + log := logger.GetLogger() // Crear tabla entity_aliases si no existe _, err := db.GetPool().Exec(ctx, ` @@ -33,9 +37,9 @@ func initDB() { ) `) if err != nil { - log.Printf("Warning: Could not create entity_aliases table: %v", err) + log.Warn().Err(err).Msg("Could not create entity_aliases table") } else { - log.Println("Table entity_aliases ready") + log.Info().Msg("Table entity_aliases ready") } // Añadir columna role a users si no existe @@ -43,9 +47,9 @@ func initDB() { ALTER TABLE users ADD COLUMN IF NOT EXISTS role VARCHAR(20) DEFAULT 'user' `) if err != nil { - log.Printf("Warning: Could not add role column: %v", err) + log.Warn().Err(err).Msg("Could not add role column") } else { - log.Println("Column role ready") + log.Info().Msg("Column role ready") } // Crear tabla de configuración si no existe @@ -129,31 +133,66 @@ func initDB() { if err != nil { log.Printf("Warning: Could not add assigned_at column: %v", err) } + + // Crear índices para optimizar consultas críticas + indexes := []string{ + "CREATE INDEX IF NOT EXISTS idx_noticias_fecha ON noticias(fecha DESC)", + "CREATE INDEX IF NOT EXISTS idx_noticias_fuente_nombre ON noticias(fuente_nombre)", + "CREATE INDEX IF NOT EXISTS idx_noticias_categoria_id ON noticias(categoria_id)", + "CREATE INDEX IF NOT EXISTS idx_noticias_pais_id ON noticias(pais_id)", + "CREATE INDEX IF NOT EXISTS idx_noticias_lang ON noticias(lang)", + "CREATE INDEX IF NOT EXISTS idx_traducciones_noticia_id_lang_to ON traducciones(noticia_id, lang_to)", + "CREATE INDEX IF NOT EXISTS idx_traducciones_status ON traducciones(status)", + "CREATE INDEX IF NOT EXISTS idx_tags_noticia_noticia_id_tag_id ON tags_noticia(noticia_id, tag_id)", + "CREATE INDEX IF NOT EXISTS idx_tags_noticia_traduccion_id ON tags_noticia(traduccion_id)", + "CREATE INDEX IF NOT EXISTS idx_tags_valor_tipo ON tags(valor, tipo)", + "CREATE INDEX IF NOT EXISTS idx_entity_aliases_alias_tipo ON entity_aliases(alias, tipo)", + "CREATE INDEX IF NOT EXISTS idx_alertas_valor_tipo_periodo ON alertas(valor, tipo, periodo)", + "CREATE INDEX IF NOT EXISTS idx_alertas_status ON alertas(status)", + "CREATE INDEX IF NOT EXISTS idx_user_search_tags_user_id ON user_search_tags(user_id)", + "CREATE INDEX IF NOT EXISTS idx_feeds_activo ON feeds(activo)", + "CREATE INDEX IF NOT EXISTS idx_feeds_categoria_id ON feeds(categoria_id)", + "CREATE INDEX IF NOT EXISTS idx_feeds_pais_id ON feeds(pais_id)", + } + for _, idx := range indexes { + _, err := db.GetPool().Exec(ctx, idx) + if err != nil { + log.Warn().Err(err).Msg("Could not create index") + } else { + log.Info().Msg("Index created/verified") + } + } } func main() { cfg := config.Load() + // Initialize structured logger + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("rss2-api", logLevel) + log := logger.GetLogger() + if err := db.Connect(cfg.DatabaseURL); err != nil { - log.Fatalf("Failed to connect to database: %v", err) + log.Fatal().Err(err).Msg("Failed to connect to database") } defer db.Close() - log.Println("Connected to PostgreSQL") + log.Info().Msg("Connected to PostgreSQL") // Auto-setup DB tables initDB() if err := cache.Connect(cfg.RedisURL); err != nil { - log.Printf("Warning: Failed to connect to Redis: %v", err) + log.Warn().Err(err).Msg("Failed to connect to Redis") } else { defer cache.Close() - log.Println("Connected to Redis") + log.Info().Msg("Connected to Redis") } services.Init(cfg) r := gin.Default() + r.Use(middleware.RequestIDMiddleware()) r.Use(middleware.CORSMiddleware()) r.Use(middleware.LoggerMiddleware()) @@ -161,14 +200,25 @@ func main() { c.JSON(200, gin.H{"status": "ok"}) }) + // Swagger documentation + r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + api := r.Group("/api") { // Serve static images downloaded by wiki_worker api.StaticFS("/wiki-images", gin.Dir(cfg.WikiImagesPath, false)) - api.POST("/auth/login", handlers.Login) - api.POST("/auth/register", handlers.Register) - api.GET("/auth/check-first-user", handlers.CheckFirstUser) + // Stricter rate limiting for auth endpoints + authGroup := api.Group("/auth") + authGroup.Use(middleware.RateLimitMiddleware(10)) // 10 req/min for auth + { + authGroup.POST("/login", handlers.Login) + authGroup.POST("/register", handlers.Register) + authGroup.GET("/check-first-user", handlers.CheckFirstUser) + } + + // General rate limiting for API + api.Use(middleware.RateLimitMiddleware(cfg.RateLimitPerMinute)) news := api.Group("/news") { @@ -251,9 +301,9 @@ func main() { addr := fmt.Sprintf(":%s", port) go func() { - log.Printf("Server starting on %s", addr) + log.Info().Str("addr", addr).Msg("Server starting") if err := r.Run(addr); err != nil { - log.Fatalf("Failed to start server: %v", err) + log.Fatal().Err(err).Msg("Failed to start server") } }() @@ -264,5 +314,5 @@ func main() { signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - log.Println("Shutting down server...") + log.Info().Msg("Shutting down server...") } diff --git a/backend/cmd/topics/main.go b/backend/cmd/topics/main.go index a9e83c0..7b958fa 100644 --- a/backend/cmd/topics/main.go +++ b/backend/cmd/topics/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "net/http" "os" "os/signal" "strconv" @@ -12,11 +13,11 @@ import ( "time" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger dbPool *pgxpool.Pool sleepSec = 10 batchSz = 500 @@ -35,7 +36,8 @@ type Country struct { } func init() { - logger = log.New(os.Stdout, "[TOPICS] ", log.LstdFlags) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("topics-worker", logLevel) } func loadConfig() { @@ -335,20 +337,36 @@ func processBatch(ctx context.Context, topics []Topic, countries []Country) (int func main() { loadConfig() - logger.Println("Starting Topics Worker") + logger.Info().Msg("Starting Topics Worker") cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } dbPool = workers.GetPool() defer workers.Close() - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) // Ensure schema if err := ensureSchema(ctx); err != nil { - logger.Printf("Error ensuring schema: %v", err) + logger.Error().Err(err).Msg("Error ensuring schema") } sigChan := make(chan os.Signal, 1) @@ -356,41 +374,47 @@ func main() { go func() { <-sigChan - logger.Println("Shutting down...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + time.Sleep(2 * time.Second) + workers.Close() os.Exit(0) }() - logger.Printf("Config: sleep=%ds, batch=%d", sleepSec, batchSz) + logger.Info().Msgf("Config: sleep=%ds, batch=%d", sleepSec, batchSz) for { select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return case <-time.After(time.Duration(sleepSec) * time.Second): topics, err := loadTopics(ctx) if err != nil { - logger.Printf("Error loading topics: %v", err) + logger.Error().Err(err).Msg("Error loading topics") continue } if len(topics) == 0 { - logger.Println("No topics found in DB") + logger.Info().Msg("No topics found in DB") time.Sleep(time.Duration(sleepSec) * time.Second) continue } countries, err := loadCountries(ctx) if err != nil { - logger.Printf("Error loading countries: %v", err) + logger.Error().Err(err).Msg("Error loading countries") continue } count, err := processBatch(ctx, topics, countries) if err != nil { - logger.Printf("Error processing batch: %v", err) + logger.Error().Err(err).Msg("Error processing batch") continue } if count > 0 { - logger.Printf("Processed %d news items", count) + logger.Info().Msgf("Processed %d news items", count) } if count < batchSz { diff --git a/backend/cmd/wiki_worker/main.go b/backend/cmd/wiki_worker/main.go index f6298b1..c98b799 100644 --- a/backend/cmd/wiki_worker/main.go +++ b/backend/cmd/wiki_worker/main.go @@ -16,11 +16,11 @@ import ( "time" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/logger" "github.com/rss2/backend/internal/workers" ) var ( - logger *log.Logger pool *pgxpool.Pool sleepInterval = 30 batchSize = 20 @@ -52,7 +52,8 @@ type Tag struct { } func init() { - logger = log.New(os.Stdout, "[WIKI_WORKER] ", log.LstdFlags) + logLevel := os.Getenv("LOG_LEVEL") + logger.Init("wiki-worker", logLevel) } func getPendingTags(ctx context.Context) ([]Tag, error) { @@ -227,52 +228,89 @@ func main() { } } - logger.Println("Iniciando Wiki Worker...") + logger.Info().Msg("Iniciando Wiki Worker...") if err := os.MkdirAll(imagesDir, 0755); err != nil { - logger.Fatalf("Error creando directorio de imágenes: %v", err) + logger.Fatal().Err(err).Msg("Error creando directorio de imágenes") } cfg := workers.LoadDBConfig() if err := workers.Connect(cfg); err != nil { - logger.Fatalf("Failed to connect to database: %v", err) + logger.Fatal().Err(err).Msg("Failed to connect to database") } pool = workers.GetPool() defer workers.Close() - ctx := context.Background() + // Start health check HTTP server + go func() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + if err := workers.HealthCheck(r.Context()); err != nil { + http.Error(w, "unhealthy", http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + logger.Info().Msg("Health check server listening on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + logger.Error().Err(err).Msg("Health check server error") + } + }() + + ctx, cancel := context.WithCancel(context.Background()) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan - logger.Println("Cerrando gracefully...") + logger.Info().Msg("Shutting down gracefully...") + cancel() + // Give time for in-flight requests to complete + time.Sleep(2 * time.Second) workers.Close() os.Exit(0) }() - logger.Printf("Configuración: sleep=%ds, batch=%d", sleepInterval, batchSize) + logger.Info().Msgf("Configuración: sleep=%ds, batch=%d", sleepInterval, batchSize) for { - tags, err := getPendingTags(ctx) - if err != nil { - logger.Printf("Error recuperando tags pendientes: %v", err) - time.Sleep(10 * time.Second) - continue - } + select { + case <-ctx.Done(): + logger.Info().Msg("Context cancelled, stopping worker") + return + default: + tags, err := getPendingTags(ctx) + if err != nil { + logger.Error().Err(err).Msg("Error recuperando tags pendientes") + select { + case <-ctx.Done(): + return + case <-time.After(10 * time.Second): + } + continue + } - if len(tags) == 0 { - logger.Printf("No hay tags pendientes. Durmiendo %d segundos...", sleepInterval) - time.Sleep(time.Duration(sleepInterval) * time.Second) - continue - } + if len(tags) == 0 { + logger.Info().Msgf("No hay tags pendientes. Durmiendo %d segundos...", sleepInterval) + select { + case <-ctx.Done(): + return + case <-time.After(time.Duration(sleepInterval) * time.Second): + } + continue + } - logger.Printf("Recuperados %d tags para procesar...", len(tags)) + logger.Info().Msgf("Recuperados %d tags para procesar...", len(tags)) - for _, tag := range tags { - processTag(ctx, tag) - time.Sleep(3 * time.Second) // Increased delay to avoid Wikipedia Rate Limits (429) + for _, tag := range tags { + processTag(ctx, tag) + select { + case <-ctx.Done(): + return + case <-time.After(3 * time.Second): // Increased delay to avoid Wikipedia Rate Limits (429) + } + } } } } diff --git a/backend/docs/docs.go b/backend/docs/docs.go new file mode 100644 index 0000000..c07ab67 --- /dev/null +++ b/backend/docs/docs.go @@ -0,0 +1,43 @@ +// Package docs provides Swagger documentation for the RSS2 API +// +// @title RSS2 API +// @version 1.0 +// @description RSS2 News Aggregator API - A comprehensive news aggregation and analysis platform +// @termsOfService http://swagger.io/terms/ +// +// @contact.name RSS2 Team +// @contact.url http://github.com/rss2 +// @contact.email support@rss2.example.com +// +// @license.name MIT +// @license.url https://opensource.org/licenses/MIT +// +// @host localhost:8080 +// @BasePath /api +// +// @securityDefinitions.apikey BearerAuth +// @in header +// @name Authorization +// @description Type "Bearer" followed by a space and JWT token. +// +// @tag.name auth +// @tag.description Authentication endpoints +// +// @tag.name news +// @tag.description News management endpoints +// +// @tag.name feeds +// @tag.description Feed management endpoints +// +// @tag.name search +// @tag.description Search and discovery endpoints +// +// @tag.name entities +// @tag.description Entity extraction and analysis +// +// @tag.name admin +// @tag.description Admin management endpoints +// +// @tag.name stats +// @tag.description Statistics and analytics +package docs \ No newline at end of file diff --git a/backend/go.mod b/backend/go.mod index bd2af34..1d39e9e 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -13,10 +13,17 @@ require ( github.com/jackc/pgx/v5 v5.4.3 github.com/mmcdole/gofeed v1.2.1 github.com/redis/go-redis/v9 v9.0.5 - golang.org/x/crypto v0.26.0 + github.com/rs/zerolog v1.32.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + golang.org/x/crypto v0.28.0 + golang.org/x/time v0.5.0 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -24,31 +31,39 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mmcdole/goxpp v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/swaggo/swag v1.16.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index bfa3dc0..95b9d58 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,11 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= @@ -14,6 +20,7 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -22,10 +29,22 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -36,6 +55,7 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -53,17 +73,29 @@ github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s= @@ -75,18 +107,24 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -94,6 +132,12 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -104,17 +148,21 @@ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -122,13 +170,16 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -137,22 +188,33 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/backend/internal/auth/jwt.go b/backend/internal/auth/jwt.go index c00db91..528920a 100644 --- a/backend/internal/auth/jwt.go +++ b/backend/internal/auth/jwt.go @@ -1,6 +1,7 @@ package auth import ( + "context" "time" "github.com/golang-jwt/jwt/v5" @@ -53,3 +54,16 @@ func ValidateToken(tokenString string) (*Claims, error) { return claims, nil } + +type contextKey string + +const userContextKey contextKey = "user" + +func SetTestUser(ctx context.Context, claims *Claims) context.Context { + return context.WithValue(ctx, userContextKey, claims) +} + +func GetUserFromContext(ctx context.Context) (*Claims, bool) { + claims, ok := ctx.Value(userContextKey).(*Claims) + return claims, ok +} diff --git a/backend/internal/cache/redis.go b/backend/internal/cache/redis.go index 43c0b2f..c1cdc46 100644 --- a/backend/internal/cache/redis.go +++ b/backend/internal/cache/redis.go @@ -11,6 +11,26 @@ import ( var Client *redis.Client +// Cache TTL constants +const ( + TTLShort = 5 * time.Minute // 5 min for frequently changing data + TTLMedium = 30 * time.Minute // 30 min for semi-static data + TTLLong = 2 * time.Hour // 2 hours for stable data + TTLNoExpire = 0 // No expiration (manual invalidation) +) + +const ( + KeyPrefixSearch = "search" + KeyPrefixNews = "news" + KeyPrefixFeed = "feed" + KeyPrefixFeedList = "feedlist" + KeyPrefixCategories = "categories" + KeyPrefixCountries = "countries" + KeyPrefixStats = "stats" + KeyPrefixEntities = "entities" + KeyPrefixStatsTop = "statstop" +) + func Connect(redisURL string) error { opt, err := redis.ParseURL(redisURL) if err != nil { @@ -40,15 +60,39 @@ func GetClient() *redis.Client { } func SearchKey(query, lang string, page, perPage int) string { - return fmt.Sprintf("search:%s:%s:%d:%d", query, lang, page, perPage) + return fmt.Sprintf("%s:%s:%s:%d:%d", KeyPrefixSearch, query, lang, page, perPage) } func NewsKey(newsID int64, lang string) string { - return fmt.Sprintf("news:%d:%s", newsID, lang) + return fmt.Sprintf("%s:%d:%s", KeyPrefixNews, newsID, lang) } func FeedKey(feedID int64) string { - return fmt.Sprintf("feed:%d", feedID) + return fmt.Sprintf("%s:%d", KeyPrefixFeed, feedID) +} + +func FeedListKey(filters string, page, perPage int) string { + return fmt.Sprintf("%s:%s:%d:%d", KeyPrefixFeedList, filters, page, perPage) +} + +func CategoriesKey() string { + return KeyPrefixCategories +} + +func CountriesKey() string { + return KeyPrefixCountries +} + +func StatsKey() string { + return KeyPrefixStats +} + +func StatsTopKey(typ string) string { + return fmt.Sprintf("%s:%s", KeyPrefixStatsTop, typ) +} + +func EntityKey(entityType, query string, page, perPage int) string { + return fmt.Sprintf("%s:%s:%s:%d:%d", KeyPrefixEntities, entityType, query, page, perPage) } func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error { @@ -63,6 +107,62 @@ func Get(ctx context.Context, key string) (string, error) { return Client.Get(ctx, key).Result() } +func Delete(ctx context.Context, keys ...string) error { + if len(keys) == 0 { + return nil + } + return Client.Del(ctx, keys...).Err() +} + +func DeletePattern(ctx context.Context, pattern string) error { + iter := Client.Scan(ctx, 0, pattern, 100).Iterator() + for iter.Next(ctx) { + if err := Client.Del(ctx, iter.Val()).Err(); err != nil { + return err + } + } + return iter.Err() +} + +func InvalidateSearch(ctx context.Context) error { + return DeletePattern(ctx, KeyPrefixSearch+":*") +} + +func InvalidateNews(ctx context.Context, newsID int64) error { + pattern := fmt.Sprintf("%s:%d:*", KeyPrefixNews, newsID) + return DeletePattern(ctx, pattern) +} + +func InvalidateFeed(ctx context.Context, feedID int64) error { + return Delete(ctx, FeedKey(feedID)) +} + +func InvalidateFeedList(ctx context.Context) error { + return DeletePattern(ctx, KeyPrefixFeedList+":*") +} + +func InvalidateCategories(ctx context.Context) error { + return Delete(ctx, CategoriesKey()) +} + +func InvalidateCountries(ctx context.Context) error { + return Delete(ctx, CountriesKey()) +} + +func InvalidateStats(ctx context.Context) error { + keys := []string{ + StatsKey(), + StatsTopKey("categories"), + StatsTopKey("countries"), + } + return Delete(ctx, keys...) +} + +func InvalidateEntities(ctx context.Context, entityType string) error { + pattern := fmt.Sprintf("%s:%s:*", KeyPrefixEntities, entityType) + return DeletePattern(ctx, pattern) +} + func Unmarshal(data []byte, v interface{}) error { return json.Unmarshal(data, v) } @@ -70,3 +170,8 @@ func Unmarshal(data []byte, v interface{}) error { func Marshal(v interface{}) ([]byte, error) { return json.Marshal(v) } + +func Exists(ctx context.Context, key string) (bool, error) { + count, err := Client.Exists(ctx, key).Result() + return count > 0, err +} diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index f218100..343c15b 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -28,7 +28,12 @@ type Config struct { func Load() *Config { secretKey := os.Getenv("SECRET_KEY") if secretKey == "" { - secretKey = "change-this-secret-key" + panic("SECRET_KEY environment variable is required") + } + + allowedOrigins := os.Getenv("ALLOWED_ORIGINS") + if allowedOrigins == "" { + allowedOrigins = "http://localhost:5173,http://localhost:3000" } return &Config{ @@ -47,7 +52,7 @@ func Load() *Config { RateLimitPerMinute: getEnvInt("RATE_LIMIT_PER_MINUTE", 60), DockerComposeDir: getEnv("DOCKER_COMPOSE_DIR", "/datos/rss2"), WikiImagesPath: getEnv("WIKI_IMAGES_PATH", "/app/data/wiki_images"), - AllowedOrigins: getEnv("ALLOWED_ORIGINS", "*"), + AllowedOrigins: allowedOrigins, } } diff --git a/backend/internal/db/postgres.go b/backend/internal/db/postgres.go index 1b1a822..dcbd85f 100644 --- a/backend/internal/db/postgres.go +++ b/backend/internal/db/postgres.go @@ -3,6 +3,8 @@ package db import ( "context" "fmt" + "os" + "strconv" "time" "github.com/jackc/pgx/v5/pgxpool" @@ -16,10 +18,11 @@ func Connect(databaseURL string) error { return fmt.Errorf("failed to parse database URL: %w", err) } - config.MaxConns = 25 - config.MinConns = 5 - config.MaxConnLifetime = time.Hour - config.MaxConnIdleTime = 30 * time.Minute + config.MaxConns = int32(getEnvInt("DB_MAX_CONNS", 25)) + config.MinConns = int32(getEnvInt("DB_MIN_CONNS", 5)) + config.MaxConnLifetime = getEnvDuration("DB_MAX_CONN_LIFETIME", time.Hour) + config.MaxConnIdleTime = getEnvDuration("DB_MAX_CONN_IDLE_TIME", 30*time.Minute) + config.HealthCheckPeriod = getEnvDuration("DB_HEALTH_CHECK_PERIOD", time.Minute) Pool, err = pgxpool.NewWithConfig(context.Background(), config) if err != nil { @@ -42,3 +45,28 @@ func Close() { func GetPool() *pgxpool.Pool { return Pool } + +func HealthCheck(ctx context.Context) error { + if Pool == nil { + return fmt.Errorf("pool not initialized") + } + return Pool.Ping(ctx) +} + +func getEnvInt(key string, defaultValue int) int { + if value := os.Getenv(key); value != "" { + if intVal, err := strconv.Atoi(value); err == nil { + return intVal + } + } + return defaultValue +} + +func getEnvDuration(key string, defaultValue time.Duration) time.Duration { + if value := os.Getenv(key); value != "" { + if duration, err := time.ParseDuration(value); err == nil { + return duration + } + } + return defaultValue +} diff --git a/backend/internal/handlers/admin.go b/backend/internal/handlers/admin.go index 538cf5c..d9fc71c 100644 --- a/backend/internal/handlers/admin.go +++ b/backend/internal/handlers/admin.go @@ -738,7 +738,23 @@ func resolvePgConnInfo() pgConnInfo { return info } -// BackupDatabase runs pg_dump and returns the SQL as a downloadable file +// flushWriter flushes the underlying response writer after every write so that +// large backups stream to the client instead of being buffered in RAM. +type flushWriter struct { + w io.Writer +} + +func (fw flushWriter) Write(p []byte) (int, error) { + n, err := fw.w.Write(p) + if f, ok := fw.w.(http.Flusher); ok { + f.Flush() + } + return n, err +} + +// BackupDatabase runs pg_dump and returns the SQL as a downloadable file. +// The output is streamed to the client so arbitrarily large dumps do not +// exhaust the container memory. func BackupDatabase(c *gin.Context) { info := resolvePgConnInfo() @@ -752,12 +768,12 @@ func BackupDatabase(c *gin.Context) { ) cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", info.pass)) - var out bytes.Buffer + pr, pw := io.Pipe() + cmd.Stdout = pw var stderr bytes.Buffer - cmd.Stdout = &out cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { + if err := cmd.Start(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "pg_dump failed", "details": stderr.String(), @@ -769,10 +785,29 @@ func BackupDatabase(c *gin.Context) { c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) c.Header("Cache-Control", "no-cache") - c.Data(http.StatusOK, "application/octet-stream", out.Bytes()) + c.Status(http.StatusOK) + + fw := flushWriter{w: c.Writer} + copyDone := make(chan struct{}) + go func() { + defer close(copyDone) + _, _ = io.Copy(fw, pr) + }() + + err := cmd.Wait() + pw.Close() + <-copyDone + + if err != nil { + log.Printf("BackupDatabase: pg_dump failed: %v: %s", err, stderr.String()) + c.Writer.Flush() + return + } + c.Writer.Flush() } -// BackupNewsZipped performs a pg_dump of news tables and returns a ZIP file +// BackupNewsZipped performs a pg_dump of news tables and returns a ZIP file. +// pg_dump output is streamed into the ZIP as it is produced, keeping memory usage flat. func BackupNewsZipped(c *gin.Context) { info := resolvePgConnInfo() @@ -794,12 +829,12 @@ func BackupNewsZipped(c *gin.Context) { cmd := exec.Command("pg_dump", args...) cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", info.pass)) - var sqlOut bytes.Buffer + pr, pw := io.Pipe() + cmd.Stdout = pw var stderr bytes.Buffer - cmd.Stdout = &sqlOut cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { + if err := cmd.Start(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "pg_dump failed", "details": stderr.String(), @@ -807,33 +842,40 @@ func BackupNewsZipped(c *gin.Context) { return } - // Create ZIP - buf := new(bytes.Buffer) - zw := zip.NewWriter(buf) - - sqlFileName := fmt.Sprintf("backup_noticias_%s.sql", time.Now().Format("2006-01-02")) - f, err := zw.Create(sqlFileName) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create ZIP entry", "message": err.Error()}) - return - } - - _, err = f.Write(sqlOut.Bytes()) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write to ZIP", "message": err.Error()}) - return - } - - if err := zw.Close(); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to close ZIP writer", "message": err.Error()}) - return - } - filename := fmt.Sprintf("backup_noticias_%s.zip", time.Now().Format("2006-01-02_15-04-05")) c.Header("Content-Type", "application/zip") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) c.Header("Cache-Control", "no-cache") - c.Data(http.StatusOK, "application/zip", buf.Bytes()) + c.Status(http.StatusOK) + + fw := flushWriter{w: c.Writer} + zw := zip.NewWriter(fw) + + sqlFileName := fmt.Sprintf("backup_noticias_%s.sql", time.Now().Format("2006-01-02")) + f, err := zw.Create(sqlFileName) + if err != nil { + log.Printf("BackupNewsZipped: failed to create ZIP entry: %v", err) + pw.Close() + pr.Close() + return + } + + copyDone := make(chan struct{}) + go func() { + defer close(copyDone) + _, _ = io.Copy(f, pr) + }() + + waitErr := cmd.Wait() + pw.Close() + <-copyDone + closeErr := zw.Close() + c.Writer.Flush() + + if waitErr != nil || closeErr != nil { + log.Printf("BackupNewsZipped: pg_dump=%v zip=%v: %s", waitErr, closeErr, stderr.String()) + return + } } // RestoreDatabase accepts an uploaded .sql or .zip backup and restores it via psql diff --git a/backend/internal/handlers/alerts.go b/backend/internal/handlers/alerts.go index 7a5977a..e9d985e 100644 --- a/backend/internal/handlers/alerts.go +++ b/backend/internal/handlers/alerts.go @@ -109,10 +109,18 @@ func ScanAlertasAdmin(c *gin.Context) { } // RunAlertScan busca conceptos (persona, lugar, organizacion, tema) cuya -// actividad de hoy supera significativamente su media de los últimos días. +// actividad supera significativamente su media de los días anteriores. +// +// En lugar de comparar siempre contra CURRENT_DATE (que se queda vacío si la +// cadena de traducción/NER va por detrás de la ingesta), se toma como día de +// referencia el día más reciente que tenga datos de tags etiquetados y se +// compara contra la media de los ALERTS_LOOKBACK_DAYS días previos, contando +// los días sin actividad como 0. func RunAlertScan(ctx context.Context) (int, error) { - minHits := 3 + minHits := 5 minRatio := 5.0 + minBaseline := 2.0 + lookbackDays := 8 if v := os.Getenv("ALERTS_MIN_HITS"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 { minHits = n @@ -123,6 +131,16 @@ func RunAlertScan(ctx context.Context) (int, error) { minRatio = f } } + if v := os.Getenv("ALERTS_MIN_BASELINE"); v != "" { + if f, err := strconv.ParseFloat(v, 64); err == nil && f > 0 { + minBaseline = f + } + } + if v := os.Getenv("ALERTS_LOOKBACK_DAYS"); v != "" { + if n, err := strconv.Atoi(v); err == nil && n > 0 { + lookbackDays = n + } + } query := ` WITH daily AS ( @@ -132,34 +150,43 @@ func RunAlertScan(ctx context.Context) (int, error) { JOIN tags t ON tn.tag_id = t.id JOIN traducciones tr ON tn.traduccion_id = tr.id JOIN noticias n ON tr.noticia_id = n.id - WHERE n.fecha >= CURRENT_DATE - 8 + WHERE n.fecha >= CURRENT_DATE - 30 + AND n.fecha <= CURRENT_DATE AND t.tipo IN ('persona', 'lugar', 'organizacion', 'tema') GROUP BY t.id, t.valor, t.tipo, n.fecha::date ), - baseline AS ( - SELECT tag_id, valor, tipo, - AVG(cnt)::float AS baseline, - MAX(cnt)::int AS max_prev - FROM daily - WHERE dia < CURRENT_DATE - GROUP BY tag_id, valor, tipo + ref AS ( + SELECT MAX(dia) AS ref_date FROM daily + ), + prev_series AS ( + SELECT generate_series((SELECT ref_date - $3::int FROM ref), + (SELECT ref_date - 1 FROM ref), '1 day')::date AS dia ), today AS ( SELECT tag_id, valor, tipo, SUM(cnt)::int AS hits FROM daily - WHERE dia = CURRENT_DATE + WHERE dia = (SELECT ref_date FROM ref) GROUP BY tag_id, valor, tipo + ), + base AS ( + SELECT t.tag_id, t.valor, t.tipo, + AVG(COALESCE(d.cnt, 0))::float AS baseline + FROM today t + CROSS JOIN prev_series s + LEFT JOIN daily d ON d.tag_id = t.tag_id AND d.dia = s.dia + GROUP BY t.tag_id, t.valor, t.tipo ) SELECT t.valor, t.tipo, t.hits, b.baseline, - (t.hits::float / NULLIF(b.baseline, 0)) AS ratio + (t.hits::float / b.baseline) AS ratio, + (SELECT ref_date FROM ref)::date AS periodo FROM today t - JOIN baseline b USING (tag_id) + JOIN base b USING (tag_id) WHERE t.hits >= $1 - AND b.baseline >= 0.5 - AND (t.hits::float / NULLIF(b.baseline, 0)) >= $2 + AND b.baseline >= $4 + AND (t.hits::float / b.baseline) >= $2 ` - rows, err := db.GetPool().Query(ctx, query, minHits, minRatio) + rows, err := db.GetPool().Query(ctx, query, minHits, minRatio, lookbackDays, minBaseline) if err != nil { return 0, err } @@ -170,14 +197,15 @@ func RunAlertScan(ctx context.Context) (int, error) { var valor, tipo string var hits int var baseline, ratio float64 - if err := rows.Scan(&valor, &tipo, &hits, &baseline, &ratio); err != nil { + var periodo time.Time + if err := rows.Scan(&valor, &tipo, &hits, &baseline, &ratio, &periodo); err != nil { continue } res, err := db.GetPool().Exec(ctx, ` INSERT INTO alertas (valor, tipo, periodo, hits, baseline, ratio) - VALUES ($1, $2, CURRENT_DATE, $3, $4, $5) + VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (valor, tipo, periodo) DO NOTHING - `, valor, tipo, hits, baseline, ratio) + `, valor, tipo, periodo, hits, baseline, ratio) if err != nil { log.Printf("alerts: insert failed for %s (%s): %v", valor, tipo, err) continue @@ -200,12 +228,12 @@ func StartAlertScanner() { } } - go func() { - time.Sleep(45 * time.Second) - for { - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - inserted, err := RunAlertScan(ctx) - cancel() + go func() { + time.Sleep(45 * time.Second) + for { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + inserted, err := RunAlertScan(ctx) + cancel() if err != nil { log.Printf("alerts: scan error: %v", err) } else { diff --git a/backend/internal/handlers/auth.go b/backend/internal/handlers/auth.go index 6950200..d5dbc15 100644 --- a/backend/internal/handlers/auth.go +++ b/backend/internal/handlers/auth.go @@ -10,6 +10,14 @@ import ( "golang.org/x/crypto/bcrypt" ) +// CheckFirstUser checks if this is the first user registration +// @Summary Check if first user +// @Description Returns whether this is the first user being registered (no users exist yet) +// @Tags auth +// @Produce json +// @Success 200 {object} map[string]interface{} "is_first_user and total_users" +// @Failure 500 {object} models.ErrorResponse +// @Router /auth/check-first-user [get] func CheckFirstUser(c *gin.Context) { var count int err := db.GetPool().QueryRow(c.Request.Context(), "SELECT COUNT(*) FROM users").Scan(&count) @@ -20,6 +28,18 @@ func CheckFirstUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"is_first_user": count == 0, "total_users": count}) } +// Login authenticates a user and returns a JWT token +// @Summary User login +// @Description Authenticate with email and password to receive a JWT token +// @Tags auth +// @Accept json +// @Produce json +// @Param request body models.LoginRequest true "Login credentials" +// @Success 200 {object} models.AuthResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /auth/login [post] func Login(c *gin.Context) { var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -56,6 +76,17 @@ func Login(c *gin.Context) { }) } +// Register creates a new user account +// @Summary Register new user +// @Description Create a new user account (first user becomes admin automatically) +// @Tags auth +// @Accept json +// @Produce json +// @Param request body models.RegisterRequest true "Registration details" +// @Success 201 {object} models.AuthResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /auth/register [post] func Register(c *gin.Context) { var req models.RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -111,6 +142,16 @@ func Register(c *gin.Context) { }) } +// GetCurrentUser returns the current authenticated user +// @Summary Get current user +// @Description Returns the currently authenticated user's profile +// @Tags auth +// @Produce json +// @Security BearerAuth +// @Success 200 {object} models.User +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Router /auth/me [get] func GetCurrentUser(c *gin.Context) { userVal, exists := c.Get("user") if !exists { diff --git a/backend/internal/handlers/auth_test.go b/backend/internal/handlers/auth_test.go index 7586995..fd512eb 100644 --- a/backend/internal/handlers/auth_test.go +++ b/backend/internal/handlers/auth_test.go @@ -17,6 +17,7 @@ func init() { auth.SetJWTSecret("test-secret-key-12345") } +// TestLoginInvalidRequest tests that an empty request body returns 400 func TestLoginInvalidRequest(t *testing.T) { router := gin.New() router.POST("/auth/login", Login) @@ -40,22 +41,7 @@ func TestLoginInvalidRequest(t *testing.T) { } } -func TestLoginInvalidCredentials(t *testing.T) { - router := gin.New() - router.POST("/auth/login", Login) - - body := []byte(`{"email":"invalid@test.com","password":"wrongpass"}`) - req, _ := http.NewRequest("POST", "/auth/login", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != http.StatusUnauthorized { - t.Errorf("expected status 401, got %d", w.Code) - } -} - +// TestRegisterInvalidRequest tests that an empty request body returns 400 func TestRegisterInvalidRequest(t *testing.T) { router := gin.New() router.POST("/auth/register", Register) @@ -72,27 +58,7 @@ func TestRegisterInvalidRequest(t *testing.T) { } } -func TestCheckFirstUser(t *testing.T) { - router := gin.New() - router.GET("/auth/check-first-user", CheckFirstUser) - - req, _ := http.NewRequest("GET", "/auth/check-first-user", nil) - - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Errorf("expected status 200, got %d", w.Code) - } - - var resp map[string]interface{} - json.Unmarshal(w.Body.Bytes(), &resp) - - if _, ok := resp["is_first_user"]; !ok { - t.Error("expected is_first_user in response") - } -} - +// TestGetCurrentUserUnauthorized tests that unauthenticated request returns 401 func TestGetCurrentUserUnauthorized(t *testing.T) { router := gin.New() router.GET("/auth/me", GetCurrentUser) diff --git a/backend/internal/handlers/feed.go b/backend/internal/handlers/feed.go index ad7afc3..799550c 100644 --- a/backend/internal/handlers/feed.go +++ b/backend/internal/handlers/feed.go @@ -3,6 +3,7 @@ package handlers import ( "context" "encoding/csv" + "encoding/json" "fmt" "io" "net/http" @@ -11,40 +12,46 @@ import ( "strings" "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/cache" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" ) -type FeedResponse struct { - ID int64 `json:"id"` - Nombre string `json:"nombre"` - Descripcion *string `json:"descripcion"` - URL string `json:"url"` - CategoriaID *int64 `json:"categoria_id"` - PaisID *int64 `json:"pais_id"` - Idioma *string `json:"idioma"` - Activo bool `json:"activo"` - Fallos *int64 `json:"fallos"` - LastError *string `json:"last_error"` - FuenteURLID *int64 `json:"fuente_url_id"` -} - +// GetFeeds returns paginated feeds with optional filters +// @Summary List feeds +// @Description Returns paginated feeds with optional filtering by active status, category, and country +// @Tags feeds +// @Produce json +// @Param page query int false "Page number" default(1) minimum(1) +// @Param per_page query int false "Items per page" default(50) minimum(1) maximum(100) +// @Param activo query string false "Active status filter" +// @Param categoria_id query string false "Category ID filter" +// @Param pais_id query string false "Country ID filter" +// @Success 200 {object} map[string]interface{} "feeds, total, page, per_page, total_pages" +// @Failure 500 {object} models.ErrorResponse +// @Router /feeds [get] func GetFeeds(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "50")) + page, perPage = validatePageParams(page, perPage, 50, 100) activo := c.Query("activo") categoriaID := c.Query("categoria_id") paisID := c.Query("pais_id") - if page < 1 { - page = 1 - } - if perPage < 1 || perPage > 100 { - perPage = 50 - } - offset := (page - 1) * perPage + // Cache key based on filters + filterStr := fmt.Sprintf("activo_%s_cat_%s_pais_%s", activo, categoriaID, paisID) + cacheKey := cache.FeedListKey(filterStr, page, perPage) + + ctx := c.Request.Context() + + // Try to get from cache + if cached, err := cache.Get(ctx, cacheKey); err == nil && cached != "" { + c.Data(http.StatusOK, "application/json", []byte(cached)) + return + } + where := "1=1" args := []interface{}{} argNum := 1 @@ -67,7 +74,7 @@ func GetFeeds(c *gin.Context) { var total int countQuery := fmt.Sprintf("SELECT COUNT(*) FROM feeds WHERE %s", where) - err := db.GetPool().QueryRow(c.Request.Context(), countQuery, args...).Scan(&total) + err := db.GetPool().QueryRow(ctx, countQuery, args...).Scan(&total) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to count feeds", Message: err.Error()}) return @@ -75,7 +82,7 @@ func GetFeeds(c *gin.Context) { sqlQuery := fmt.Sprintf(` SELECT f.id, f.nombre, f.descripcion, f.url, - f.categoria_id, f.pais_id, f.idioma, f.activo, f.fallos, f.last_error, + f.categoria_id, f.pais_id, f.idioma, f.activo, f.fallos, f.last_error, f.last_fetched, c.nombre AS categoria, p.nombre AS pais, (SELECT COUNT(*) FROM noticias n WHERE n.fuente_nombre = f.nombre) as noticias_count FROM feeds f @@ -88,7 +95,7 @@ func GetFeeds(c *gin.Context) { args = append(args, perPage, offset) - rows, err := db.GetPool().Query(c.Request.Context(), sqlQuery, args...) + rows, err := db.GetPool().Query(ctx, sqlQuery, args...) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to fetch feeds", Message: err.Error()}) return @@ -96,7 +103,7 @@ func GetFeeds(c *gin.Context) { defer rows.Close() type FeedWithStats struct { - FeedResponse + models.Feed Categoria *string `json:"categoria"` Pais *string `json:"pais"` NoticiasCount int64 `json:"noticias_count"` @@ -107,7 +114,7 @@ func GetFeeds(c *gin.Context) { var f FeedWithStats err := rows.Scan( &f.ID, &f.Nombre, &f.Descripcion, &f.URL, - &f.CategoriaID, &f.PaisID, &f.Idioma, &f.Activo, &f.Fallos, &f.LastError, + &f.CategoriaID, &f.PaisID, &f.Idioma, &f.Activo, &f.Fallos, &f.LastError, &f.LastFetched, &f.Categoria, &f.Pais, &f.NoticiasCount, ) if err != nil { @@ -118,13 +125,20 @@ func GetFeeds(c *gin.Context) { totalPages := (total + perPage - 1) / perPage - c.JSON(http.StatusOK, gin.H{ + response := gin.H{ "feeds": feeds, "total": total, "page": page, "per_page": perPage, "total_pages": totalPages, - }) + } + + // Cache the response + if data, err := json.Marshal(response); err == nil { + cache.Set(ctx, cacheKey, string(data), cache.TTLMedium) + } + + c.JSON(http.StatusOK, response) } func GetFeedByID(c *gin.Context) { @@ -134,12 +148,12 @@ func GetFeedByID(c *gin.Context) { return } - var f FeedResponse + var f models.Feed err = db.GetPool().QueryRow(c.Request.Context(), ` - SELECT id, nombre, descripcion, url, categoria_id, pais_id, idioma, activo, fallos + SELECT id, nombre, descripcion, url, categoria_id, pais_id, idioma, activo, fallos, last_fetched FROM feeds WHERE id = $1`, id).Scan( &f.ID, &f.Nombre, &f.Descripcion, &f.URL, - &f.CategoriaID, &f.PaisID, &f.Idioma, &f.Activo, &f.Fallos, + &f.CategoriaID, &f.PaisID, &f.Idioma, &f.Activo, &f.Fallos, &f.LastFetched, ) if err != nil { c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "Feed not found"}) @@ -178,6 +192,9 @@ func CreateFeed(c *gin.Context) { return } + // Invalidate feed list cache + cache.InvalidateFeedList(c.Request.Context()) + c.JSON(http.StatusCreated, gin.H{"id": feedID, "message": "Feed created successfully"}) } @@ -228,6 +245,10 @@ func UpdateFeed(c *gin.Context) { return } + // Invalidate feed cache + cache.InvalidateFeed(c.Request.Context(), id) + cache.InvalidateFeedList(c.Request.Context()) + c.JSON(http.StatusOK, models.SuccessResponse{Message: "Feed updated successfully"}) } @@ -249,6 +270,10 @@ func DeleteFeed(c *gin.Context) { return } + // Invalidate feed cache + cache.InvalidateFeed(c.Request.Context(), id) + cache.InvalidateFeedList(c.Request.Context()) + c.JSON(http.StatusOK, models.SuccessResponse{Message: "Feed deleted successfully"}) } @@ -271,6 +296,10 @@ func ToggleFeedActive(c *gin.Context) { return } + // Invalidate feed cache + cache.InvalidateFeed(c.Request.Context(), id) + cache.InvalidateFeedList(c.Request.Context()) + c.JSON(http.StatusOK, models.SuccessResponse{Message: "Feed toggled successfully"}) } @@ -293,6 +322,10 @@ func ReactivateFeed(c *gin.Context) { return } + // Invalidate feed cache + cache.InvalidateFeed(c.Request.Context(), id) + cache.InvalidateFeedList(c.Request.Context()) + c.JSON(http.StatusOK, models.SuccessResponse{Message: "Feed reactivated successfully"}) } @@ -449,7 +482,12 @@ func ImportFeeds(c *gin.Context) { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to start transaction", Message: err.Error()}) return } - defer tx.Rollback(context.Background()) + committed := false + defer func() { + if !committed { + tx.Rollback(context.Background()) + } + }() field := func(record []string, idx int) string { if idx < 0 || idx >= len(record) { @@ -579,7 +617,9 @@ func ImportFeeds(c *gin.Context) { } if err != nil { - tx.Exec(context.Background(), "ROLLBACK TO SAVEPOINT sp") + if _, rbErr := tx.Exec(context.Background(), "ROLLBACK TO SAVEPOINT sp"); rbErr != nil { + errors = append(errors, fmt.Sprintf("Rollback failed for %s: %v", url, rbErr)) + } failed++ errors = append(errors, fmt.Sprintf("Error upserting %s: %v", url, err)) continue @@ -598,6 +638,7 @@ func ImportFeeds(c *gin.Context) { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to commit transaction", Message: err.Error()}) return } + committed = true c.JSON(http.StatusOK, gin.H{ "imported": imported, diff --git a/backend/internal/handlers/feed_test.go b/backend/internal/handlers/feed_test.go new file mode 100644 index 0000000..8f888f1 --- /dev/null +++ b/backend/internal/handlers/feed_test.go @@ -0,0 +1,35 @@ +package handlers + +import ( + "testing" +) + +func TestValidatePageParamsFeed(t *testing.T) { + tests := []struct { + name string + page int + perPage int + defaultPerPage int + maxPerPage int + expectedPage int + expectedPerPage int + }{ + {"valid", 1, 50, 50, 100, 1, 50}, + {"page zero", 0, 50, 50, 100, 1, 50}, + {"per_page too large", 1, 200, 50, 100, 1, 100}, + {"page negative", -5, 50, 50, 100, 1, 50}, + {"per_page zero", 1, 0, 50, 100, 1, 50}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + page, perPage := validatePageParams(tt.page, tt.perPage, tt.defaultPerPage, tt.maxPerPage) + if page != tt.expectedPage { + t.Errorf("page: expected %d, got %d", tt.expectedPage, page) + } + if perPage != tt.expectedPerPage { + t.Errorf("perPage: expected %d, got %d", tt.expectedPerPage, perPage) + } + }) + } +} \ No newline at end of file diff --git a/backend/internal/handlers/news.go b/backend/internal/handlers/news.go index 374d0db..e9a0bfb 100644 --- a/backend/internal/handlers/news.go +++ b/backend/internal/handlers/news.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "encoding/json" "fmt" "net/http" "strconv" @@ -9,40 +10,45 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/rss2/backend/internal/config" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" ) -type NewsResponse struct { - ID string `json:"id"` - Titulo string `json:"titulo"` - Resumen string `json:"resumen"` - URL string `json:"url"` - Fecha *time.Time `json:"fecha"` - ImagenURL *string `json:"imagen_url"` - CategoriaID *int64 `json:"categoria_id"` - PaisID *int64 `json:"pais_id"` - FuenteNombre string `json:"fuente_nombre"` - TitleTranslated *string `json:"title_translated"` - SummaryTranslated *string `json:"summary_translated"` - LangTranslated *string `json:"lang_translated"` - Entities []Entity `json:"entities,omitempty"` +// getTargetLang returns the target language for translations, defaulting to config DefaultLang +func getTargetLang(c *gin.Context) string { + lang := c.Query("lang") + if lang == "" { + cfg := config.Load() + lang = cfg.DefaultLang + } + return lang } +// GetNews returns paginated news with optional filters +// @Summary List news +// @Description Returns paginated news with optional filtering by query, category, country, and translation status +// @Tags news +// @Produce json +// @Param page query int false "Page number" default(1) minimum(1) +// @Param per_page query int false "Items per page" default(30) minimum(1) maximum(100) +// @Param q query string false "Search query" +// @Param category_id query string false "Category ID filter" +// @Param country_id query string false "Country ID filter" +// @Param lang query string false "Target translation language" default(es) +// @Param translated_only query string false "Only show translated news" default("false") +// @Success 200 {object} map[string]interface{} "news, total, page, per_page, total_pages" +// @Failure 500 {object} models.ErrorResponse +// @Router /news [get] func GetNews(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "30")) + page, perPage = validatePageParams(page, perPage, 30, 100) query := c.Query("q") categoryID := c.Query("category_id") countryID := c.Query("country_id") translatedOnly := c.Query("translated_only") == "true" - - if page < 1 { - page = 1 - } - if perPage < 1 || perPage > 100 { - perPage = 30 - } + targetLang := getTargetLang(c) offset := (page - 1) * perPage @@ -53,7 +59,7 @@ func GetNews(c *gin.Context) { if query != "" { if translatedOnly { // With translated_only, search the translated fields too so users can - // find translated news by the Spanish (translated) wording. + // find translated news by the target language wording. where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d OR t.titulo_trad ILIKE $%d OR t.resumen_trad ILIKE $%d)", argNum, argNum, argNum, argNum) } else { where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d)", argNum, argNum) @@ -76,7 +82,8 @@ func GetNews(c *gin.Context) { } var total int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM noticias n LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es' WHERE %s", where) + countQuery := fmt.Sprintf("SELECT COUNT(*) FROM noticias n LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $%d WHERE %s", argNum, where) + args = append(args, targetLang) err := db.GetPool().QueryRow(c.Request.Context(), countQuery, args...).Scan(&total) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to count news", Message: err.Error()}) @@ -94,19 +101,18 @@ func GetNews(c *gin.Context) { return } - sqlQuery := fmt.Sprintf(` - SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, - n.categoria_id, n.pais_id, n.fuente_nombre, + sqlQuery := ` + SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.contenido, n.url, n.fecha, n.imagen_url, + n.categoria_id, n.pais_id, n.fuente_nombre, n.lang, t.titulo_trad, t.resumen_trad, t.lang_to as lang_trad FROM noticias n - LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es' - WHERE %s - ORDER BY n.fecha DESC LIMIT $%d OFFSET $%d - `, where, argNum, argNum+1) + LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $` + strconv.Itoa(argNum) + ` + WHERE ` + where + ` + ORDER BY n.fecha DESC LIMIT $` + strconv.Itoa(argNum+1) + ` OFFSET $` + strconv.Itoa(argNum+2) - args = append(args, perPage, offset) + args = append(args, targetLang, perPage, offset) rows, err := db.GetPool().Query(c.Request.Context(), sqlQuery, args...) if err != nil { @@ -115,15 +121,16 @@ func GetNews(c *gin.Context) { } defer rows.Close() - var newsList []NewsResponse + var newsList []models.NewsWithTranslations for rows.Next() { - var n NewsResponse + var n models.NewsWithTranslations var imagenURL, fuenteNombre *string var categoriaID, paisID *int32 + var lang string err := rows.Scan( - &n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL, - &categoriaID, &paisID, &fuenteNombre, + &n.ID, &n.Titulo, &n.Resumen, &n.Contenido, &n.URL, &n.Fecha, &imagenURL, + &categoriaID, &paisID, &fuenteNombre, &lang, &n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated, ) if err != nil { @@ -137,12 +144,13 @@ func GetNews(c *gin.Context) { } if categoriaID != nil { catID := int64(*categoriaID) - n.CategoriaID = &catID + n.CategoryID = &catID } if paisID != nil { pID := int64(*paisID) - n.PaisID = &pID + n.CountryID = &pID } + n.Lang = lang newsList = append(newsList, n) } @@ -161,18 +169,11 @@ func GetNews(c *gin.Context) { func GetEntityNews(c *gin.Context) { valor := c.Query("valor") tipo := c.DefaultQuery("tipo", "persona") - pageStr := c.DefaultQuery("page", "1") - perPageStr := c.DefaultQuery("per_page", "20") - - page, _ := strconv.Atoi(pageStr) - perPage, _ := strconv.Atoi(perPageStr) - if page < 1 { - page = 1 - } - if perPage < 1 || perPage > 50 { - perPage = 20 - } + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "20")) + page, perPage = validatePageParams(page, perPage, 20, 50) offset := (page - 1) * perPage + targetLang := getTargetLang(c) daysStr := c.Query("days") days := 0 @@ -194,8 +195,8 @@ func GetEntityNews(c *gin.Context) { useDayOffset = true } - params := []interface{}{valor, tipo} - next := 3 + params := []interface{}{valor, tipo, targetLang} + next := 4 timeCond := "" switch { case useDayOffset: @@ -214,7 +215,7 @@ func GetEntityNews(c *gin.Context) { base := fmt.Sprintf(` FROM tags_noticia tn JOIN tags t ON tn.tag_id = t.id - JOIN traducciones tr ON tn.traduccion_id = tr.id + JOIN traducciones tr ON tn.traduccion_id = tr.id AND tr.lang_to = $3 JOIN noticias n ON tr.noticia_id = n.id LEFT JOIN entity_aliases ea ON LOWER(ea.alias) = LOWER(t.valor) AND ea.tipo = t.tipo WHERE LOWER(COALESCE(ea.canonical_name, t.valor)) = LOWER($1) AND t.tipo = $2%s`, timeCond) @@ -227,8 +228,8 @@ func GetEntityNews(c *gin.Context) { } query := fmt.Sprintf(` - SELECT DISTINCT n.id, n.titulo, COALESCE(n.resumen,''), n.url, n.fecha, n.imagen_url, - n.fuente_nombre, tr.titulo_trad, tr.resumen_trad + SELECT DISTINCT n.id, n.titulo, COALESCE(n.resumen,''), n.contenido, n.url, n.fecha, n.imagen_url, + n.fuente_nombre, n.lang, tr.titulo_trad, tr.resumen_trad %s ORDER BY n.fecha DESC LIMIT $%d OFFSET $%d`, base, next, next+1) @@ -240,12 +241,12 @@ func GetEntityNews(c *gin.Context) { } defer rows.Close() - var news []NewsResponse + var news []models.NewsWithTranslations for rows.Next() { - var n NewsResponse + var n models.NewsWithTranslations var img *string - if err := rows.Scan(&n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &img, - &n.FuenteNombre, &n.TitleTranslated, &n.SummaryTranslated); err != nil { + if err := rows.Scan(&n.ID, &n.Titulo, &n.Resumen, &n.Contenido, &n.URL, &n.Fecha, &img, + &n.FuenteNombre, &n.Lang, &n.TitleTranslated, &n.SummaryTranslated); err != nil { continue } if img != nil { @@ -254,7 +255,7 @@ func GetEntityNews(c *gin.Context) { news = append(news, n) } if news == nil { - news = []NewsResponse{} + news = []models.NewsWithTranslations{} } totalPages := (total + perPage - 1) / perPage @@ -268,27 +269,65 @@ func GetEntityNews(c *gin.Context) { }) } +// GetNewsByID returns a single news item with entities +// @Summary Get news by ID +// @Description Returns a single news item with translations and associated entities +// @Tags news +// @Produce json +// @Param id path string true "News ID" +// @Param lang query string false "Target translation language" default(es) +// @Success 200 {object} models.NewsWithTranslations +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /news/{id} [get] func GetNewsByID(c *gin.Context) { id := c.Param("id") + targetLang := getTargetLang(c) + // Combined query to fetch news with entities in one round trip sqlQuery := ` - SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, - n.categoria_id, n.pais_id, n.fuente_nombre, + SELECT + n.id, n.titulo, COALESCE(n.resumen, ''), n.contenido, n.url, n.fecha, n.imagen_url, + n.categoria_id, n.pais_id, n.fuente_nombre, n.lang, t.titulo_trad, t.resumen_trad, - t.lang_to as lang_trad + t.lang_to as lang_trad, + json_agg( + json_build_object( + 'valor', ent.valor, + 'tipo', ent.tipo, + 'count', ent.cnt, + 'wiki_summary', ent.wiki_summary, + 'wiki_url', ent.wiki_url, + 'image_path', ent.image_path + ) + ) FILTER (WHERE ent.valor IS NOT NULL) as entities FROM noticias n - LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es' - WHERE n.id = $1` + LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = $1 + LEFT JOIN ( + SELECT tn.noticia_id, t.valor, t.tipo, 1 as cnt, t.wiki_summary, t.wiki_url, t.image_path + FROM tags_noticia tn + JOIN tags t ON tn.tag_id = t.id + JOIN traducciones tr ON tn.traduccion_id = tr.id + WHERE t.tipo IN ('persona', 'organizacion') + ) ent ON ent.noticia_id = n.id + WHERE n.id = $2 + GROUP BY n.id, n.titulo, n.resumen, n.contenido, n.url, n.fecha, n.imagen_url, + n.categoria_id, n.pais_id, n.fuente_nombre, n.lang, + t.titulo_trad, t.resumen_trad, t.lang_to + ` - var n NewsResponse + var n models.NewsWithTranslations var imagenURL, fuenteNombre *string var categoriaID, paisID *int32 + var lang string + var entitiesJSON *string - err := db.GetPool().QueryRow(c.Request.Context(), sqlQuery, id).Scan( - &n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL, - &categoriaID, &paisID, &fuenteNombre, + err := db.GetPool().QueryRow(c.Request.Context(), sqlQuery, targetLang, id).Scan( + &n.ID, &n.Titulo, &n.Resumen, &n.Contenido, &n.URL, &n.Fecha, &imagenURL, + &categoriaID, &paisID, &fuenteNombre, &lang, &n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated, + &entitiesJSON, ) if err != nil { c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "News not found"}) @@ -303,36 +342,23 @@ func GetNewsByID(c *gin.Context) { } if categoriaID != nil { catID := int64(*categoriaID) - n.CategoriaID = &catID + n.CategoryID = &catID } if paisID != nil { pID := int64(*paisID) - n.PaisID = &pID + n.CountryID = &pID } + n.Lang = lang - // Fetch entities for this news - entitiesQuery := ` - SELECT t.valor, t.tipo, 1 as cnt, t.wiki_summary, t.wiki_url, t.image_path - FROM tags_noticia tn - JOIN tags t ON tn.tag_id = t.id - JOIN traducciones tr ON tn.traduccion_id = tr.id - WHERE tr.noticia_id = $1 AND t.tipo IN ('persona', 'organizacion') - ` - rows, err := db.GetPool().Query(c.Request.Context(), entitiesQuery, id) - var entities []Entity - if err == nil { - defer rows.Close() - for rows.Next() { - var e Entity - if err := rows.Scan(&e.Valor, &e.Tipo, &e.Count, &e.WikiSummary, &e.WikiURL, &e.ImagePath); err == nil { - entities = append(entities, e) - } + // Parse entities from JSON + if entitiesJSON != nil && *entitiesJSON != "null" { + var entities []models.Entity + if err := json.Unmarshal([]byte(*entitiesJSON), &entities); err == nil { + n.Entities = entities } + } else { + n.Entities = []models.Entity{} } - if entities == nil { - entities = []Entity{} - } - n.Entities = entities c.JSON(http.StatusOK, n) } @@ -354,23 +380,20 @@ func DeleteNews(c *gin.Context) { c.JSON(http.StatusOK, models.SuccessResponse{Message: "News deleted successfully"}) } -type Entity struct { - Valor string `json:"valor"` - Tipo string `json:"tipo"` - Count int `json:"count"` - WikiSummary *string `json:"wiki_summary"` - WikiURL *string `json:"wiki_url"` - ImagePath *string `json:"image_path"` -} - -type EntityListResponse struct { - Entities []Entity `json:"entities"` - Total int `json:"total"` - Page int `json:"page"` - PerPage int `json:"per_page"` - TotalPages int `json:"total_pages"` -} - +// GetEntities returns paginated entities with optional filters +// @Summary List entities +// @Description Returns paginated entities (personas, organizations, locations) with optional filters +// @Tags entities +// @Produce json +// @Param tipo query string false "Entity type" default("persona") Enums(persona, organizacion, lugar, tema) +// @Param page query int false "Page number" default(1) minimum(1) +// @Param per_page query int false "Items per page" default(50) minimum(1) maximum(100) +// @Param country_id query string false "Country ID filter" +// @Param category_id query string false "Category ID filter" +// @Param q query string false "Search query" +// @Success 200 {object} models.EntityListResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /entities [get] func GetEntities(c *gin.Context) { countryID := c.Query("country_id") categoryID := c.Query("category_id") @@ -378,18 +401,9 @@ func GetEntities(c *gin.Context) { q := c.Query("q") - pageStr := c.DefaultQuery("page", "1") - perPageStr := c.DefaultQuery("per_page", "50") - - page, _ := strconv.Atoi(pageStr) - perPage, _ := strconv.Atoi(perPageStr) - - if page < 1 { - page = 1 - } - if perPage < 1 || perPage > 100 { - perPage = 50 - } + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "50")) + page, perPage = validatePageParams(page, perPage, 50, 100) offset := (page - 1) * perPage @@ -430,8 +444,8 @@ func GetEntities(c *gin.Context) { } if total == 0 { - c.JSON(http.StatusOK, EntityListResponse{ - Entities: []Entity{}, + c.JSON(http.StatusOK, models.EntityListResponse{ + Entities: []models.Entity{}, Total: 0, Page: page, PerPage: perPage, @@ -463,9 +477,9 @@ func GetEntities(c *gin.Context) { } defer rows.Close() - var entities []Entity + var entities []models.Entity for rows.Next() { - var e Entity + var e models.Entity if err := rows.Scan(&e.Valor, &e.Tipo, &e.Count, &e.WikiSummary, &e.WikiURL, &e.ImagePath); err != nil { continue } @@ -473,12 +487,12 @@ func GetEntities(c *gin.Context) { } if entities == nil { - entities = []Entity{} + entities = []models.Entity{} } totalPages := (total + perPage - 1) / perPage - c.JSON(http.StatusOK, EntityListResponse{ + c.JSON(http.StatusOK, models.EntityListResponse{ Entities: entities, Total: total, Page: page, @@ -487,23 +501,6 @@ func GetEntities(c *gin.Context) { }) } -type MentionPoint struct { - Fecha string `json:"fecha"` - Count int `json:"count"` -} - -type MentionSeries struct { - Valor string `json:"valor"` - Tipo string `json:"tipo"` - Count int `json:"count"` - Data []MentionPoint `json:"data"` -} - -type MentionsResponse struct { - Days int `json:"days"` - Series []MentionSeries `json:"series"` -} - func GetEntityMentions(c *gin.Context) { valuesRaw := c.DefaultQuery("values", "") days := 30 @@ -531,7 +528,7 @@ func GetEntityMentions(c *gin.Context) { start := time.Now().AddDate(0, 0, -(days - 1)).Truncate(24 * time.Hour) - series := make([]MentionSeries, 0, len(values)) + series := make([]models.MentionSeries, 0, len(values)) for _, valor := range values { s, err := buildMentionSeries(c.Request.Context(), valor, days) if err != nil { @@ -543,10 +540,10 @@ func GetEntityMentions(c *gin.Context) { for _, p := range s.Data { m[p.Fecha] = p.Count } - data := make([]MentionPoint, 0, days) + data := make([]models.MentionPoint, 0, days) for i := 0; i < days; i++ { fecha := start.AddDate(0, 0, i).Format("2006-01-02") - data = append(data, MentionPoint{Fecha: fecha, Count: m[fecha]}) + data = append(data, models.MentionPoint{Fecha: fecha, Count: m[fecha]}) } s.Data = data s.Count = 0 @@ -559,11 +556,11 @@ func GetEntityMentions(c *gin.Context) { series = append(series, *s) } - c.JSON(http.StatusOK, MentionsResponse{Days: days, Series: series}) + c.JSON(http.StatusOK, models.MentionsResponse{Days: days, Series: series}) } -func buildMentionSeries(ctx context.Context, valor string, days int) (*MentionSeries, error) { - s := &MentionSeries{Valor: valor} +func buildMentionSeries(ctx context.Context, valor string, days int) (*models.MentionSeries, error) { + s := &models.MentionSeries{Valor: valor} var tagIDs []int64 rows, err := db.GetPool().Query(ctx, ` @@ -590,7 +587,7 @@ func buildMentionSeries(ctx context.Context, valor string, days int) (*MentionSe rows.Close() if len(tagIDs) == 0 { - s.Data = []MentionPoint{} + s.Data = []models.MentionPoint{} return s, nil } @@ -615,10 +612,10 @@ func buildMentionSeries(ctx context.Context, valor string, days int) (*MentionSe if err := drows.Scan(&fecha, &cnt); err != nil { continue } - s.Data = append(s.Data, MentionPoint{Fecha: fecha, Count: cnt}) + s.Data = append(s.Data, models.MentionPoint{Fecha: fecha, Count: cnt}) } if s.Data == nil { - s.Data = []MentionPoint{} + s.Data = []models.MentionPoint{} } return s, nil diff --git a/backend/internal/handlers/news_test.go b/backend/internal/handlers/news_test.go new file mode 100644 index 0000000..4fc7108 --- /dev/null +++ b/backend/internal/handlers/news_test.go @@ -0,0 +1,38 @@ +package handlers + +import ( + "testing" +) + +func TestValidatePageParams(t *testing.T) { + tests := []struct { + name string + page int + perPage int + defaultPerPage int + maxPerPage int + expectedPage int + expectedPerPage int + }{ + {"normal", 1, 30, 30, 100, 1, 30}, + {"page zero", 0, 30, 30, 100, 1, 30}, + {"page negative", -5, 30, 30, 100, 1, 30}, + {"per_page zero", 1, 0, 30, 100, 1, 30}, + {"per_page negative", 1, -10, 30, 100, 1, 30}, + {"per_page exceeds max", 1, 200, 30, 100, 1, 100}, + {"page exceeds max", 999, 30, 30, 100, 999, 30}, + {"custom max", 1, 200, 20, 50, 1, 50}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + page, perPage := validatePageParams(tt.page, tt.perPage, tt.defaultPerPage, tt.maxPerPage) + if page != tt.expectedPage { + t.Errorf("page: expected %d, got %d", tt.expectedPage, page) + } + if perPage != tt.expectedPerPage { + t.Errorf("perPage: expected %d, got %d", tt.expectedPerPage, perPage) + } + }) + } +} \ No newline at end of file diff --git a/backend/internal/handlers/search.go b/backend/internal/handlers/search.go index ef5314f..edc7fa1 100644 --- a/backend/internal/handlers/search.go +++ b/backend/internal/handlers/search.go @@ -1,12 +1,14 @@ package handlers import ( + "encoding/json" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/rss2/backend/internal/auth" + "github.com/rss2/backend/internal/cache" "github.com/rss2/backend/internal/db" "github.com/rss2/backend/internal/models" "github.com/rss2/backend/internal/services" @@ -79,10 +81,27 @@ func SearchSuggestions(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"terms": terms}) } +// SearchNews searches news with various filters +// @Summary Search news +// @Description Search news with text query, language, category, country, and semantic search options +// @Tags search +// @Produce json +// @Param q query string false "Search query" +// @Param page query int false "Page number" default(1) minimum(1) +// @Param per_page query int false "Items per page" default(30) minimum(1) maximum(100) +// @Param lang query string false "Language code" default("es") +// @Param categoria_id query string false "Category ID filter" +// @Param pais_id query string false "Country ID filter" +// @Param semantic query string false "Use semantic search" default("false") +// @Success 200 {object} map[string]interface{} "news, total, page, per_page, total_pages" +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /search [get] func SearchNews(c *gin.Context) { query := c.Query("q") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "30")) + page, perPage = validatePageParams(page, perPage, 30, 100) lang := c.DefaultQuery("lang", "") categoriaID := c.Query("categoria_id") paisID := c.Query("pais_id") @@ -93,13 +112,6 @@ func SearchNews(c *gin.Context) { return } - if page < 1 { - page = 1 - } - if perPage < 1 || perPage > 100 { - perPage = 30 - } - // Default to Spanish if no lang specified if lang == "" { lang = "es" @@ -117,6 +129,15 @@ func SearchNews(c *gin.Context) { return } + // Cache key for search results + cacheKey := cache.SearchKey(query, lang, page, perPage) + + // Try to get from cache + if cached, err := cache.Get(ctx, cacheKey); err == nil && cached != "" { + c.Data(http.StatusOK, "application/json", []byte(cached)) + return + } + offset := (page - 1) * perPage // Build dynamic query @@ -147,8 +168,8 @@ func SearchNews(c *gin.Context) { } sqlQuery := ` - SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, - n.categoria_id, n.pais_id, n.fuente_nombre, + SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.contenido, n.url, n.fecha, n.imagen_url, + n.categoria_id, n.pais_id, n.fuente_nombre, n.lang, t.titulo_trad, t.resumen_trad, t.lang_to as lang_trad @@ -167,15 +188,16 @@ func SearchNews(c *gin.Context) { } defer rows.Close() - var newsList []NewsResponse + var newsList []models.NewsWithTranslations for rows.Next() { - var n NewsResponse + var n models.NewsWithTranslations var imagenURL, fuenteNombre *string var categoriaIDp, paisIDp *int64 + var lang string err := rows.Scan( - &n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL, - &categoriaIDp, &paisIDp, &fuenteNombre, + &n.ID, &n.Titulo, &n.Resumen, &n.Contenido, &n.URL, &n.Fecha, &imagenURL, + &categoriaIDp, &paisIDp, &fuenteNombre, &lang, &n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated, ) if err != nil { @@ -187,8 +209,9 @@ func SearchNews(c *gin.Context) { if fuenteNombre != nil { n.FuenteNombre = *fuenteNombre } - n.CategoriaID = categoriaIDp - n.PaisID = paisIDp + n.CategoryID = categoriaIDp + n.CountryID = paisIDp + n.Lang = lang newsList = append(newsList, n) } @@ -206,19 +229,42 @@ func SearchNews(c *gin.Context) { totalPages := (total + perPage - 1) / perPage - c.JSON(http.StatusOK, gin.H{ + response := gin.H{ "news": newsList, "total": total, "page": page, "per_page": perPage, "total_pages": totalPages, - }) + } + + // Cache the response + if data, err := json.Marshal(response); err == nil { + cache.Set(ctx, cacheKey, string(data), cache.TTLShort) + } + +c.JSON(http.StatusOK, response) } +// GetStats returns global statistics +// @Summary Get statistics +// @Description Returns global statistics about news, feeds, users, and translations +// @Tags stats +// @Produce json +// @Success 200 {object} models.Stats +// @Failure 500 {object} models.ErrorResponse +// @Router /stats [get] func GetStats(c *gin.Context) { + ctx := c.Request.Context() + cacheKey := cache.StatsKey() + + if cached, err := cache.Get(ctx, cacheKey); err == nil && cached != "" { + c.Data(http.StatusOK, "application/json", []byte(cached)) + return + } + var stats models.Stats - err := db.GetPool().QueryRow(c.Request.Context(), ` + err := db.GetPool().QueryRow(ctx, ` SELECT (SELECT COUNT(*) FROM noticias) as total_news, (SELECT COUNT(*) FROM feeds WHERE activo = true) as total_feeds, @@ -237,7 +283,7 @@ func GetStats(c *gin.Context) { return } - rows, err := db.GetPool().Query(c.Request.Context(), ` + rows, err := db.GetPool().Query(ctx, ` SELECT c.id, c.nombre, COUNT(n.id) as count FROM categorias c LEFT JOIN noticias n ON n.categoria_id = c.id @@ -249,12 +295,12 @@ func GetStats(c *gin.Context) { defer rows.Close() for rows.Next() { var cs models.CategoryStat - rows.Scan(&cs.CategoryID, &cs.CategoryName, &cs.Count) + rows.Scan(&cs.CategoriaID, &cs.CategoriaName, &cs.Count) stats.TopCategories = append(stats.TopCategories, cs) } } - rows, err = db.GetPool().Query(c.Request.Context(), ` + rows, err = db.GetPool().Query(ctx, ` SELECT p.id, p.nombre, p.flag_emoji, COUNT(n.id) as count FROM paises p LEFT JOIN noticias n ON n.pais_id = p.id @@ -266,16 +312,28 @@ func GetStats(c *gin.Context) { defer rows.Close() for rows.Next() { var cs models.CountryStat - rows.Scan(&cs.CountryID, &cs.CountryName, &cs.FlagEmoji, &cs.Count) + rows.Scan(&cs.PaisID, &cs.PaisName, &cs.FlagEmoji, &cs.Count) stats.TopCountries = append(stats.TopCountries, cs) } } + if data, err := json.Marshal(stats); err == nil { + cache.Set(ctx, cacheKey, string(data), cache.TTLMedium) + } + c.JSON(http.StatusOK, stats) } func GetCategories(c *gin.Context) { - rows, err := db.GetPool().Query(c.Request.Context(), ` + ctx := c.Request.Context() + cacheKey := cache.CategoriesKey() + + if cached, err := cache.Get(ctx, cacheKey); err == nil && cached != "" { + c.Data(http.StatusOK, "application/json", []byte(cached)) + return + } + + rows, err := db.GetPool().Query(ctx, ` SELECT id, nombre FROM categorias ORDER BY nombre`) if err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to get categories", Message: err.Error()}) @@ -283,23 +341,30 @@ func GetCategories(c *gin.Context) { } defer rows.Close() - type Category struct { - ID int64 `json:"id"` - Nombre string `json:"nombre"` - } - - var categories []Category + var categories []models.Category for rows.Next() { - var cat Category + var cat models.Category rows.Scan(&cat.ID, &cat.Nombre) categories = append(categories, cat) } + if data, err := json.Marshal(categories); err == nil { + cache.Set(ctx, cacheKey, string(data), cache.TTLLong) + } + c.JSON(http.StatusOK, categories) } func GetCountries(c *gin.Context) { - rows, err := db.GetPool().Query(c.Request.Context(), ` + ctx := c.Request.Context() + cacheKey := cache.CountriesKey() + + if cached, err := cache.Get(ctx, cacheKey); err == nil && cached != "" { + c.Data(http.StatusOK, "application/json", []byte(cached)) + return + } + + rows, err := db.GetPool().Query(ctx, ` SELECT p.id, p.nombre, c.nombre as continente FROM paises p LEFT JOIN continentes c ON c.id = p.continente_id @@ -310,18 +375,16 @@ func GetCountries(c *gin.Context) { } defer rows.Close() - type Country struct { - ID int64 `json:"id"` - Nombre string `json:"nombre"` - Continente string `json:"continente"` - } - - var countries []Country + var countries []models.Country for rows.Next() { - var country Country + var country models.Country rows.Scan(&country.ID, &country.Nombre, &country.Continente) countries = append(countries, country) } + if data, err := json.Marshal(countries); err == nil { + cache.Set(ctx, cacheKey, string(data), cache.TTLLong) + } + c.JSON(http.StatusOK, countries) } diff --git a/backend/internal/handlers/search_test.go b/backend/internal/handlers/search_test.go new file mode 100644 index 0000000..6995814 --- /dev/null +++ b/backend/internal/handlers/search_test.go @@ -0,0 +1,35 @@ +package handlers + +import ( + "testing" +) + +func TestValidatePageParamsSearch(t *testing.T) { + tests := []struct { + name string + page int + perPage int + defaultPerPage int + maxPerPage int + expectedPage int + expectedPerPage int + }{ + {"normal", 1, 30, 30, 100, 1, 30}, + {"page zero", 0, 30, 30, 100, 1, 30}, + {"per_page too large", 1, 200, 30, 100, 1, 100}, + {"page negative", -5, 30, 30, 100, 1, 30}, + {"per_page zero", 1, 0, 30, 100, 1, 30}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + page, perPage := validatePageParams(tt.page, tt.perPage, tt.defaultPerPage, tt.maxPerPage) + if page != tt.expectedPage { + t.Errorf("page: expected %d, got %d", tt.expectedPage, page) + } + if perPage != tt.expectedPerPage { + t.Errorf("perPage: expected %d, got %d", tt.expectedPerPage, perPage) + } + }) + } +} \ No newline at end of file diff --git a/backend/internal/handlers/validation.go b/backend/internal/handlers/validation.go new file mode 100644 index 0000000..fc8f66e --- /dev/null +++ b/backend/internal/handlers/validation.go @@ -0,0 +1,14 @@ +package handlers + +func validatePageParams(page, perPage, defaultPerPage, maxPerPage int) (int, int) { + if page < 1 { + page = 1 + } + if perPage < 1 { + perPage = defaultPerPage + } + if perPage > maxPerPage { + perPage = maxPerPage + } + return page, perPage +} \ No newline at end of file diff --git a/backend/internal/logger/logger.go b/backend/internal/logger/logger.go new file mode 100644 index 0000000..239d89c --- /dev/null +++ b/backend/internal/logger/logger.go @@ -0,0 +1,60 @@ +package logger + +import ( + "os" + "time" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +var Logger zerolog.Logger + +func Init(serviceName, level string) { + zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs + zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string { + short := file + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + short = file[i+1:] + break + } + } + return short + ":" + string(rune(line)) + } + + output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: false} + logger := zerolog.New(output).With().Timestamp().Caller().Str("service", serviceName).Logger() + + switch level { + case "debug": + logger = logger.Level(zerolog.DebugLevel) + case "info": + logger = logger.Level(zerolog.InfoLevel) + case "warn": + logger = logger.Level(zerolog.WarnLevel) + case "error": + logger = logger.Level(zerolog.ErrorLevel) + default: + logger = logger.Level(zerolog.InfoLevel) + } + + Logger = logger + log.Logger = logger +} + +func GetLogger() zerolog.Logger { + return Logger +} + +func WithRequestID(requestID string) zerolog.Logger { + return Logger.With().Str("request_id", requestID).Logger() +} + +func WithFields(fields map[string]interface{}) zerolog.Logger { + ctx := Logger.With() + for k, v := range fields { + ctx = ctx.Interface(k, v) + } + return ctx.Logger() +} \ No newline at end of file diff --git a/backend/internal/middleware/auth.go b/backend/internal/middleware/auth.go index 4c35c35..92d8e2b 100644 --- a/backend/internal/middleware/auth.go +++ b/backend/internal/middleware/auth.go @@ -1,12 +1,17 @@ package middleware import ( + "fmt" "net/http" "strings" + "sync" + "time" "github.com/gin-gonic/gin" "github.com/rss2/backend/internal/auth" "github.com/rss2/backend/internal/config" + "github.com/rss2/backend/internal/logger" + "golang.org/x/time/rate" ) func AuthRequired() gin.HandlerFunc { @@ -63,23 +68,19 @@ func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { origin := c.Request.Header.Get("Origin") - + allowed := false for _, o := range allowedOrigins { o = strings.TrimSpace(o) - if o == "*" || o == origin { + if o == origin { allowed = true break } } if allowed { - if cfg.AllowedOrigins == "*" { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - } else { - c.Writer.Header().Set("Access-Control-Allow-Origin", origin) - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - } + c.Writer.Header().Set("Access-Control-Allow-Origin", origin) + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") } c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") @@ -96,17 +97,101 @@ func CORSMiddleware() gin.HandlerFunc { func LoggerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + raw := c.Request.URL.RawQuery + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = c.GetString("request_id") + } + c.Next() + latency := time.Since(start) status := c.Writer.Status() - if status >= 400 { - // Log error responses + clientIP := c.ClientIP() + method := c.Request.Method + + if raw != "" { + path = path + "?" + raw + } + + log := logger.GetLogger().With(). + Str("method", method). + Str("path", path). + Int("status", status). + Str("client_ip", clientIP). + Dur("latency", latency). + Logger() + + if requestID != "" { + log = log.With().Str("request_id", requestID).Logger() + } + + if status >= 500 { + log.Error().Msg("Server error") + } else if status >= 400 { + log.Warn().Msg("Client error") + } else { + log.Info().Msg("Request completed") } } } +type clientLimiter struct { + limiter *rate.Limiter + lastSeen time.Time +} + +var ( + clientLimiters = make(map[string]*clientLimiter) + limitersMu sync.Mutex +) + func RateLimitMiddleware(requestsPerMinute int) gin.HandlerFunc { + limit := rate.Limit(requestsPerMinute) / 60 + burst := requestsPerMinute + return func(c *gin.Context) { + ip := c.ClientIP() + + limitersMu.Lock() + cl, exists := clientLimiters[ip] + if !exists { + cl = &clientLimiter{limiter: rate.NewLimiter(limit, burst)} + clientLimiters[ip] = cl + } + cl.lastSeen = time.Now() + limiter := cl.limiter + limitersMu.Unlock() + + if !limiter.Allow() { + c.JSON(http.StatusTooManyRequests, gin.H{"error": "Rate limit exceeded"}) + c.Abort() + return + } + + go func() { + time.Sleep(10 * time.Minute) + limitersMu.Lock() + if cl, ok := clientLimiters[ip]; ok && time.Since(cl.lastSeen) > 10*time.Minute { + delete(clientLimiters, ip) + } + limitersMu.Unlock() + }() + + c.Next() + } +} + +func RequestIDMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = fmt.Sprintf("%d", time.Now().UnixNano()) + } + c.Set("request_id", requestID) + c.Header("X-Request-ID", requestID) c.Next() } } diff --git a/backend/internal/models/models.go b/backend/internal/models/models.go index 5653503..d118603 100644 --- a/backend/internal/models/models.go +++ b/backend/internal/models/models.go @@ -6,12 +6,12 @@ import ( type News struct { ID int64 `json:"id"` - Title string `json:"title"` - Summary string `json:"summary"` - Content string `json:"content"` + Titulo string `json:"titulo"` + Resumen string `json:"resumen"` + Contenido string `json:"contenido"` URL string `json:"url"` - ImageURL *string `json:"image_url"` - PublishedAt *time.Time `json:"published_at"` + ImagenURL *string `json:"imagen_url"` + Fecha *time.Time `json:"fecha"` Lang string `json:"lang"` FeedID int64 `json:"feed_id"` CreatedAt time.Time `json:"created_at"` @@ -20,35 +20,73 @@ type News struct { type NewsWithTranslations struct { ID int64 `json:"id"` - Title string `json:"title"` - Summary string `json:"summary"` - Content string `json:"content"` + Titulo string `json:"titulo"` + Resumen string `json:"resumen"` + Contenido string `json:"contenido"` URL string `json:"url"` - ImageURL *string `json:"image_url"` - PublishedAt *string `json:"published_at"` + ImagenURL *string `json:"imagen_url"` + Fecha *string `json:"fecha"` Lang string `json:"lang"` FeedID int64 `json:"feed_id"` - CategoryID *int64 `json:"category_id"` - CountryID *int64 `json:"country_id"` + CategoryID *int64 `json:"categoria_id"` + CountryID *int64 `json:"pais_id"` + FuenteNombre string `json:"fuente_nombre"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` TitleTranslated *string `json:"title_translated"` SummaryTranslated *string `json:"summary_translated"` ContentTranslated *string `json:"content_translated"` LangTranslated *string `json:"lang_translated"` + Entities []Entity `json:"entities,omitempty"` +} + +type Entity struct { + Valor string `json:"valor"` + Tipo string `json:"tipo"` + Count int `json:"count"` + WikiSummary *string `json:"wiki_summary"` + WikiURL *string `json:"wiki_url"` + ImagePath *string `json:"image_path"` +} + +type EntityListResponse struct { + Entities []Entity `json:"entities"` + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + TotalPages int `json:"total_pages"` +} + +type MentionPoint struct { + Fecha string `json:"fecha"` + Count int `json:"count"` +} + +type MentionSeries struct { + Valor string `json:"valor"` + Tipo string `json:"tipo"` + Count int `json:"count"` + Data []MentionPoint `json:"data"` +} + +type MentionsResponse struct { + Days int `json:"days"` + Series []MentionSeries `json:"series"` } type Feed struct { ID int64 `json:"id"` - Title string `json:"title"` + Nombre string `json:"nombre"` URL string `json:"url"` SiteURL *string `json:"site_url"` - Description *string `json:"description"` - ImageURL *string `json:"image_url"` - Language *string `json:"language"` - CategoryID *int64 `json:"category_id"` - CountryID *int64 `json:"country_id"` - Active bool `json:"active"` + Descripcion *string `json:"descripcion"` + ImagenURL *string `json:"imagen_url"` + Idioma *string `json:"idioma"` + CategoriaID *int64 `json:"categoria_id"` + PaisID *int64 `json:"pais_id"` + Activo bool `json:"activo"` + Fallos *int64 `json:"fallos"` + LastError *string `json:"last_error"` LastFetched *time.Time `json:"last_fetched"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` @@ -56,27 +94,27 @@ type Feed struct { type Category struct { ID int64 `json:"id"` - Name string `json:"name"` + Nombre string `json:"nombre"` Color string `json:"color"` Icon string `json:"icon"` ParentID *int64 `json:"parent_id"` } type Country struct { - ID int64 `json:"id"` - Name string `json:"name"` - Code string `json:"code"` - Continent string `json:"continent"` - FlagEmoji string `json:"flag_emoji"` + ID int64 `json:"id"` + Nombre string `json:"nombre"` + Codigo string `json:"codigo"` + Continente string `json:"continente"` + FlagEmoji string `json:"flag_emoji"` } type Translation struct { ID int64 `json:"id"` - NewsID int64 `json:"news_id"` + NoticiaID int64 `json:"noticia_id"` LangFrom string `json:"lang_from"` LangTo string `json:"lang_to"` - Title string `json:"title"` - Summary string `json:"summary"` + Titulo string `json:"titulo"` + Resumen string `json:"resumen"` Status string `json:"status"` Error *string `json:"error"` CreatedAt time.Time `json:"created_at"` @@ -89,6 +127,7 @@ type User struct { Username string `json:"username"` PasswordHash string `json:"-"` IsAdmin bool `json:"is_admin"` + Role string `json:"role"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } @@ -97,8 +136,8 @@ type SearchHistory struct { ID int64 `json:"id"` UserID int64 `json:"user_id"` Query string `json:"query"` - CategoryID *int64 `json:"category_id"` - CountryID *int64 `json:"country_id"` + CategoriaID *int64 `json:"categoria_id"` + PaisID *int64 `json:"pais_id"` ResultsCount int `json:"results_count"` SearchedAt time.Time `json:"searched_at"` } @@ -132,14 +171,14 @@ type Stats struct { } type CategoryStat struct { - CategoryID int64 `json:"category_id"` - CategoryName string `json:"category_name"` - Count int64 `json:"count"` + CategoriaID int64 `json:"categoria_id"` + CategoriaName string `json:"categoria_nombre"` + Count int64 `json:"count"` } type CountryStat struct { - CountryID int64 `json:"country_id"` - CountryName string `json:"country_name"` + PaisID int64 `json:"pais_id"` + PaisName string `json:"pais_nombre"` FlagEmoji string `json:"flag_emoji"` Count int64 `json:"count"` } diff --git a/backend/internal/workers/db.go b/backend/internal/workers/db.go index 7f01625..fe899ae 100644 --- a/backend/internal/workers/db.go +++ b/backend/internal/workers/db.go @@ -8,10 +8,9 @@ import ( "time" "github.com/jackc/pgx/v5/pgxpool" + "github.com/rss2/backend/internal/db" ) -var pool *pgxpool.Pool - type Config struct { Host string Port int @@ -31,39 +30,23 @@ func LoadDBConfig() *Config { } func Connect(cfg *Config) error { - dsn := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", - cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName) - - poolConfig, err := pgxpool.ParseConfig(dsn) - if err != nil { - return fmt.Errorf("failed to parse config: %w", err) + // Just verify the main pool is accessible + if db.GetPool() == nil { + return fmt.Errorf("database pool not initialized") } - - poolConfig.MaxConns = 25 - poolConfig.MinConns = 5 - poolConfig.MaxConnLifetime = time.Hour - poolConfig.MaxConnIdleTime = 30 * time.Minute - - pool, err = pgxpool.NewWithConfig(context.Background(), poolConfig) - if err != nil { - return fmt.Errorf("failed to create pool: %w", err) - } - - if err = pool.Ping(context.Background()); err != nil { - return fmt.Errorf("failed to ping database: %w", err) - } - - return nil + return db.HealthCheck(context.Background()) } func GetPool() *pgxpool.Pool { - return pool + return db.GetPool() } func Close() { - if pool != nil { - pool.Close() - } + // No-op: main db package manages the pool +} + +func HealthCheck(ctx context.Context) error { + return db.HealthCheck(ctx) } func getEnv(key, defaultValue string) string { diff --git a/docker-compose.yml b/docker-compose.yml index 4c9ea80..db6b4f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -340,12 +340,12 @@ services: condition: service_healthy restart: unless-stopped # Prioridad BAJA: el traductor no debe acaparar toda la CPU. - # cpu_shares menor que el resto (1024) + tope de 2 núcleos dejan CPU para NER, embeddings, etc. + # cpu_shares menor que el resto (1024) + tope que deje CPU para NER, embeddings, etc. cpu_shares: 512 deploy: resources: limits: - cpus: '2' + cpus: '4' memory: 6G # ================================================================================== @@ -390,7 +390,7 @@ services: deploy: resources: limits: - cpus: '2' + cpus: '4' memory: 6G # ================================================================================== diff --git a/entity_config.json b/entity_config.json index cbcaca5..b028678 100644 --- a/entity_config.json +++ b/entity_config.json @@ -99,8 +99,16 @@ "Pakistán", "Tolo News", "Gladbach", - "Alger" - + "Alger", + "Comisión", + "Dirección", + "Más", + "Regístrese", + "Consejo", + "Fondo", + "PVT LTD", + "Beneficios", + "Ingrese" ], "synonyms": { "Alassane Ouattara": [ diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx index f203767..465279b 100644 --- a/frontend/src/components/layout/Layout.tsx +++ b/frontend/src/components/layout/Layout.tsx @@ -1,6 +1,6 @@ import { Outlet, Link, useNavigate } from 'react-router-dom' -import { Search, Rss, BarChart3, Home as HomeIcon, Heart, User, Flame, Settings, Users, Tags, Database, Server, TrendingUp, Bell } from 'lucide-react' -import { useEffect, useState } from 'react' +import { Search, Rss, BarChart3, Home as HomeIcon, Heart, User, Flame, Settings, Users, Tags, Database, Server, TrendingUp, Bell, X, Menu } from 'lucide-react' +import { useEffect, useState, useRef } from 'react' import { apiService } from '../../services/api' export function Layout() { diff --git a/frontend/src/components/ui/EmptyState.tsx b/frontend/src/components/ui/EmptyState.tsx new file mode 100644 index 0000000..71f8d6c --- /dev/null +++ b/frontend/src/components/ui/EmptyState.tsx @@ -0,0 +1,166 @@ +import { Search, Rss, Globe, AlertTriangle, Inbox } from 'lucide-react' + +interface EmptyStateProps { + icon?: 'search' | 'rss' | 'globe' | 'alert' | 'inbox' + title: string + description: string + action?: { + label: string + onClick: () => void + } + className?: string +} + +export function EmptyState({ + icon = 'search', + title, + description, + action, + className = '' +}: EmptyStateProps) { + const icons = { + search: Search, + rss: Rss, + globe: Globe, + alert: AlertTriangle, + inbox: Inbox, + } + + const Icon = icons[icon] + + return ( +
    +
    + +
    +

    {title}

    +

    {description}

    + {action && ( + + )} +
    + ) +} + +export function NoResultsFound({ + query, + onClearFilters +}: { + query?: string + onClearFilters?: () => void +}) { + return ( + + ) +} + +export function NoNewsYet({ + onAddFeed +}: { + onAddFeed?: () => void +}) { + return ( + + ) +} + +export function NoFeedsConfigured({ + onAddFeed +}: { + onAddFeed?: () => void +}) { + return ( + + ) +} + +export function NoSearchResults({ + query, + onSearch +}: { + query?: string + onSearch?: () => void +}) { + return ( + + ) +} + +export function OfflineState({ + onRetry +}: { + onRetry?: () => void +}) { + return ( + + ) +} + +export function ErrorState({ + message = 'Ha ocurrido un error inesperado', + onRetry +}: { + message?: string + onRetry?: () => void +}) { + return ( + + ) +} \ No newline at end of file diff --git a/frontend/src/components/ui/Skeleton.tsx b/frontend/src/components/ui/Skeleton.tsx new file mode 100644 index 0000000..30cb580 --- /dev/null +++ b/frontend/src/components/ui/Skeleton.tsx @@ -0,0 +1,110 @@ +export function SkeletonNewsCard() { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ) +} + +interface SkeletonNewsListProps { + count?: number +} + +export function SkeletonNewsList({ count = 6 }: SkeletonNewsListProps) { + return ( +
    + {Array.from({ length: count }).map((_, i) => ( + + ))} +
    + ) +} + +export function SkeletonFeedCard() { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ) +} + +interface SkeletonFeedListProps { + count?: number +} + +export function SkeletonFeedList({ count = 5 }: SkeletonFeedListProps) { + return ( +
    + {Array.from({ length: count }).map((_, i) => ( + + ))} +
    + ) +} + +export function SkeletonEntityCard() { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    + ) +} + +interface SkeletonEntityListProps { + count?: number +} + +export function SkeletonEntityList({ count = 4 }: SkeletonEntityListProps) { + return ( +
    + {Array.from({ length: count }).map((_, i) => ( + + ))} +
    + ) +} + +export function SkeletonStats() { + return ( +
    + {Array.from({ length: 4 }).map((_, i) => ( +
    +
    +
    +
    + ))} +
    + ) +} + +export function SkeletonChart() { + return ( +
    +
    +
    +
    + ) +} \ No newline at end of file diff --git a/frontend/src/components/ui/WikiTooltip.tsx b/frontend/src/components/ui/WikiTooltip.tsx index 96624a7..7484cf8 100644 --- a/frontend/src/components/ui/WikiTooltip.tsx +++ b/frontend/src/components/ui/WikiTooltip.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useRef, useEffect } from 'react'; import { ExternalLink } from 'lucide-react'; interface WikiTooltipProps { @@ -14,19 +14,43 @@ export function WikiTooltip({ children, summary, imagePath, wikiUrl, name }: Wik return {children}; } + const tooltipRef = useRef(null) + + // Close tooltip on Escape key + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + // Force blur to close tooltip + const activeElement = document.activeElement as HTMLElement + activeElement?.blur() + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, []) + return ( -
    - +
    + {children} -
    +
    ); diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts new file mode 100644 index 0000000..7a1a2dd --- /dev/null +++ b/frontend/src/components/ui/index.ts @@ -0,0 +1,3 @@ +export { SkeletonNewsCard, SkeletonNewsList, SkeletonFeedCard, SkeletonFeedList, SkeletonEntityCard, SkeletonEntityList, SkeletonStats, SkeletonChart } from './Skeleton' +export { EmptyState, NoResultsFound, NoNewsYet, NoFeedsConfigured, NoSearchResults, OfflineState, ErrorState } from './EmptyState' +export { WikiTooltip } from './WikiTooltip' \ No newline at end of file diff --git a/frontend/src/pages/Alertas.tsx b/frontend/src/pages/Alertas.tsx index 9f5853b..10fe7c6 100644 --- a/frontend/src/pages/Alertas.tsx +++ b/frontend/src/pages/Alertas.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' -import { apiService, Alerta } from '../services/api' -import { Bell, CheckCheck, ChevronDown, ChevronUp } from 'lucide-react' +import { Link } from 'react-router-dom' +import { apiService, api, Alerta, News } from '../services/api' +import { Bell, CheckCheck, ChevronDown, ChevronUp, FileText } from 'lucide-react' const TIPO_LABEL: Record = { persona: 'Persona', @@ -15,6 +16,11 @@ export function Alertas() { const [nuevas, setNuevas] = useState(0) const [limit, setLimit] = useState(50) + const [newsModal, setNewsModal] = useState(null) + const [entityNews, setEntityNews] = useState([]) + const [newsLoading, setNewsLoading] = useState(false) + const [newsTotal, setNewsTotal] = useState(0) + const load = async () => { setLoading(true) try { @@ -42,6 +48,26 @@ export function Alertas() { load() } + const openNews = async (alerta: Alerta) => { + setNewsModal(alerta) + setNewsLoading(true) + setEntityNews([]) + setNewsTotal(0) + try { + const params = new URLSearchParams() + params.append('valor', alerta.valor) + params.append('tipo', alerta.tipo) + params.append('per_page', '50') + const res = await api.get(`/entities/news?${params}`) + setEntityNews(res.data.news || []) + setNewsTotal(res.data.total || 0) + } catch (err) { + console.error(err) + } finally { + setNewsLoading(false) + } + } + return (
    @@ -108,13 +134,17 @@ export function Alertas() { - {a.status === 'nueva' ? ( - - ) : ( - - )} + {a.status === 'nueva' && ( + + )} +
    ))} @@ -132,6 +162,75 @@ export function Alertas() { Mostrar {limit > 50 ? 'menos' : 'más'} ({limit})
    + + {/* Noticias relacionadas del concepto */} + {newsModal && ( +
    setNewsModal(null)}> +
    e.stopPropagation()} + > +
    +
    +

    Noticias sobre "{newsModal.valor}"

    +

    + {newsTotal} noticias encontradas · {TIPO_LABEL[newsModal.tipo] || newsModal.tipo} · {newsModal.periodo} +

    +
    + +
    + +
    + {newsLoading ? ( +
    Cargando...
    + ) : entityNews.length === 0 ? ( +
    No hay noticias que mencionen a este concepto.
    + ) : ( +
      + {entityNews.map((n) => ( +
    • + setNewsModal(null)} + className="block p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors border border-gray-100 dark:border-gray-700" + > +
      + {n.imagen_url && ( + (e.currentTarget.style.display = 'none')} + /> + )} +
      +
      + {n.title_translated || n.titulo} +
      +

      + {n.summary_translated || n.resumen} +

      + {n.fecha && ( + + {new Date(n.fecha).toLocaleDateString('es-ES')} + + )} +
      +
      + +
    • + ))} +
    + )} +
    +
    +
    + )}
    ) } \ No newline at end of file diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index cac8c99..b567df2 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -5,6 +5,7 @@ import { formatDistanceToNow } from 'date-fns' import { es } from 'date-fns/locale' import { apiService, api } from '../services/api' import { Search, Globe, Newspaper, Filter } from 'lucide-react' +import { SkeletonNewsList, NoResultsFound } from '../components/ui' export function Home() { const [searchParams, setSearchParams] = useSearchParams() @@ -156,13 +157,22 @@ export function Home() {
    {isLoading ? ( -
    -
    -
    + ) : error ? (
    Error al cargar noticias +
    + ) : data?.news?.length === 0 ? ( + setSearchParams({ page: '1', q })} + /> ) : ( <>
    diff --git a/frontend/src/pages/Populares.tsx b/frontend/src/pages/Populares.tsx index 39bcd95..5acff34 100644 --- a/frontend/src/pages/Populares.tsx +++ b/frontend/src/pages/Populares.tsx @@ -33,13 +33,13 @@ const TIPOS = [ { value: 'tema', label: '📰 Tema', color: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' }, ] -const TIMELINE_WINDOWS: { key: string; label: string; title: string; offset: number }[] = [ - { key: '0', label: 'Hoy', title: 'hoy', offset: 0 }, - { key: '1', label: '1d', title: 'hace 1 día', offset: 1 }, - { key: '2', label: '2d', title: 'hace 2 días', offset: 2 }, - { key: '3', label: '3d', title: 'hace 3 días', offset: 3 }, - { key: '4', label: '4d', title: 'hace 4 días', offset: 4 }, - { key: '5', label: '5d', title: 'hace 5 días', offset: 5 }, +const TIMELINE_WINDOWS: { key: string; label: string; title: string; days: number }[] = [ + { key: '1', label: '24h', title: 'últimas 24 horas', days: 1 }, + { key: '2', label: '2 días', title: 'últimos 2 días', days: 2 }, + { key: '3', label: '3 días', title: 'últimos 3 días', days: 3 }, + { key: '4', label: '4 días', title: 'últimos 4 días', days: 4 }, + { key: '5', label: '5 días', title: 'últimos 5 días', days: 5 }, + { key: '7', label: '7 días', title: 'últimos 7 días', days: 7 }, ] function getTipoInfo(tipo: string) { @@ -61,6 +61,8 @@ export function Populares() { const [configModal, setConfigModal] = useState(null) const [saving, setSaving] = useState(false) const [successMsg, setSuccessMsg] = useState('') + const [expandedEntities, setExpandedEntities] = useState>(new Set()) + const [allExpanded, setAllExpanded] = useState(false) // News-by-entity modal state const [newsModal, setNewsModal] = useState(null) @@ -158,7 +160,7 @@ export function Populares() { const params = new URLSearchParams() params.append('valor', entity.valor) params.append('tipo', entity.tipo) - params.append('day_offset', String(win.offset)) + params.append('days', String(win.days)) params.append('per_page', '50') const res = await api.get(`/entities/news?${params}`) setEntityNews(res.data.news || []) @@ -270,6 +272,29 @@ export function Populares() {

    🔥 Populares {selectedCountry ? `en ${selectedCountry.nombre}` : 'Global'}

    +
    + + + +
    - {/* Table */} + {/* Listado de entidades */} {loading ? (
    Cargando...
    ) : entities.length === 0 ? (
    No se encontraron entidades
    ) : (
    - - - - - - - - - - - - {entities.map((entity, idx) => { - const tipoInfo = getTipoInfo(entity.tipo) - return ( - - - - - - - - ) - })} - -
    #EntidadTipoMencionesConfig
    {(page - 1) * 50 + idx + 1} +
    +
    + 📊 {entities.length} entidades populares +
    + + +
    +
    +
    +
    + {entities.map((entity, idx) => { + const isExpanded = allExpanded || expandedEntities.has(entity.valor) + const tipoInfo = getTipoInfo(entity.tipo) + return ( +
    +
    +
    setExpandedEntities(p => p.toggle(entity.valor))}> -
    - {entity.image_path && ( - (e.currentTarget.style.display = 'none')} - /> - )} - {entity.valor} -
    + + {entity.valor} +
    -
    + + #{entity.count} menciones + + +
    {tipoInfo.label} -
    - - {entity.count} - - -
    -
    - - -
    -
    - {TIMELINE_WINDOWS.map((win) => ( - - ))} -
    + {entity.count} +
    + + {isExpanded && ( +
    +
    + +
    -
    +
    + {TIMELINE_WINDOWS.map((win) => ( + + ))} +
    +
    + )} +
    + ) + })} + )} diff --git a/frontend/src/pages/Search.tsx b/frontend/src/pages/Search.tsx index 5f739b2..f970964 100644 --- a/frontend/src/pages/Search.tsx +++ b/frontend/src/pages/Search.tsx @@ -3,6 +3,7 @@ import { useSearchParams, Link } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { apiService, api, News, Category, Country } from '../services/api' import { Search as SearchIcon, Filter } from 'lucide-react' +import { SkeletonNewsList, NoResultsFound, NoSearchResults } from '../components/ui' export function Search() { const [searchParams, setSearchParams] = useSearchParams() @@ -132,13 +133,14 @@ export function Search() {

    {data.total} resultados encontrados

    )} - {isLoading && ( -
    -
    -
    - )} - - {data?.news && data.news.length > 0 && ( + {isLoading ? ( + + ) : data?.news?.length === 0 ? ( + + ) : data?.news && data.news.length > 0 ? (
    {data.news.map((news: News) => ( @@ -151,14 +153,8 @@ export function Search() { ))}
    - )} - - {(!q && !lang && !categoria && !pais) && ( -

    Introduce una búsqueda o selecciona filtros

    - )} - - {q && !isLoading && data?.news?.length === 0 && ( -

    No se encontraron resultados

    + ) : ( + )} ) diff --git a/plan-app.md b/plan-app.md new file mode 100644 index 0000000..20c6ca8 --- /dev/null +++ b/plan-app.md @@ -0,0 +1,1154 @@ +# Plan de Migración de RSS2 a Aplicación Multiplataforma + +**Versión:** 1.0 +**Fecha:** 2026-08-14 +**Estado:** Propuesta de arquitectura y plan de migración + +--- + +## Tabla de Contenidos + +1. [Resumen Ejecutivo](#1-resumen-ejecutivo) +2. [Análisis Profundo del Código Actual](#2-análisis-profundo-del-código-actual) +3. [Objetivos y Requisitos de la Nueva Aplicación](#3-objetivos-y-requisitos-de-la-nueva-aplicación) +4. [Arquitectura Objetivo (Alternativas)](#4-arquitectura-objetivo-alternativas) +5. [Selección Tecnológica por Plataforma](#5-selección-tecnológica-por-plataforma) +6. [Estrategia de Datos y Sincronización](#6-estrategia-de-datos-y-sincronización) +7. [Estrategia de IA/ML en el Dispositivo](#7-estrategia-de-iaml-en-el-dispositivo) +8. [Núcleo Embebido (Embedded Backend)](#8-núcleo-embebido-embedded-backend) +9. [Rediseño de la API para Clientes Nativos](#9-rediseño-de-la-api-para-clientes-nativos) +10. [Migración del Frontend Paso a Paso](#10-migración-del-frontend-paso-a-paso) +11. [Empaquetado y Distribución por Plataforma](#11-empaquetado-y-distribución-por-plataforma) +12. [Infraestructura de Build y CI/CD](#12-infraestructura-de-build-y-cicd) +13. [Plan de Implementación por Fases](#13-plan-de-implementación-por-fases) +14. [Riesgos y Mitigaciones](#14-riesgos-y-mitigaciones) +15. [Estrategia de Testing y QA](#15-estrategia-de-testing-y-qa) +16. [Referencias y Documentación](#16-referencias-y-documentación) + +--- + +## 1. Resumen Ejecutivo + +RSS2 es actualmente una **aplicación web monolítica orquestada con Docker Compose** compuesta por más de 20 servicios interconectados: un frontend React SPA, un backend Go (API REST Gin + 6 workers de ingesta/enriquecimiento), 11 workers Python (traducción neuronal, embeddings, NER, clustering, detección de idioma, categorización LLM) y tres sistemas de almacenamiento (PostgreSQL, Redis, Qdrant). + +El objetivo de este plan es transformar este sistema en una **aplicación nativa instalable** en **Windows, Linux, Android e iOS**. Dada la enorme complejidad del stack actual (modelos ML de 600MB–1.3B, vector DB, colas, 20 servicios), la estrategia recomendada es una **arquitectura cliente–servidor con capacidades offline-first**: + +- **El "cerebro" del sistema** (ingesta, IA, almacenamiento) sigue corriendo como un servidor (Docker o binario nativo en un equipo potente). +- **Las apps nativas** (desktop y móviles) son clientes ricos con caché local SQLite, modo offline, notificaciones push y, en escritorios con GPU, capacidad de actuar como **workers remotos de traducción** (reutilizando la infraestructura WebSocket ya existente). + +La **clave del éxito** es el altísimo grado de reutilización del frontend React actual: la misma base de código puede empaquetarse con **Tauri** (Windows/Linux) y con **Capacitor** (Android/iOS), requiriendo únicamente abstracciones sobre las APIs del navegador. + +--- + +## 2. Análisis Profundo del Código Actual + +### 2.1 Arquitectura General + +``` + ┌────────────────────────────────────────────┐ + │ CLIENTE WEB (Navegador) │ + │ React 18 + Vite + Tailwind + React Query │ + └───────────────────┬────────────────────────┘ + │ HTTP/JSON /api + JWT + ▼ + ┌────────────────────────────────────────────┐ + │ nginx (puerto 8888) │ + │ Gateway + proxy inverso + estáticos SPA │ + └───────┬─────────────────────────┬──────────┘ + │ /api/ │ / (estáticos) + ▼ ▼ + ┌────────────────────┐ ┌────────────────────┐ + │ backend-go │ │ rss2_frontend │ + │ Gin REST API :8080│ │ React compilado │ + └───────┬──────┬─────┘ └────────────────────┘ + ┌─────────────────┘ └───────────────┬────────────────────┐ + ▼ ▼ ▼ + PostgreSQL 18 Redis 7 Qdrant + (esquema relacional) (caché/colas) (vector DB) + ▲ + │ Red interna de Docker Compose (backend network) + ┌──────────┴─────────────────────────────────────────────────────────────┐ + │ Workers Go Workers Python │ + │ ──────────── ────────────── │ + │ rss-ingestor-go langdetect_worker.py │ + │ scraper (deep scraping) ctranslator_worker.py (NLLB-200) │ + │ discovery (descubrir feeds) embeddings_worker.py (MiniLM) │ + │ wiki_worker (Wikipedia) ner_worker.py (spaCy es_core_news_lg) │ + │ topics (países/temas) cluster_worker.py │ + │ related (similitud coseno) translation_scheduler.py │ + │ qdrant (vectorización) llm_categorizer_worker.py (Ollama) │ + │ remote_translator_worker.py (WS GPU) │ + └────────────────────────────────────────────────────────────────────────┘ +``` + +**Topología de redes (docker-compose.yml):** +- Red `frontend`: nginx, rss2_frontend, backend-go (puente entre redes). +- Red `backend`: db, redis, ingestor, langdetect, scraper, discovery, wiki-worker, translator, translator-gpu, scheduler, embeddings, topics, related, qdrant, qdrant-worker, ner, cluster. +- Único puerto expuesto al exterior: **nginx :8888**. + +### 2.2 Inventario de Servicios (docker-compose.yml, 20 servicios) + +| Servicio | Imagen/Dockerfile | Rol | Recursos | +|---|---|---|---| +| `db` | postgres:18-alpine | Base de datos relacional | 2 CPU / 8G | +| `redis` | redis:7-alpine | Caché + colas (workers Python) | 768M | +| `rss-ingestor-go` | rss-ingestor-go/Dockerfile | Crawler RSS (100 workers) | 2 CPU / 2G | +| `langdetect` | Dockerfile | Detección de idioma | 0.5 CPU / 512M | +| `scraper` | Dockerfile.scraper | Extracción profunda de artículos | 1 CPU / 512M | +| `discovery` | Dockerfile.discovery | Descubrimiento de feeds | 1 CPU / 512M | +| `wiki-worker` | Dockerfile.wiki | Enriquecimiento Wikipedia | 0.5 CPU / 256M | +| `backend-go` | backend/Dockerfile | API REST principal | 2 CPU / 512M | +| `rss2_frontend` | frontend/Dockerfile | SPA React | 1 CPU / 512M | +| `nginx` | nginx:alpine | Gateway | — | +| `translator` | Dockerfile.translator | Traducción NLLB CPU | 4 CPU / 6G | +| `translator-gpu` | Dockerfile.translator | 2º worker CPU (nombre heredado) | 4 CPU / 6G | +| `translation-scheduler` | Dockerfile.scheduler | Crea trabajos de traducción | 0.5 CPU / 256M | +| `embeddings` | Dockerfile | Vectores MiniLM | 2 CPU / 6G | +| `topics` | Dockerfile.topics | Matcher países/temas | 1 CPU / 512M | +| `related` | Dockerfile.related | Noticias relacionadas | 1 CPU / 1G | +| `qdrant` | qdrant/qdrant:latest | Base vectorial | 4 CPU / 4G | +| `qdrant-worker` | Dockerfile.qdrant | Vectorización → Qdrant | 1 CPU / 1G | +| `ner` | Dockerfile | NER spaCy | 2 CPU / 2G | +| `cluster` | Dockerfile | Clustering de noticias | 2 CPU / 2G | + +> **Observación crítica para la migración:** el servicio `translator-gpu` NO usa CUDA; es un segundo worker CPU. La única capacidad GPU real es vía **workers remotos WebSocket** (`remote_translator_worker.py`). Esto es una ventaja enorme: la infraestructura para que un equipo externo (potencialmente una app de escritorio) traduzca vía WebSocket **ya existe y está probada**. + +### 2.3 Frontend (React + TypeScript + Vite) + +**Ubicación:** `frontend/src` — 26 archivos, ~4.300 líneas TS/TSX. + +**Stack:** React 18.2, TypeScript 5.3 (strict), Vite 5, Tailwind CSS 3.4, React Router 6, TanStack React Query 5, Axios, lucide-react (iconos), date-fns. + +**Estructura:** +- `main.tsx` → `QueryClientProvider` → `BrowserRouter` → `App`. +- `App.tsx`: llama a `GET /auth/check-first-user`; si es el primer usuario muestra `WelcomeWizard` (onboarding de 3 pasos), si no, el árbol de rutas envuelto en `Layout`. +- 18 páginas: `Home`, `News`, `Search`, `Feeds`, `Stats`, `Favorites`, `Account`, `Login`, `Populares` (732 líneas, la más compleja), `Analisis`, `Alertas`, `WelcomeWizard`, `AdminAliases`, `AdminUsers`, `AdminSettings`, `AdminWorkers`. +- 4 componentes reutilizables: `Layout`, `ProtectedRoute`, `LineChart` (SVG manual), `WikiTooltip`. +- `services/api.ts` (279 líneas): instancia Axios, interceptor de petición (inyecta JWT de `localStorage`), interceptor de respuesta (401 → redirige a `/login?expired=1`), tipos TS e `apiService` (22 métodos tipados) + exportación del cliente crudo `api`. + +**Dependencias de navegador que bloquean una migración nativa directa:** + +| API web actual | Uso | Sustitución nativa | +|---|---|---| +| `localStorage` (`token`, `user`, `favorites`) | Almacenar sesión y favoritos | SecureStorage/Keychain/Keyring (Electron/Tauri/Capacitor) + SQLite | +| `window.location.href` (redirect 401) | Forzar logout | Callback de sesión expirada → navegación interna | +| `window.confirm/prompt/alert` | Confirmar borrados, doble confirmación ("type SI"), etc. | Diálogos nativos (capas JS→Rust/ObjC/Swift/Kotlin) o modales React | +| `URL.createObjectURL` + `` | Descarga de CSV/ZIP/SQL | API de archivos nativa (`saveDialog` en Tauri/Electron, `Share`/filesystem en Capacitor) | +| `window.addEventListener('storage')` | Sync de auth entre pestañas | Eventos de sesión dentro de la misma app | +| `window.history` (BrowserRouter) | Navegación | `HashRouter`/`MemoryRouter` o navegación nativa | +| `window.URLSearchParams` | Parámetros de ruta | React Router compatible | +| `navigator`/Viewport | Responsividad | Adaptación a pantallas táctiles | + +**Deudas técnicas detectadas en el frontend (deben corregirse durante la migración):** +1. **Nav móvil rota:** `hidden sm:flex` sin hamburguesa → inutilizable en móvil (< 640px). +2. `WikiTooltip` solo responde a hover → sin soporte táctil. +3. Página `Favorites` usa solo `localStorage` (no sincronizada con backend, pese a existir la tabla `favoritos`). +4. `Account` no persiste cambios (guardado falso). +5. Botón `btn-danger` usado pero **no definido** en `index.css`. +6. Dos patrones de fetch inconsistentes: React Query vs `useEffect`+fetch manual (Populares, Alertas, Analisis, Admin*). +7. Dos clientes de API: `api` crudo vs `apiService` tipado. +8. Filtros de categoría/país en `Search` son "muertos" (no se envían al backend). +9. `clsx` y `tailwind-merge` instalados pero sin uso. +10. **No existe ni un solo test** pese a vitest configurado. +11. Sin code-splitting (`React.lazy` ausente); todo el bundle en un solo archivo. +12. `JWT_EXPIRATION` del backend ignorado; el token expira en 24h fijas sin refresh — **crítico para apps móviles**. + +### 2.4 Backend Go (API + Workers) + +**Módulo:** `github.com/rss2/backend`, Go 1.23, `gin v1.9.1`, `pgx/v5`, `go-redis/v9`, `golang-jwt/v5`, `gorilla/websocket`, `gofeed`, `goquery`. + +**Paquetes internos:** +- `internal/config` — `Load()` lee variables de entorno (sin archivos `.env`; todo lo inyecta Docker). +- `internal/db` — `pgxpool` (25 conns max), solo para la API (`DATABASE_URL`). +- `internal/cache` — Cliente Redis (helpers nunca usados por handlers; solo conexión). +- `internal/auth` — JWT HS256, expiración 24h **hardcodeada**. +- `internal/middleware` — `AuthRequired`, `AdminRequired`, CORS. `RateLimit` es un stub **no conectado**. +- `internal/models` — Tipos de dominio. ⚠️ `News.ID` es `int64` pero la DB usa `VARCHAR(32)` (MD5 de la URL). +- `internal/handlers` — auth, news, feed, search, alerts, admin, remote_worker, worker_ws. +- `internal/services/ml.go` — Clientes HTTP hacia Libretranslate/Ollama/spaCy; `SemanticSearch` es **stub que devuelve vacío**. +- `internal/workers/db.go` — Helper de conexión compartido por los 6 workers (usa `DB_*` env vars). + +**Arranque del servidor (`cmd/server/main.go`):** +1. `config.Load()` → 2. `db.Connect()` (fatal si falla) → 3. `initDB()` (migraciones idempotentes: crea `entity_aliases`, `config`, `alertas`, `remote_workers`; añade `role`; seed de `config`) → 4. `cache.Connect()` (no fatal) → 5. `services.Init()` → 6. router Gin → 7. CORS → 8. rutas → 9. `auth.SetJWTSecret()` → 10. `r.Run(":8080")` → 11. goroutines `StartJobAssigner()` (cada 5s) y `StartAlertScanner()` (cada 120 min) → 12. señal SIGINT/SIGTERM para shutdown graceful. + +**Workers Go (binarios independientes, todos con bucle `time.Ticker`):** + +| Worker | Loop | Función | +|---|---|---| +| `scraper` | 60s | Enriquece `noticias` con resumen corto (<200 chars) vía goquery; extrae artículos de `fuentes_url` (OpenGraph + selectores). | +| `discovery` | 900s | Detecta feeds en `fuentes_url`; crea `feeds` o `feeds_pending` (aprobación manual). | +| `wiki_worker` | 30s | Consulta la API REST de Wikipedia ES; descarga thumbnails (≤2MB) a `data/wiki_images`; rellena `wiki_summary`, `wiki_url`, `image_path`. | +| `topics` | 10s | Matchea keywords de `topics` contra noticias sin procesar; asigna `pais_id`. | +| `related` | 10s | Similitud coseno entre embeddings almacenados; rellena `related_noticias`. | +| `qdrant` | 30s | Toma `traducciones` sin vectorizar, parsea el embedding del array Postgres, hace upsert en colección `news_vectors` de Qdrant. | + +**WebSocket `/ws/worker` (`handlers/worker_ws.go`):** +- Protocolo existente y probado para workers remotos GPU. +- Auth por `api_key` (query o header `X-API-Key`) contra tabla `remote_workers`. +- Registro, heartbeat (10s), ping (30s), asignación de jobs con `FOR UPDATE SKIP LOCKED`, límite de 5 min en `assigned`, re-encolado tras desconexión. +- **El endpoint se registra fuera de `/api`** (en `/ws/worker`) y **no está proxied** por el nginx de producción — quirk a documentar. + +### 2.5 Workers Python + +**Dependencias (`requirements.txt`):** Flask, feedparser, APScheduler, psycopg2, transformers 4.43, ctranslate2, sentence-transformers 3.0.1, spaCy 3.7, sklearn, qdrant-client 1.11, redis, langdetect, newspaper3k, etc. + +| Worker | Modelo | Recursos | Comportamiento | +|---|---|---|---| +| `ctranslator_worker.py` | NLLB-200 distilled 600M (CT2, int8) | 4 CPU / 6G | Loop 30s; `FOR UPDATE SKIP LOCKED`; batch 128; cache Redis por título/cuerpo; trocea cuerpos en chunks de 900 chars; `executemany`; registra `translation_stats`. | +| `embeddings_worker.py` | sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 | 2 CPU / 6G | `model.encode(normalize_embeddings=True)`; upsert en `traduccion_embeddings`; sleep 5s idle. | +| `ner_worker.py` | spaCy `es_core_news_lg` | 2 CPU / 2G | Extrae PER/ORG/LOC → `tags` y `tags_noticia`. | +| `cluster_worker.py` | embeddings MiniLM + sklearn | 2 CPU / 2G | Agrupa noticias en `eventos` (umbral 0.35). | +| `langdetect_worker.py` | langdetect | 0.5 CPU | Detecta idioma → `noticias.lang`. | +| `llm_categorizer_worker.py` | Ollama/Mistral-7B | (externa) | Clasificación LLM (opcional). | +| `translation_scheduler.py` | — | 0.5 CPU | Crea filas `traducciones` pendientes. | +| `remote_translator_worker.py` | NLLB-200 (600M/1.3B) | GPU/CPU | Cliente WebSocket → servidor `/ws/worker`; reconnect exponencial; procesa jobs de traducción. **Este es el prototipo perfecto para el módulo "worker" de la app de escritorio.** | + +**Dependencia CTranslate2:** `libctranslate2.so` requiere `patchelf --clear-execstack` en Linux (ver `Dockerfile.remote-worker`), y torch==2.1.0 cu118. Esto condiciona el empaquetado nativo (ver §8 y §11). + +### 2.6 Base de Datos PostgreSQL + +**Esquema** (fuente: `init-db/00-complete-schema.sql` + 37 migraciones numeradas; se aplica al iniciar el contenedor, sin framework de migraciones Go). + +**Tablas principales (28):** +- **Núcleo:** `users`, `config`, `continentes`, `categorias`, `paises`, `feeds`, `fuentes_url`, `noticias`, `traducciones`. +- **Enriquecimiento:** `tags`, `tags_noticia`, `entity_aliases`, `entity_images`, `topics`, `news_topics`, `related_noticias`, `eventos`, `eventos_noticias`. +- **IA:** `traduccion_embeddings` (array `DOUBLE PRECISION[]`), vista `embeddings`. +- **Social:** `favoritos`, `search_history`, `user_search_tags`, `alertas`, `videos`, `video_parrillas`, `remote_workers`, `translation_stats`, `feeds_pending`. + +**Características PostgreSQL-específicas (problemas para un embebido SQLite):** +1. **PK de noticias = `VARCHAR(32)`** = MD5 hex de la URL. +2. Full-text en español: columnas `tsvector` (`tsv`, `search_vector_es`) con triggers (`to_tsvector('spanish', ...)`) + índices GIN. +3. `traduccion_embeddings.embedding` es un **array de precisión doble**. +4. Consultas avanzadas: `FOR UPDATE SKIP LOCKED` (workers), `unnest` (bulk inserts), `generate_series` (mentions), CTEs con ventanas (alerta anomalías), `ON CONFLICT DO NOTHING/UPDATE`. +5. El backend llama a `pg_dump`/`psql` desde `cmd/admin.go` para backup/restore (requiere el binario `postgresql-client` en la imagen). + +**Fuga de memoria / diferencia de tipos:** `News.ID` en Go models es `int64`; los handlers devuelven el ID string real. Los clientes deben tratar los IDs de noticia como **strings opacos**. + +### 2.7 Qdrant (Base Vectorial) + +- Colección única `news_vectors` (distancia coseno, dim derivada del modelo MiniLM = 384). +- Payload: `news_id`, `titulo`, `resumen`, `url`, `fuente`, `lang`, `fecha`, `category`, `pais`. +- El worker `qdrant-worker` hace upserts de puntos con UUIDs; marca `traducciones.vectorized=TRUE` y guarda `qdrant_point_id`. +- ⚠️ La búsqueda semántica vía API (`/api/search?semantic=true`) es un **stub vacío**: no consulta Qdrant. + +### 2.8 Redis + +- 512MB maxmemory, appendonly, requirepass. +- Usado por `ctranslator_worker` como **cache de traducciones** (títulos/cuerpos). +- El backend Go solo establece la conexión; **no usa los helpers de cache**. +- Para la app nativa: Redis es irrelevante (el servidor lo gestiona). + +### 2.9 Modelos ML y Pesos + +| Modelo | Tamaño | Uso | +|---|---|---| +| `nllb-ct2` (600M, int8) | ~600MB (`models/`) | Traducción CPU | +| `nllb-ct2-1.3b` | ~1.3GB | Traducción GPU/calidad | +| `nllb-200-distilled-600M` (tokenizer) | via HF | Tokenizer | +| `paraphrase-multilingual-MiniLM-L12-v2` | ~470MB | Embeddings | +| `es_core_news_lg` (spaCy) | ~500MB | NER | +| Mistral-7B (Ollama) | ~4GB | Categorización LLM (opcional) | +| `hf_cache/` | **7.9GB** | Caché HuggingFace | + +**Conclusión para el dispositivo:** los modelos completos no caben en móvil de forma realista. Ver §7. + +### 2.10 Superficie de API Completa + +Todas las rutas bajo `/api`, auth JWT Bearer (salvo las públicas). + +| Método | Ruta | Auth | Descripción | +|---|---|---|---| +| GET | `/health` | No | Health check | +| GET | `/api/wiki-images/*` | No | Estáticos (imágenes Wikipedia) | +| POST | `/api/auth/login` | No | Login | +| POST | `/api/auth/register` | No | Registro (1er usuario = admin) | +| GET | `/api/auth/check-first-user` | No | ¿Primer usuario? | +| GET | `/api/auth/me` | Sí | Usuario actual | +| GET | `/api/news` | No | Lista paginada con filtros `q, category_id, country_id, translated_only, page, per_page` | +| GET | `/api/news/:id` | No | Detalle + entidades con wiki | +| DELETE | `/api/news/:id` | Sí | Eliminar noticia | +| GET | `/api/feeds` | No | Feeds paginados + categorías/países + conteos | +| GET | `/api/feeds/export` | No | CSV | +| GET | `/api/feeds/:id` | No | Detalle feed | +| POST | `/api/feeds` | Sí | Crear feed | +| POST | `/api/feeds/import` | Sí | Importar CSV | +| PUT | `/api/feeds/:id` | Sí | Actualizar | +| DELETE | `/api/feeds/:id` | Sí | Eliminar | +| POST | `/api/feeds/:id/toggle` | Sí | Activar/desactivar | +| POST | `/api/feeds/:id/reactivate` | Sí | Reset fallos | +| GET | `/api/search` | No | Búsqueda texto (`q, lang`; `semantic=true` = stub) | +| GET | `/api/search/suggestions` | Sí | Términos del usuario | +| POST | `/api/searchlog` | Sí | Registrar búsqueda | +| GET | `/api/entities` | No | Entidades canónicas paginadas | +| GET | `/api/entities/news` | No | Noticias de una entidad | +| GET | `/api/entities/mentions` | No | Serie temporal de menciones | +| GET | `/api/alerts` | No | Alertas de anomalías | +| POST | `/api/alerts/:id/read` | Sí | Marcar leída | +| POST | `/api/alerts/read-all` | Sí | Marcar todas leídas | +| GET | `/api/stats` | No | Estadísticas globales | +| GET | `/api/categories` | No | Categorías | +| GET | `/api/countries` | No | Países | +| POST | `/api/admin/aliases` | Admin | Crear aliases | +| GET | `/api/admin/aliases/export` | Admin | CSV | +| POST | `/api/admin/aliases/import` | Admin | Importar CSV | +| POST | `/api/admin/entities/retype` | Admin | Cambiar tipo entidad | +| GET | `/api/admin/backup` | Admin | pg_dump SQL | +| GET | `/api/admin/backup/news` | Admin | ZIP noticias | +| POST | `/api/admin/restore` | Admin | Restaurar | +| GET | `/api/admin/users` | Admin | Usuarios | +| POST | `/api/admin/users/:id/promote` | Admin | Hacer admin | +| POST | `/api/admin/users/:id/demote` | Admin | Quitar admin | +| POST | `/api/admin/reset-db` | Admin | Vaciar 11 tablas | +| POST | `/api/admin/alerts/scan` | Admin | Ejecutar escáner | +| GET | `/api/admin/workers/status` | Admin | Estado Docker | +| GET | `/api/admin/workers/stats` | Admin | Throughput traducción | +| POST | `/api/admin/workers/config` | Admin | Config traducción | +| POST | `/api/admin/workers/start` | Admin | `docker compose up` | +| POST | `/api/admin/workers/stop` | Admin | `docker compose stop` | +| GET/POST | `/api/admin/workers/remote` | Admin | Listar/crear workers remotos | +| GET/DELETE | `/api/admin/workers/remote/:id` | Admin | Detalle/borrar | +| POST | `/api/admin/workers/remote/:id/toggle` | Admin | Online/offline | +| POST | `/api/admin/workers/remote/:id/regenerate-key` | Admin | Regenerar API key | +| GET | `/ws/worker` | API key | WebSocket workers remotos | + +### 2.11 Configuración (Variables de Entorno) + +Sin archivo `.env` en Go; todo via env vars inyectadas por Docker Compose. Tabla clave: + +| Variable | Default | Uso | +|---|---|---| +| `SERVER_PORT` | 8080 | Puerto API | +| `DATABASE_URL` | postgres://rss:rss@localhost:5432/rss | DSN API | +| `REDIS_URL` | redis://localhost:6379 | DSN cache | +| `SECRET_KEY` | — | Firma JWT | +| `JWT_EXPIRATION` | 24h | **Leído pero ignorado** | +| `TRANSLATION_URL` | http://libretranslate:7790 | Servicio traducción (sin uso real) | +| `OLLAMA_URL` | http://ollama:11434 | Embeddings/categorización | +| `SPACY_URL` | http://spacy:8000 | NER | +| `DOCKER_COMPOSE_DIR` | /datos/rss2 | Directorio para `docker compose` de admin | +| `WIKI_IMAGES_PATH` | /app/data/wiki_images | Imágenes wiki | +| `ALLOWED_ORIGINS` | * | CORS | + +Los 6 workers usan `DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASS` + variables propias (`SCRAPER_*`, `WIKI_*`, `TOPICS_*`, `RELATED_*`, `QDRANT_*`, `ALERTS_*`). + +### 2.12 Limitaciones Relevantes para la Migración + +1. **`SemanticSearch` no implementado** en la API Go (stub vacío). +2. **`JWT_EXPIRATION` ignorado** — sin refresh tokens; crítico en móvil. +3. **Admin depende de Docker** (`start/stop/status/backup/restore` hacen `exec.Command`). +4. **Sin framework de migraciones DB** (todo en el init del contenedor PostgreSQL). +5. **Esquema fuertemente PostgreSQL-específico** (tsvector, GIN, arrays, `FOR UPDATE SKIP LOCKED`). +6. **Sin tests** en frontend y sin suite CI visible para la mayoría de servicios. +7. **Sin rate-limiting real** (stub no conectado). +8. **Mensajes y UI 100% en español** hardcodeados (falta i18n). +9. **Favoritos sin backend** aunque existe la tabla `favoritos`. + +--- + +## 3. Objetivos y Requisitos de la Nueva Aplicación + +### 3.1 Requisitos Funcionales + +1. **RF-1 Lectura de noticias:** listar, filtrar (categoría, país, solo traducidas), ver detalle, entidades con Wikipedia. +2. **RF-2 Búsqueda:** texto (y semántica si se implementa), sugerencias. +3. **RF-3 Favoritos:** guardar/eliminar noticias (sincronizados con servidor). +4. **RF-4 Gestión de feeds:** CRUD, activar/desactivar, reactivar, importar/exportar CSV. +5. **RF-5 Análisis:** entidades populares, evolución temporal de menciones, alertas de anomalías. +6. **RF-6 Estadísticas globales.** +7. **RF-7 Autenticación:** login/registro, sesión persistente, logout. +8. **RF-8 Administración** (solo admin): usuarios, aliases, workers, backup/restore, reset. +9. **RF-9 Notificaciones push** (móvil) y del sistema (desktop) para nuevas alertas. +10. **RF-10 Modo offline:** lectura de noticias cacheadas sin conexión. +11. **RF-11 (Avanzado) Modo worker:** el desktop puede actuar como **worker de traducción remota** (WebSocket GPU/CPU) conectado a un servidor central. +12. **RF-12 (Avanzado) Núcleo local:** el desktop puede ejecutar el **backend completo embebido** (servidor personal local) con la API en `localhost`. + +### 3.2 Requisitos No Funcionales + +| # | Requisito | +|---|---| +| RNF-1 | Instalación nativa en Windows 10/11 x64, Linux x64 (deb/AppImage/Flatpak), Android 8+, iOS 15+. | +| RNF-2 | Tamaño de descarga: ≤80MB (sin modelos embebidos). | +| RNF-3 | Arranque < 3s; UI fluida (60fps) en dispositivos medios. | +| RNF-4 | Cifrado de credenciales (Keychain/Keyring/Keystore). | +| RNF-5 | Actualizaciones automáticas (auto-update). | +| RNF-6 | Soporte offline-first con reconciliación al reconectar. | +| RNF-7 | Cumplir políticas de tiendas (Google Play, App Store) — sin descargas de modelos gigantes en primer arranque móvil. | +| RNF-8 | Seguridad: JWT con refresh, almacenamiento seguro, `https` obligatorio en producción. | +| RNF-9 | i18n (al menos ES + EN) — aprovechar la migración para introducir `react-i18next`. | + +--- + +## 4. Arquitectura Objetivo (Alternativas) + +### 4.1 Opción A — Cliente–Servidor con Offline-First (RECOMENDADA) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ SERVIDOR RSS2 (central o personal) │ +│ Docker Compose / binario nativo → ingesta, IA, DB, Qdrant, Redis │ +│ nginx :8888 → API REST /api + WebSocket /ws/worker │ +└───────────────┬─────────────────────────────┬──────────────────────────┘ + │ HTTPS (REST + JWT refresh) │ WebSocket (trabajos) + ▼ ▼ +┌───────────────────────────────┐ ┌────────────────────────────────────┐ +│ Apps nativas (clientes) │ │ Desktop con GPU (modo worker) │ +│ Windows / Linux / Android/iOS│ │ NLLB-200 local via WebSocket │ +│ │ │ (reutiliza remote_translator) │ +│ SQLite local (caché offline) │ └────────────────────────────────────┘ +└───────────────────────────────┘ +``` + +**Ventajas:** reutilización total del backend actual (sin reescribir nada de Go/Python); las apps son "clientes ricos"; el modo worker desktop aprovecha la infraestructura WebSocket existente. +**Desventajas:** requiere conexión para ingesta en tiempo real; el modo offline es de lectura/caché. + +### 4.2 Opción B — Núcleo Embebido Total (server local en el dispositivo) + +Cada desktop ejecuta **todo el stack embebido**: backend Go + PostgreSQL embebido + Qdrant + modelos ML. Móviles se conectan al desktop o usan el servidor central. + +**Ventajas:** privacidad total, sin servidor externo. +**Desventajas:** enorme complejidad de empaquetado (Postgres/Qdrant/modelos por plataforma), requisitos de RAM/almacenamiento prohibitivos en móvil, y duplicación de todo el coste de operación. **Descartado como objetivo principal**, solo viable como **modo avanzado desktop** en Fase 5. + +### 4.3 Opción C — Híbrida (recomendada como hoja de ruta de 3 etapas) + +1. **Etapa 1 (MVP):** Opción A pura — apps nativas como clientes del servidor central. +2. **Etapa 2 (Offline):** Caché SQLite local + sincronización bidireccional + push. +3. **Etapa 3 (Pro):** Modo "worker" desktop (GPU) + modo "núcleo local" desktop (backend embebido) para usuarios avanzados y despliegues domésticos. + +**Recomendación:** **Opción C**. Es la que equilibra esfuerzo, valor y viabilidad técnica. El resto del plan se basa en ella. + +--- + +## 5. Selección Tecnológica por Plataforma + +### 5.1 Estrategia de Reuso del Frontend + +El 95% de la UI actual es React + Tailwind. Se recomienda **una sola base de código React** empaquetada con: + +| Plataforma | Tecnología | Razonamiento | +|---|---|---| +| **Windows + Linux** | **Tauri 2** (Rust shell + WebView2/WebKitGTK) | Binarios pequeños (~8-15MB), bajo consumo, API nativa para archivos/notificaciones/servicios, ideal para empaquetar el backend Go como sidecar. | +| **Android + iOS** | **Capacitor 6** (wrapper nativo de la misma SPA React) | Reutiliza la SPA tal cual; acceso a Push, SecureStorage, Filesystem, SplashScreen, autoupdate (capgo). | + +**Alternativa evaluada y rechazada:** +- **Electron:** gran ecosistema y autoupdate maduro, pero binarios de 80-120MB y alto consumo RAM; innecesario con WebView del SO moderno. (Puede elegirse si el equipo prefiere JS puro para el shell — coste en tamaño/RAM.) +- **Flutter / React Native:** supondría **reescribir** las ~4.300 líneas TSX. Solo tiene sentido si se quiere UI 100% nativa y se descarta el reuso. **No recomendado** dado el frontend existente. +- **.NET MAUI / Kotlin Multiplatform:** mismo argumento, coste de reescritura alto. + +**Arquitectura de paquetes (monorepo):** + +``` +rss2/ +├── app/ # NUEVO: aplicación multiplataforma +│ ├── packages/ +│ │ ├── core-ui/ # Componentes React reutilizables (portados) +│ │ ├── api-client/ # Cliente HTTP + tipos (portados de api.ts) +│ │ ├── storage/ # Abstracción de almacenamiento seguro +│ │ ├── sync/ # Motor de sincronización offline +│ │ └── platform/ # Bridge nativo (Tauri/Capacitor) +│ ├── src/ # SPA React compartida +│ ├── desktop/ # Shell Tauri (Rust) +│ │ └── src-tauri/ +│ ├── mobile/ # Configuración Capacitor (Android/iOS) +│ └── worker/ # Módulo "worker de traducción" embebido +│ └── (port de remote_translator_worker.py a Rust/embedded) +``` + +### 5.2 Desktop (Windows/Linux) — Tauri 2 + +- **Rust shell** con `tauri-plugin-shell`, `tauri-plugin-store`, `tauri-plugin-notification`, `tauri-plugin-autostart`, `tauri-plugin-updater` (v2), `tauri-plugin-sql` (SQLite vía rusqlite), `tauri-plugin-fs`. +- **Comunicación JS↔Rust:** comandos `invoke` (ej. `save_dialog`, `read_file`, `notification_permission`). +- **Sidecar backend Go:** compilado con `go build -ldflags "-s -w"` y registrado como **sidecar binario** de Tauri (`externalBin`). Arrancado/bajo control de Rust (`tauri-plugin-shell`), con auto-restart y registro de logs. +- **Ventana:** aprovecha WebView2 (Windows 10/11 preinstalado) y WebKitGTK (Linux). Tamaño mínimo 1024×700, redimensionable. +- **Bandeja del sistema** (tray) para background-ingesta (opcional en Fase 5). +- **Notificaciones nativas** del sistema. + +### 5.3 Android/iOS — Capacitor 6 + +- **Mismo `dist/` de Vite** sirve para web y para Capacitor (`npx cap add android/ios`). +- **Plugins requeridos:** + - `@capacitor/preferences` (o `@capacitor/secure-storage-plugin`) → sustituye `localStorage` para token. + - `@capacitor/push-notifications` → notificaciones push (FCM/APNs). + - `@capacitor/filesystem` → exportación de CSV/ZIP/SQL a la carpeta de descargas / Share. + - `@capacitor/share` → compartir noticias. + - `@capacitor/network` → detección de conectividad para el modo offline. + - `@capacitor/status-bar`, `@capacitor/splash-screen`. +- **Requiere `HashRouter`** en móvil (sin `window.history` en file://). +- **Actualizaciones:** [Capgo](https://capgo.app) o re-publicación en tiendas para updates OTA; revisión obligatoria para iOS. + +### 5.4 Servidor (cerebro) + +Dos opciones de despliegue del backend que la app debe soportar: +1. **Servidor central (SaaS/self-hosted):** el `docker-compose.yml` actual sin cambios; la app se conecta a una URL HTTPS. +2. **Núcleo embebido desktop (Fase 5):** el desktop ejecuta un **binario único** que integra la API Go + los workers esenciales (ingesta, wiki, topics, langdetect) sobre **PostgreSQL embebido portable** (ver §8), sin Docker. + +### 5.5 Base de Datos Local — SQLite SOLO como caché de cliente (nunca como almacén principal) + +**Decisión arquitectónica clave (escala: millones de noticias):** el sistema de registro (system of record) es **PostgreSQL + Qdrant en el servidor**, igual que hoy. **SQLite se usa ÚNICAMENTE como caché acotada y estado de usuario en el dispositivo** — nunca debe contener el dataset completo ni servir como almacén principal. + +Razones técnicas (derivadas del código actual): + +| Limitación de SQLite | Impacto en RSS2 a escala de millones | +|---|---| +| **Escritura serializada (single-writer)** | El `rss-ingestor-go` corre hasta 100 workers concurrentes y los workers Python usan `FOR UPDATE SKIP LOCKED`. SQLite encolaría todas las escrituras → cuello de botella de ingesta. | +| **Sin `FOR UPDATE SKIP LOCKED` ni `unnest`** | Los workers de traducción/topics dependen de estos patrones; reimplementarlos sobre SQLite degrada el rendimiento y añade bugs. | +| **FTS5 vs tsvector/GIN** | El esquema actual usa `to_tsvector('spanish', ...)` con triggers e índices GIN. FTS5 funciona, pero con millones de filas y escrituras concurrentes el mantenimiento del índice es más débil que Postgres. | +| **Embeddings y vectores** | `traduccion_embeddings` guarda arrays `DOUBLE PRECISION[]` (384 dims ≈ 3KB+/fila). 1 noticia → N traducciones → M embeddings: millones de noticias ⇒ decenas de millones de filas. La búsqueda semántica necesita **Qdrant**, no similitud in-memory de SQLite. | +| **Backups/restore** | El backend ya usa `pg_dump`/`psql`; con SQLite habría que reimplementar y perderías la madurez de esas herramientas. | + +**Dónde SÍ es válido SQLite (roles de cliente):** +- Caché de lectura con **evicción forzada** (ver §6.1: presupuesto máx. ~50k noticias y retención de 7–30 días, LRU). +- Estado de usuario: favoritos, leídos, `outbox` de escrituras pendientes, metadatos de feeds. +- Esquema local (ver §6.2) **no replica el esquema PostgreSQL**: es un esquema de aplicación mínimo para lectura rápida. + +**Alternativa evaluada:** libSQL/Turso (replicación distribuida) — solo si más adelante se quiere multi-dispositivo con el cliente como fuente. **No recomendado ahora**; el servidor PostgreSQL sigue siendo la fuente de verdad. + +### 5.6 Resumen de Decisión + +| Componente | Tecnología | Reuso del código actual | +|---|---|---| +| UI | React 18 + Tailwind (misma SPA) | 95% reutilizado | +| Desktop shell | Tauri 2 (Rust) | — | +| Móvil shell | Capacitor 6 | — | +| Cliente HTTP | Axios (portado a `api-client`) | 100% | +| Almacenamiento seguro | Keyring/Keychain/Keystore | nuevo | +| DB local | SQLite | nuevo | +| Motor de traducción (worker) | CTranslate2 (embedded, port a Rust opcional) | reutiliza lógica de `remote_translator_worker.py` | +| Servidor | Docker Compose actual / binario nativo | 100% | + +--- + +## 6. Estrategia de Datos y Sincronización + +### 6.1 Modelo Offline-First + +**Principios:** +1. **Lectura:** todas las listas de noticias/entidades/feeds se cachean en SQLite local **con presupuesto y evicción forzada**. SQLite es caché, no almacén: la app NUNCA guarda el dataset completo. +2. **Presupuesto de caché (regla dura):** máx. **50.000 noticias** en `news_cache` y retención por TTL (5 min listas, 24h detalle, 30 días fondo). Al superar el tope se aplica **LRU** (borrar por `fetched_at` más antiguo) y se recorta por antigüedad (`fecha < now - 30d`). El servidor conserva el histórico completo; la app solo su ventana de trabajo. +3. **Escritura (favoritos/leídos):** se aplican localmente al instante y se encolan en una tabla `outbox` para sincronizar. El `outbox` se vacía tras confirmación del servidor y no tiene límite práctico (son pocas filas por usuario). +4. **Sincronización:** un motor de sync (background worker) reconcilia `outbox` contra el servidor cuando hay red; conflictos resueltos por "última escritura gana" con timestamp. +5. **Rehidratación:** al reconectar, se invalida la caché y se refresca desde el servidor. + +### 6.2 Esquema SQLite Local (caché + estado, con evicción) + +```sql +-- Caché de lectura (SIEMPRE acotada: máx. ~50k filas, LRU + TTL) +CREATE TABLE news_cache ( + id TEXT PRIMARY KEY, -- MD5 de la URL (string opaco) + payload TEXT NOT NULL, -- JSON completo de la noticia + fetched_at INTEGER NOT NULL, -- epoch ms (usado por el LRU) + ttl INTEGER NOT NULL DEFAULT 86400000 +); +CREATE TABLE list_cache ( + key TEXT PRIMARY KEY, -- "news:page=1:cat=2" etc. + payload TEXT NOT NULL, + fetched_at INTEGER NOT NULL, + ttl INTEGER NOT NULL DEFAULT 300000 +); +CREATE INDEX idx_news_fetched ON news_cache(fetched_at); +-- Trabajo de evicción (job diario): +-- DELETE FROM news_cache WHERE fetched_at < now - 30d; +-- DELETE FROM news_cache WHERE id IN ( +-- SELECT id FROM news_cache ORDER BY fetched_at ASC +-- LIMIT (SELECT MAX(0, COUNT(*) - 50000) FROM news_cache)); +-- DELETE FROM list_cache WHERE fetched_at < now - ttl; + +-- Estado de usuario (sync bidireccional) +CREATE TABLE favorites ( + noticia_id TEXT PRIMARY KEY, + created_at INTEGER NOT NULL, + dirty INTEGER NOT NULL DEFAULT 1 +); +CREATE TABLE read_states ( + noticia_id TEXT PRIMARY KEY, + read_at INTEGER NOT NULL, + dirty INTEGER NOT NULL DEFAULT 1 +); + +-- Cola de escrituras pendientes de sincronizar (pocas filas; se vacía al sync) +CREATE TABLE outbox ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + op TEXT NOT NULL, -- 'add_favorite' | 'remove_favorite' | 'read' + payload TEXT NOT NULL, + created_at INTEGER NOT NULL, + synced INTEGER NOT NULL DEFAULT 0 +); + +-- Metadatos de feeds (para gestión offline) +CREATE TABLE feeds_meta ( + id INTEGER PRIMARY KEY, + nombre TEXT, url TEXT, activo INTEGER, + last_error TEXT, updated_at INTEGER +); + +-- Config local +CREATE TABLE app_settings (key TEXT PRIMARY KEY, value TEXT); +``` + +> **Nota de escala:** este esquema NO es una réplica del esquema PostgreSQL (que alberga los millones de noticias, traducciones, embeddings y relaciones). Es una capa de lectura/escritura mínima cuyo tamaño total se mantiene **< 100MB** gracias a la evicción. + +### 6.3 Sincronización Bidireccional + +**Flujo del motor de sync:** +1. Detectar conectividad (`@capacitor/network` / evento de red en Tauri). +2. Enviar `outbox` pendiente en lotes (con el JWT). +3. Aplicar confirmaciones (borrar filas sincronizadas). +4. Pull incremental: `GET /api/news?since=` (requiere añadir filtro `since` al backend, ver §9) → upsert en `news_cache`. +5. Refrescar `favorites` del servidor (una vez `POST /api/favorites` esté implementado, ver §6.5). + +### 6.4 Imágenes y Multimedia + +- Las imágenes de noticias son **URLs remotas**; se cachean en disco (`~/.cache/rss2` en desktop, app dir en móvil) con límite (ej. 200MB LRU). +- Las imágenes wiki se sirven desde `/api/wiki-images/`; la app debe descargarlas y cachearlas igual. +- **Lazy-loading** ya presente en las tarjetas; optimizar con `srcset`/formatos modernos. +- En móvil: preferir miniatura de 400px para ahorrar datos (sugerir añadir parámetro de tamaño al backend). + +### 6.5 Favoritos: de localStorage a sincronizados + +El frontend actual guarda favoritos en `localStorage` (objetos completos). Para offline-first real: +1. **Añadir endpoints al backend:** `GET/POST/DELETE /api/favorites` sobre la tabla `favoritos` existente (no utilizada por la UI actual). +2. La app usa la tabla local `favorites` + `outbox`. +3. Migrar los favoritos existentes de `localStorage` al SQLite local en el primer arranque de la app. + +### 6.6 Backup y Exportación Local + +- Desktop: exportar favoritos/caché a archivo (botón nativo save dialog). +- Sustituir `URL.createObjectURL`+`` por API nativa: + - Tauri: `invoke('save_dialog')` + `fs.write_file`. + - Capacitor: `Filesystem.writeFile` a `DIRECTORY.DOCUMENTS` + `Share.share` con el URI. +- Backup servidor (`/admin/backup`) sigue disponible solo en modo admin. + +--- + +## 7. Estrategia de IA/ML en el Dispositivo + +### 7.1 Realidad de los Modelos + +| Modelo | Tamaño | Desktop (x64) | Android/iOS | +|---|---|---|---| +| NLLB-200 600M (CT2 int8) | ~600MB | ✅ viable (2-4 GB RAM) | ❌ excesivo para la mayoría | +| NLLB-200 1.3B (float16) | ~2.6GB | ✅ con GPU 8GB+ | ❌ | +| MiniLM embeddings | ~470MB | ✅ | ⚠️ borde | +| spaCy es_core_news_lg | ~500MB | ✅ | ❌ | +| Mistral-7B | ~4GB | ⚠️ solo con GPU | ❌ | + +### 7.2 Modelo por Capas (tiered) + +**Capa 0 (obligatoria, sin IA local):** la app **solo consume la API**. Toda la IA corre en el servidor. Es el modo por defecto para móvil. + +**Capa 1 (desktop con GPU — modo worker):** el desktop instala (bajo demanda, descarga de HuggingFace con opción de "descargar ahora/diferir") el modelo NLLB CTranslate2. Se conecta al servidor por WebSocket (`/ws/worker`) como **worker de traducción remota**. Implementación: **portar `remote_translator_worker.py`** a un servicio interno de la app. + +Opciones de implementación del worker desktop: +- **A) Embed Python:** empaquetar el intérprete Python (PyInstaller) con `ctranslate2`+`transformers` como **proceso sidecar** de la app Tauri. Rápido de portar (reutiliza el .py tal cual) pero binarios grandes (300-500MB) y fragilidad de CTranslate2 en empaquetado. +- **B) Port a Rust `ctranslate2-rs`:** binario único pequeño, sin Python. Más trabajo pero mucho más robusto y limpio. **Recomendado a medio plazo.** +- **C) Go + cgo a CTranslate2 C API:** intermedio. + +**Recomendación:** empezar con **(A)** (time-to-market) y migrar a **(B)** como mejora de Fase 5. + +### 7.3 IA On-Device Móvil (opcional, futuro) + +Si se desea búsqueda semántica o traducción offline en móvil: +- **Traducción:** modelos ONNX/ML Kit (NLLB no es práctico; alternativas: `google` ML Kit translate offline ~80 idiomas, pesos menores). +- **Embeddings:** ONNX Runtime Mobile con MiniLM cuantizado (int8, ~40MB). +- **NER:** string matcher local (como el worker `topics` de Go) en lugar de spaCy. + +Estos se descargan **bajo demanda** con consentimiento del usuario (política de tiendas). + +### 7.4 Categorización LLM y Búsqueda Semántica + +- **Categorización LLM** (Ollama/Mistral): permanece en servidor; la app solo muestra resultados. +- **Búsqueda semántica:** el stub de la API deberá implementarse en el servidor consultando Qdrant (Fase 1 de servidor, independiente de la app). La app simplemente usará el endpoint cuando exista. + +--- + +## 8. Núcleo Embebido (Embedded Backend) + +### 8.1 Contexto + +Para la **Fase 5** (modo avanzado desktop y despliegues domésticos), se propone ejecutar el backend sin Docker: un **binario único** que integre API + workers sobre un almacén local. + +**Decisión de escala:** dado que el sistema acumulará millones de noticias, el almacén del núcleo embebido debe ser **PostgreSQL embebido**, NO SQLite. PostgreSQL embebido mantiene paridad total con el servidor (mismo esquema, mismos workers, `FOR UPDATE SKIP LOCKED`, tsvector/GIN, arrays, `pg_dump`). SQLite queda **exclusivamente como caché de cliente** (§6) y solo se ofrece como "modo limitado" para despliegues personales pequeños con techo documentado (§8.5). + +### 8.2 Estrategia de Almacén del Núcleo Embebido + +**Opción A (RECOMENDADA): PostgreSQL embebido.** + +| Aspecto | Detalle | +|---|---| +| Librería | `github.com/fergusstrange/embedded-postgres` (descarga/arranca un binario portable de Postgres) o `zonky`/`otj-pg-embedded`. | +| Compatibilidad | 100% con el esquema `init-db/*.sql` actual; mismo `DATABASE_URL`; los 6 workers Go y los handlers no cambian. | +| Backup/restore | Se reutilizan `pg_dump`/`psql` ya implementados en `internal/handlers/admin.go`. | +| Concurrencia | Soporta los 100 workers del ingestor y `FOR UPDATE SKIP LOCKED` de traducción. | +| Coste de empaquetado | +~50–80MB de binarios Postgres por SO (no es pesado para desktop). | +| Escala | Capaz de albergar millones de noticias sin cambio de diseño. | + +El binario de Postgres se registra como **sidecar** adicional de Tauri (junto al núcleo Go) y Rust lo arranca/supervisa antes de lanzar la API. Datos en el directorio de la app (`%APPDATA%/rss2/pgdata`). Puerto loopback (5432 → uno efímero) y credenciales aleatorias generadas en el primer arranque. + +**Opción B: "Modo limitado" SQLite (solo personal, con techo).** +- Válido únicamente para el perfil "usuario doméstico con feeds personales". +- **Techo duro:** ≤ 200.000 noticias, ≤ 5 feeds activos, sin búsqueda semántica (solo FTS5 de texto), sin clustering/related. +- Debe existir **ruta de migración** al superar el techo: exportar a `pg_dump`/archivo SQL e importar en un servidor PostgreSQL o en el modo A. Documentar en la UI ("Tu catálogo local ha superado el modo limitado"). +- Riesgo asumido: reescritura de consultas Postgres-específicas (`unnest`, `FOR UPDATE SKIP LOCKED`, `generate_series`) para SQLite. + +**Recomendación:** implementar **solo la Opción A** en la Fase 5. Mantener SQLite únicamente como caché de cliente. Si el equipo quiere abaratar el modo personal, la Opción B puede añadirse después como mejora, nunca como base. + +### 8.3 Componentes del Binario Único + +``` +rss2-core (un solo binario Go) +├── API REST (Gin, puerto 127.0.0.1:8080) — rutas /api completas para la app +├── Motor PostgreSQL embebido (sidecar portable, arrancado por Rust) o Postgres externo +├── Módulo de ingesta RSS (port del rss-ingestor-go) — goroutine con ticker +├── Módulo wiki (port de wiki_worker) — goroutine +├── Módulo topics (port de topics) — goroutine +├── Módulo langdetect (Go) — goroutine +├── Módulo NER ligero (string matcher) — goroutine +└── Qdrant embebido (sidecar, opcional) o búsqueda vectorial desactivada +``` + +**Configuración:** archivo `rss2.json` en el directorio de configuración del SO (`%APPDATA%/rss2`, `~/.config/rss2`, `~/Library/Application Support/rss2`). Generado en el primer arranque con secretos aleatorios. + +### 8.4 Gestión de Procesos en la App + +- **Arranque:** Tauri sidecar arranca el binario con `--serve` y espera el healthcheck (`GET /health`). +- **Supervisión:** Rust monitoriza el proceso; auto-restart tras crash (máx. 3 reintentos, backoff exponencial). +- **Shutdown ordenado:** señal SIGTERM → shutdown graceful del servidor Gin. +- **Logs:** `stderr` redirigido a `rss2.log` rotativo. +- **Seguridad:** bind solo a `127.0.0.1`; token de sesión local; CORS restringido a `tauri://localhost` y `https://app.rss2.local`. + +### 8.5 Descarga del Núcleo (Opcional en Desktop) + +El paquete del núcleo (binario Go ≈30–50MB + binarios PostgreSQL portable ≈50–80MB) puede **descargarse bajo demanda** desde el servidor de releases (GitHub Releases) con checksum SHA-256 verificado, e instalarse en el directorio de la app. Si el usuario no descarga el núcleo, la app funciona en modo cliente puro (Capa 0). + +--- + +## 9. Rediseño de la API para Clientes Nativos + +Cambios mínimos necesarios en el backend Go para soportar clientes nativos de forma robusta. + +### 9.1 Autenticación con Refresh Tokens + +El JWT actual expira en 24h fijas y la app móvil no puede pedir re-login cada día. + +1. Añadir tabla `refresh_tokens (id, user_id, token_hash, expires_at, revoked_at, device_info, created_at)`. +2. Nuevos endpoints: + - `POST /api/auth/refresh` (con refresh token → nuevo par). + - `POST /api/auth/logout` (revoca refresh). +3. Usar `JWT_EXPIRATION` config (arreglar el hardcode de `internal/auth/jwt.go`). +4. La app guarda el refresh token en SecureStorage/Keychain y lo renueva de forma transparente. + +### 9.2 Endpoints Nuevos Requeridos + +| Endpoint | Propósito | +|---|---| +| `GET/POST/DELETE /api/favorites` | Sincronización de favoritos (tabla `favoritos` ya existe) | +| `GET /api/news?since=` | Pull incremental para sync offline | +| `GET /api/news/feed` (opcional) | Feed agregado optimizado para móvil (paginado con cursor) | +| `GET /api/push/config` + `POST /api/push/register` | Registrar token FCM/APNs por usuario | +| `POST /api/alerts/:id/read` (ya existe) | — | +| `GET /api/version` | Informa versión de API para compatibilidad de la app | + +### 9.3 Notificaciones Push + +- **Servidor:** nuevo endpoint para registrar token de push; worker Go que consulta `alertas` nuevas y dispara notificaciones. +- **Android:** FCM (`firebase-messaging`); **iOS:** APNs. +- **Desktop:** notificaciones del sistema vía plugin Tauri (sin infraestructura push; eventos en tiempo real por WebSocket/polling ligero). +- **Canales:** "Nuevas alertas", "Nueva traducción disponible" (opcional), "Estado de workers" (opcional, solo admin). + +### 9.4 WebSocket para Tiempo Real + +- Actualmente solo `/ws/worker` (workers). Añadir un **canal `/ws/client`** opcional con auth JWT para: notificación de nuevas noticias, actualización de alertas en vivo, estado de traducción. La app puede degradar a polling cada 60s si el WebSocket no está disponible. + +### 9.5 Versionado de API + +- Montar la API bajo `/api/v1` (manteniendo `/api` como alias por compatibilidad web). +- La app envía `Accept-Version: 1` y `User-Agent: rss2-desktop/1.4.2` para trazabilidad y rate-limiting diferenciado. + +### 9.6 CORS y Seguridad + +- La app nativa no está sujeta a CORS (sin origin de navegador), pero Tauri WebView sí lo está: incluir `tauri://localhost`, `http://localhost:*` y `https://*.rss2.local` en `ALLOWED_ORIGINS`. +- **HTTPS obligatorio** para conexión a servidores remotos (la app rechaza `http://` salvo `localhost`). +- **Certificado pinning** opcional en móvil para servidores propios. + +### 9.7 Rediseño de Funciones Admin Dependientes de Docker + +Los endpoints `/admin/workers/start|stop|status` ejecutan `docker compose`. Para la app: +- **Modo cliente:** mostrar solo estado reportado por el servidor; ocultar controles de contenedores en móvil. +- **Modo núcleo embebido:** los controles mapean a APIs de gestión del proceso interno (no Docker). + +--- + +## 10. Migración del Frontend Paso a Paso + +### 10.1 Fase 0 — Refactor de base (sin cambios visuales) + +1. **Extraer `api-client`:** mover `services/api.ts` a un paquete independiente con inyección de `baseURL`, transporte y almacenamiento. +2. **Crear `storage.ts` (abstracción):** interfaz `SecureStore { get, set, remove }` con implementaciones: `localStorage` (web/dev), `Keyring`/`tauri-plugin-store` (desktop), `Preferences`+`SecureStorage` (móvil). +3. **Sustituir `localStorage`** (token/user/favorites) por la abstracción. +4. **Unificar fetch:** envolver las páginas que usan `api` crudo y `useEffect` para que usen `apiService` + React Query (Populares, Alertas, Analisis, Admin*). +5. **Arreglar deudas menores:** `btn-danger`, filtros muertos de Search, `Account` persistente, limpiar imports muertos (Favorites, clsx), implementar `cn()`. +6. **Añadir i18n:** `react-i18next` con diccionarios ES/EN; marcar todos los strings. +7. **Introducir router portable:** pasar de `BrowserRouter` a un wrapper que use `BrowserRouter` en web y `HashRouter` en móvil (detectable por variable de compilación). + +### 10.2 Fase 1 — Abstracción de APIs de navegador + +| Función | Abstracción (`platform/`) | Desktop (Tauri) | Móvil (Capacitor) | +|---|---|---|---| +| Descarga de archivos | `saveFile(name, bytes)` | `invoke('save_dialog')` + fs | `Filesystem.writeFile` + `Share` | +| Diálogos | `confirmDialog(msg)` | `invoke('confirm')` (Rust nativo) | modales React (no window.confirm) | +| Notificación | `notify(title, body)` | plugin notification | plugin push/local | +| Conectividad | `onNetworkChange(cb)` | eventos Rust | plugin network | +| Fecha/hora | date-fns (ya portable) | — | — | +| Apertura de enlaces | `openExternal(url)` | plugin opener | `Browser.open` (in-app browser) | + +### 10.3 Fase 2 — Adaptaciones móviles + +1. **Nav responsive:** sustituir `hidden sm:flex` por un componente de navegación con hamburguesa / bottom-tab en móvil. +2. **WikiTooltip táctil:** añadir soporte de tap (primer tap muestra, segundo navega) — reemplazar hover-only. +3. **Tablas** (Feeds, Alertas, Admin): en móvil, convertir a listas de tarjetas en vez de `overflow-x-auto`. +4. **LineChart:** `min-w-[560px]` → rescalar a viewBox responsivo (eliminar scroll horizontal). +5. **Safe areas:** padding con `env(safe-area-inset-*)`. +6. **Pull-to-refresh** en listas (react-query + plugin de refresco nativo). +7. **Infinite scroll / paginación** para listas de noticias en lugar de paginación numérica. + +### 10.4 Fase 3 — Offline y Sincronización + +1. Integrar `storage`/SQLite local (`tauri-plugin-sql`, `@capacitor-community/sqlite`). +2. Implementar el motor de sync del §6.3. +3. Convertir las consultas a React Query para servir primero de caché local (`placeholderData`/`initialData` del SQLite) y refrescar del servidor. +4. Estado de conexión: banner "Modo offline" + cola de acciones pendientes. + +### 10.5 Fase 4 — Shells Nativos + +**Tauri (desktop):** +- `npm create tauri-app` + config en `app/desktop/src-tauri`. +- Plugins: shell (sidecar), sql, store, notification, autostart, updater. +- `tauri.conf.json`: `externalBin` → `binaries/rss2-core-*`, ventana 1024×700, capacidades/perms restringidas. +- Build matrix (ver §12). + +**Capacitor (móvil):** +- `npx cap init` + `npx cap add android && npx cap add ios`. +- Plugins listados en §5.3. +- `capacitor.config.ts`: `webDir: dist`, `androidScheme: https`. +- Firma de APK/AAB con keystore; provisioning para iOS. + +### 10.6 Gestión de Sesión en la App + +- Flujo: login → guardar access token (memoria + SecureStorage) y refresh token (SecureStorage). +- Interceptor: 401 → intentar refresh → reenviar petición → si falla, logout. +- Almacenar `user` (JSON) en SecureStorage y sincronizar estado de sesión por eventos internos (en vez de `storage` event del navegador). + +--- + +## 11. Empaquetado y Distribución por Plataforma + +### 11.1 Windows + +- **Formato:** NSIS installer (`.exe`) vía Tauri bundler; MSI opcional (WiX). +- **Firma:** certificado Authenticode (EV recomendado) con `signtool` en CI (Secret: `WINDOWS_CERT_BASE64`, `WINDOWS_CERT_PASSWORD`). +- **SmartScreen:** firmar y/o añadir reputación; la primera versión no firmada mostrará aviso. +- **Actualizaciones:** `tauri-plugin-updater` con endpoint de manifest (GitHub Releases). +- **WebView2:** Runtime instalado por defecto en Win10/11; incluir fallback de instalación silenciosa. +- **Instalación por usuario:** sin privilegios admin (per-user), o instalador system-wide opcional. + +### 11.2 Linux + +- **Formatos:** `.deb` (Debian/Ubuntu), `.rpm` (Fedora), **AppImage** (universal, recomendado para testing), **Flatpak** (distribución a largo plazo), `.tar.gz` portable. +- **WebKitGTK:** dependencia `webkit2gtk-4.1` + `librsvg`; documentar en README del paquete. +- **Firma:** GPG para repos; Flatpak vía Flathub (si se desea). +- **Actualizaciones:** AppImageUpdate (fork de AppImage) o el updater de Tauri con endpoint propio. + +### 11.3 Android + +- **Formato:** **AAB** (App Bundle) para Google Play; APK firmado para distribución directa. +- **Firma:** keystore `app-release.keystore` (guardar en CI como secret; **nunca en git**). +- **minSdk:** 24 (Android 7.0+) recomendado por Capacitor 6 (minSdk 22+). +- **Play Console:** política de "descarga de modelos on-demand" documentada; contenido de noticias = "News" (requiere moderación de contenido si es social). +- **Permisos:** `INTERNET`, `POST_NOTIFICATIONS` (Android 13+), `VIBRATE`. +- **Distribución alternativa:** F-Droid / directa (APK en el sitio). + +### 11.4 iOS + +- **Formato:** `.ipa` vía App Store Connect (requiere Mac + Xcode + cuenta Apple Developer $99/año). +- **Firma:** certificado Apple Distribution + provisioning profile (secrets en CI macOS). +- **App Store:** política de contenido de noticias; pantalla de "primeros pasos"; descarga de modelos bajo demanda. +- **Notarización** de macOS (si en el futuro se soporta macOS) — fuera de alcance inicial (solo Win/Linux desktop). +- **Actualización:** OTA solo vía App Store (Capgo no funciona con el App Store para updates; requeriría MDM/supervisión). Descartar OTA en iOS. + +### 11.5 Política de Actualizaciones + +| Plataforma | Mecanismo | +|---|---| +| Windows | tauri-plugin-updater (manifest JSON en GitHub Releases) | +| Linux | tauri-plugin-updater (AppImage) / repos apt/Flatpak | +| Android | Play Store + directa (APK) + opcional Capgo | +| iOS | App Store (única vía) | + +### 11.6 Tamaños Estimados del Instalador + +| Plataforma | Sin núcleo | Con núcleo embebido | Con worker Python (GPU) | +|---|---|---|---| +| Windows | ~25MB | ~60MB | ~400MB | +| Linux | ~25MB | ~60MB | ~400MB | +| Android | ~15MB | — | — | +| iOS | ~15MB | — | — | + +--- + +## 12. Infraestructura de Build y CI/CD + +### 12.1 Pipeline GitHub Actions (matrix) + +```yaml +name: build-app +on: [push, pull_request, workflow_dispatch] + +jobs: + web: + runs-on: ubuntu-latest + steps: + - checkout + - setup-node, npm ci + - npm run build # tsc + vite build (dist/) + - npm run test # vitest (nuevos tests) + desktop-windows: + needs: web + runs-on: windows-latest + steps: + - npm ci + - cargo build --release (Tauri) + - tauri build --bundles nsis + - firma (signtool) + upload artifact + desktop-linux: + needs: web + runs-on: ubuntu-latest + steps: + - apt: libwebkit2gtk-4.1-dev, libgtk-3-dev, libayatana-appindicator3-dev + - tauri build --bundles deb,appimage + - upload artifact + android: + needs: web + runs-on: ubuntu-latest + steps: + - java 17, Android SDK + - npx cap sync android + - gradlew assembleRelease (AAB + APK) + - upload artifact + ios: + needs: web + runs-on: macos-14 + steps: + - npx cap sync ios + - xcodebuild archive + export + - (opcional) upload to TestFlight via App Store Connect API + backend-core: + runs-on: matrix (windows, ubuntu) + steps: + - go build -o rss2-core-{os}-{arch} ./cmd/rss2-core # nuevo binario + - upload artifact + release: + if: startsWith(github.ref, 'refs/tags/v') + needs: [desktop-windows, desktop-linux, android, backend-core] + steps: + - download artifacts + - create GitHub Release con checksums (sha256sum) + - publish manifest.json del updater +``` + +### 12.2 Versionado + +- **Semver** estricto (`1.4.2`). El `package.json` (app) y el manifiesto del updater comparten versión. +- Tag `v*` dispara la release. `main` dispara builds de desarrollo (artifacts sin firmar). + +### 12.3 Secretos en CI + +| Secret | Uso | +|---|---| +| `WINDOWS_CERT_BASE64` / `WINDOWS_CERT_PASSWORD` | Firma Authenticode | +| `ANDROID_KEYSTORE_BASE64` / `ANDROID_KEYSTORE_PASSWORD` / `ANDROID_KEY_PASSWORD` / `ANDROID_KEY_ALIAS` | Firma AAB/APK | +| `APPLE_DIST_CERT_BASE64` / `APPLE_PROVISIONING_BASE64` | Firma iOS | +| `GH_TOKEN` | Publicar releases + manifest | +| `FCM_SERVER_KEY` / `APNS_KEY` | Push (si se testea desde CI) | + +--- + +## 13. Plan de Implementación por Fases + +### Fase 0 — Preparación y Refactor (2–3 semanas) +**Objetivo:** base de código portable sin cambios de comportamiento. +- [ ] Extraer `api-client` y `storage` como paquetes. +- [ ] Sustituir `localStorage` por `SecureStore`. +- [ ] Unificar fetch (apiService + React Query) en todas las páginas. +- [ ] Arreglar deudas técnicas §2.3 (btn-danger, Search, Account, Favorites, i18n, router portable). +- [ ] Añadir tests base (vitest + Testing Library) para `api-client`, `storage` y componentes clave. +- **Entregable:** `npm run build` + `npm test` verde; la web sigue funcionando igual en Docker. + +### Fase 1 — Backend: Refresh Tokens + API para móvil (2 semanas) +- [ ] Tabla `refresh_tokens` + endpoints `/auth/refresh`, `/auth/logout`. +- [ ] Arreglar `JWT_EXPIRATION` en `internal/auth/jwt.go`. +- [ ] Endpoints `/api/favorites` (GET/POST/DELETE). +- [ ] `GET /api/news?since=` (pull incremental). +- [ ] `/api/version`. +- [ ] Implementar `SemanticSearch` real contra Qdrant (opcional pero recomendado). +- [ ] Tests Go para los nuevos endpoints (patrón existente en `internal/handlers/auth_test.go`). +- **Entregable:** API versionada y consumible por clientes nativos. + +### Fase 2 — Shell Desktop Tauri (Windows/Linux) — MVP (3–4 semanas) +- [ ] Proyecto Tauri + integración del `dist/` de Vite. +- [ ] Plugins: store, notification, opener, updater. +- [ ] Abstracciones `platform/` (saveFile, confirmDialog, notify). +- [ ] Login/registro + sesión persistente con refresh. +- [ ] Pantallas principales: Home, News, Search, Favorites, Feeds (read-only), Stats. +- [ ] Instaladores NSIS (Win) y deb/AppImage (Linux). +- **Entregable:** instalador de escritorio funcional conectado a un servidor central. + +### Fase 3 — Shell Móvil Capacitor (Android/iOS) (4–5 semanas) +- [ ] Proyecto Capacitor + sync de la SPA. +- [ ] Plugins: preferences, secure-storage, filesystem, share, network, push, splash, status-bar. +- [ ] HashRouter + adaptaciones móviles §10.3 (nav, tooltips, tablas, chart). +- [ ] Push notifications (FCM/APNs) + endpoint `/push/register`. +- [ ] Builds Android (AAB) e iOS (ipa) en CI. +- **Entregable:** apps en Android e iOS instalables (TestFlight/Play internal testing). + +### Fase 4 — Offline-First y Sincronización (3–4 semanas) +- [ ] SQLite local (desktop + móvil) con esquema §6.2. +- [ ] Motor de sync (outbox + pull incremental) §6.3. +- [ ] Favoritos sincronizados (backend + app). +- [ ] Caché de imágenes con límite LRU. +- [ ] Modo offline de lectura + banner de estado. +- [ ] Pull-to-refresh e infinite scroll. +- **Entregable:** app funcional offline con reconciliación al reconectar. + +### Fase 5 — Núcleo Embebido y Modo Worker (5–8 semanas) +- [ ] Binario `rss2-core` (Go) + **PostgreSQL embebido portable** (Opción A) con el esquema `init-db/*.sql` intacto. **Descartado SQLite como almacén principal** (escala de millones); SQLite queda solo como caché de cliente. +- [ ] Sidecar Postgres (arranque/supervisión/shutdown por Rust) + sidecar del núcleo. +- [ ] Port de ingesta RSS, wiki, topics, langdetect al núcleo. +- [ ] Supervisión de procesos: auto-restart, logs rotativos, healthcheck. +- [ ] Configuración local `rss2.json` + wizard de "Configurar servidor local". +- [ ] Descarga bajo demanda del núcleo y de Postgres portable (checksum verificado). +- [ ] Modo worker: port de `remote_translator_worker.py` (opción A: PyInstaller sidecar; mejora B: Rust). +- [ ] Gestión de workers remotos en la UI (crear API key, ver estado) — ya existe en AdminWorkers. +- **Entregable:** desktop autónomo (sin Docker, a escala de millones vía Postgres embebido) + aporte de GPU a la red de traducción. + +### Fase 6 — Distribución y Tiendas (2–3 semanas) +- [ ] Firmas de producción (Windows, Android, iOS). +- [ ] Publicación Play Store + App Store + GitHub Releases. +- [ ] Manifiesto de auto-update y roll-out gradual. +- [ ] Dashboard de métricas de uso (opcional: telemetría anonimizada). +- [ ] Documentación de usuario final por plataforma. +- **Entregable:** releases públicas en las 4 plataformas. + +### Cronograma Total Estimado +| Fase | Duración | Acumulado | +|---|---|---| +| 0 | 2–3 sem | 3 sem | +| 1 | 2 sem | 5 sem | +| 2 | 3–4 sem | 9 sem | +| 3 | 4–5 sem | 14 sem | +| 4 | 3–4 sem | 18 sem | +| 5 | 5–8 sem | 26 sem | +| 6 | 2–3 sem | 29 sem | + +**~7 meses** con un equipo de 2-3 personas (1 full-stack React/TS, 1 Go/backend, 0.5 Rust/mobile). El MVP (Fases 0-2) se puede alcanzar en **~2 meses**. + +--- + +## 14. Riesgos y Mitigaciones + +| # | Riesgo | Probabilidad | Impacto | Mitigación | +|---|---|---|---|---| +| 1 | **Empaquetado de CTranslate2/Python en desktop** (dependencias .so, torch cu118) | Alta | Alto | Empezar con PyInstaller sidecar; roadmap hacia port Rust `ctranslate2-rs`; documentar el `patchelf --clear-execstack` existente. | +| 2 | **Políticas de tiendas (iOS/Android)** por descarga de modelos grandes | Media | Alto | Modelos **on-demand** con consentimiento; nunca en el APK; Capa 0 (sin IA local) como default móvil. | +| 3 | **Coste de mantenimiento de 3 shells** (web, Tauri, Capacitor) | Alta | Medio | Mantener **una sola SPA**; los shells son delgados; abstracciones `platform/` estrictas; tests de contrato. | +| 4 | **JWT/sesión en móvil** (24h fija, sin refresh) | Alta | Medio | Fase 1 implementa refresh tokens; SecureStorage para los tokens. | +| 5 | **SQLite ≠ PostgreSQL** en el núcleo embebido | Baja (decisión: usar PostgreSQL embebido) | Bajo | El núcleo usa **PostgreSQL portable** con el esquema actual intacto; SQLite queda solo como caché de cliente acotada. Si se añade "modo limitado" SQLite, llevar techo duro documentado + ruta de exportación a Postgres. | +| 6 | **Favoritos sin backend** (localStorage hoy) | Alta | Baja | Fase 1 añade endpoints; migración de datos local en primer arranque. | +| 7 | **Notificaciones push requieren backend** nuevo (FCM/APNs) | Media | Medio | Worker Go de push; empezar por alertas; degradar a notificaciones locales en desktop. | +| 8 | **Rendimiento del WebView** en dispositivos Android baratos | Media | Medio | Caché SQLite + infinite scroll; reducir tamaño de bundle (code-splitting); limitar tarjetas cargadas. | +| 9 | **Seguridad** (almacenar refresh token, pinning) | Media | Alta | Keychain/Keystore/Keyring; HTTPS obligatorio; opción de pinning; auditoría de secrets. | +| 10 | **Traducción de toda la UI (i18n)** retrasa fases | Media | Baja | Empezar i18n en Fase 0 (ES como default), EN incremental. | +| 11 | **Escasez de expertise Rust/mobile** | Media | Media | Usar Capacitor (JS puro, sin Swift/Kotlin deep); Rust mínimo en Tauri; plantilla de Tauri como base. | +| 12 | **`SemanticSearch` stub** — expectativa de búsqueda semántica en la app | Media | Medio | Implementarla en servidor (Fase 1 opcional) o desactivar el toggle en la app. | + +--- + +## 15. Estrategia de Testing y QA + +### 15.1 Niveles de Testing + +| Nivel | Stack | Alcance | +|---|---|---| +| Unit (TS) | vitest + Testing Library | `api-client`, `storage`, `sync`, componentes, hooks | +| Unit (Go) | `go test` | nuevos endpoints (favorites, refresh, push), helpers | +| Integration API | vitest + msw / Go httptest | contratos `api-client` ↔ backend | +| E2E web | Playwright | flujos principales (login, home, search, favorites) | +| E2E desktop | Playwright con WebView (Tauri driver) | humo por plataforma | +| E2E móvil | Detox / Maestro (emulador) | login, offline, push (móvil) | +| Visual | Percy/regression | 4 plataformas, light/dark | +| Rendimiento | Lighthouse (web) / bundle-size budget | CI gate: bundle < 250KB gzip core | + +### 15.2 Cobertura Mínima Obligatoria + +1. `api-client`: cada endpoint con mock (éxito, 401→refresh→retry, 401→logout, errores de red). +2. `storage`: round-trip, corrupción, rotación de versión. +3. `sync`: outbox vacío, envío, fallo→reintento, conflictos. +4. `favorites`: flujo local→sync→servidor. +5. Componentes clave: `Layout`, `ProtectedRoute`, tarjetas de noticia, `WikiTooltip` táctil. + +### 15.3 Testing Manual por Plataforma (checklist) + +- [ ] **Windows:** instalación NSIS, auto-update, notificaciones, tray, WebView2 ausente→instalar, SmartScreen. +- [ ] **Linux:** AppImage/deb en Ubuntu+Debian+Fedora, webkit2gtk instalado, escalado HiDPI. +- [ ] **Android:** AAB instalado, push FCM, permiso notificaciones, orientación, modo offline/avión, background. +- [ ] **iOS:** TestFlight, push APNs, permiso notificaciones, Safe Area, Dark Mode. + +--- + +## 16. Referencias y Documentación + +- [README.md](./README.md) — Guía de despliegue completo del sistema actual. +- [DEPLOY.md](./DEPLOY.md) — Instrucciones de producción. +- [AGENTS.md](./AGENTS.md) — Guía de desarrollo del repositorio. +- [remote-worker.md](./remote-worker.md) — Protocolo de workers remotos (base para el módulo worker desktop). +- [SECURITY_GUIDE.md](./SECURITY_GUIDE.md) — Guía de seguridad (a revisar para almacenamiento de tokens). +- [QUICKSTART_LLM.md](./QUICKSTART_LLM.md) — Configuración LLM. +- **Ficheros clave del código analizado:** + - `docker-compose.yml` — 20 servicios, topología de redes, recursos. + - `backend/cmd/server/main.go` — arranque, rutas, migraciones idempotentes. + - `backend/internal/handlers/*.go` — toda la superficie de API. + - `backend/internal/auth/jwt.go` — JWT (24h hardcode). + - `backend/internal/services/ml.go` — clientes ML (SemanticSearch stub). + - `frontend/src/services/api.ts` — contrato API + interceptor 401. + - `frontend/src/App.tsx`, `main.tsx` — boot y routing. + - `frontend/src/pages/*.tsx` — las 18 páginas. + - `workers/remote_translator_worker.py` — prototipo del modo worker desktop. + - `workers/ctranslator_worker.py`, `embeddings_worker.py`, `ner_worker.py` — pipelines IA. + - `rss-ingestor-go/main.go` — ingesta con politeness y ETag. + - `init-db/*.sql` — esquema completo (28 tablas). + - `nginx.conf` — gateway. + +--- + +### Apéndice A — Tabla de Correspondencia Frontend↔App + +| Página actual | Estado | Acción en la app | +|---|---|---| +| Home | ✅ | Port directo + infinite scroll | +| News | ✅ | Port + favorito offline | +| Search | ⚠️ | Corregir filtros muertos; semántica condicional | +| Feeds | ✅ | CRUD (solo desktop/admin) | +| Favorites | ⚠️ | Migrar de localStorage a sync | +| Account | ⚠️ | Implementar persistencia real | +| Login | ✅ | Añadir refresh tokens | +| Populares | ✅ | Port | +| Analisis | ✅ | Chart responsivo | +| Alertas | ✅ | + push notifications | +| WelcomeWizard | ✅ | + wizard "configurar servidor local" (Fase 5) | +| AdminAliases/Users/Settings/Workers | ⚠️ | Ocultar/adaptar acciones dependientes de Docker en móvil | + +### Apéndice B — Glosario + +- **Sidecar:** binario adicional empaquetado junto a la app y lanzado por el shell (ej. el backend Go embebido). +- **Offline-first:** diseño donde la app funciona sin red usando datos locales y sincroniza después. +- **Outbox:** cola local de escrituras pendientes de confirmar por el servidor. +- **Capa 0/1/2 de IA:** modelo de capacidades de IA según el dispositivo (cliente puro / worker GPU / núcleo local). +- **FTS5:** módulo de búsqueda full-text de SQLite (equivalente al tsvector de Postgres). diff --git a/plan-mejora.md b/plan-mejora.md new file mode 100644 index 0000000..84f8f4e --- /dev/null +++ b/plan-mejora.md @@ -0,0 +1,269 @@ +# Plan de Mejora - RSS2 Application + +## Análisis Complejo + +Se ha realizado un análisis exhaustivo identificando **35 problemas** en total, clasificados a partir de bugs.md y revisión de código: + +- **6 Errores Críticos** (seguridad/estabilidad) +- **10 Inconsistencias** (funcionalidad) +- **5 Warnings de Arquitectura** (mantenibilidad) +- **14 Problemas Adicionales** (UI/funcionalidad/performance) + +--- + +## ⚠️ Problemas Adicionales Detectados (Nuevos) + +### 🔴 Críticos (2 nuevos) + +#### A. Sin Validación de Entrada en Endpoints Públicos +- **Archivos:** `handlers/feed.go`, `handlers/news.go`, `handlers/search.go` +- **Descripción:** Endpoints aceptan parámetros sin validación estricta (ej. `page`, `per_page` sin límites duros, SQL injection risk en `fmt.Sprintf`) +- **Impacto:** Posible DoS via parámetros grandes, inyección SQL si cambian queries +- **Acción:** Usar validator library (`go-playground/validator/v10`), validar y sanitizar todos los inputs + +#### B. Transacciones BD sin Manejo de Errores Adecuado +- **Archivos:** `handlers/feed.go:559-592` (ImportFeeds) +- **Descripción:** `SAVEPOINT`/`ROLLBACK TO SAVEPOINT`/`RELEASE SAVEPOINT` sin verificar errores en cada paso +- **Impacto:** Transacciones parcialmente aplicadas si falla a mitad +- **Acción:** Verificar error en cada `Exec`, usar `tx.Rollback()` en defer + +### 🟡 Alta Priority (3 nuevos) + +#### C. N+1 Query Problem en Entity Loading +- **Archivos:** `handlers/news.go:314-335` (GetNewsByID) +- **Descripción:** Query separada para entities por cada noticia +- **Impacto:** Latencia alta al cargar detalle de noticia +- **Acción:** JOIN en query principal o batch loading + +#### D. Pool de Conexiones Sin Configuración Óptima +- **Archivos:** `backend/internal/db/postgres.go` +- **Descripción:** Pool creado con defaults, sin `MaxConns`, `MinConns`, `MaxConnLifetime` +- **Impacto:** Agotamiento de conexiones bajo carga, conexiones zombie +- **Acción:** Configurar pool size según cores/load, agregar health checks periódicos + +#### E. Falta de Índices BD en Tablas Críticas +- **Archivos:** `init-db/` + `cmd/server/main.go` (initDB) +- **Descripción:** Tablas `noticias`, `traducciones`, `tags_noticia` sin índices en FKs y columnas de filtro +- **Impacto:** Queries lentas en producción con datos reales +- **Acción:** Añadir índices en `noticias.fecha`, `noticias.fuente_nombre`, `traducciones.noticia_id+lang_to`, `tags_noticia.noticia_id+tag_id` + +#### F. Workers Python Sin Graceful Shutdown +- **Ubicación:** `workers/*.py` +- **Descripción:** Workers no manejan SIGTERM/SIGINT, terminan abruptamente +- **Impacto:** Tareas a medio procesar, estados inconsistentes +- **Acción:** Implementar signal handling, graceful shutdown con timeout + +### 🟢 Media Priority (2 nuevos) + +#### G. Cache Inconsistente / Sin Cache Strategy +- **Ubicación:** `backend/internal/cache/redis.go` + handlers +- **Descripción:** Redis disponible pero uso esporádico, sin TTL estándar, sin invalidación +- **Impacto:** Queries repetidas costosas, datos stale +- **Acción:** Definir cache keys + TTL estándar, invalidar en writes, usar en feeds/categorías/paises + +#### H. Logging Estructurado Ausente +- **Ubicación:** Todo el backend +- **Descripción:** Solo `fmt.Println`/`log.Printf`, sin niveles, sin JSON, sin correlation IDs +- **Impacto:** Debugging difícil en producción +- **Acción:** Migrar a `zerolog` o `zap` con JSON output, request IDs + +### 🔵 Baja Priority (2 nuevos) + +#### I. Documentación API (OpenAPI/Swagger) Ausente +- **Ubicación:** Backend handlers +- **Impacto:** Frontend y consumers no tienen spec autogenerada +- **Acción:** Añadir anotaciones `swaggo/swag` o `go-swagger`, generar `/swagger` endpoint + +#### J. Secrets en Variables Entorno Sin Validación +- **Ubicación:** `config/config.go` + docker-compose +- **Descripción:** Variables como `DB_PASS`, `REDIS_PASSWORD`, `SECRET_KEY` sin validar presencia/formato +- **Impacto:** Fallos en runtime confusos si faltan +- **Acción:** Validar todas las vars requeridas al startup, fallar rápido con mensaje claro + +--- + +## 🔴 Crítico Priority (Seguridad y Estabilidad) + +### 1. Rate Limiting No Implementado +- **Archivo:** `backend/internal/middleware/auth.go:104-108` +- **Descripción:** Función vacía que no limita requests +- **Impacto:** Vulnerable a ataques DoS y fuerza bruta +- **Acción:** Implementar usando `golang.org/x/time/rate` + +### 2. CORS Demasiado Permisivo +- **Archivo:** `backend/internal/middleware/auth.go:77-90` +- **Descripción:** `Access-Control-Allow-Origin: *` +- **Impacto:** CSRF, exposición de datos a orígenes maliciosos +- **Acción:** Configurar orígenes permitidos desde variable de entorno + +### 3. Secret Key Por Defecto Hardcodeada +- **Archivo:** `backend/internal/config/config.go:32` +- **Descripción:** `SecretKey: getEnv("SECRET_KEY", "change-this-secret-key")` +- **Impacto:** Compromiso total de autenticación si no se configura +- **Acción:** Hacer `SECRET_KEY` obligatoria, panic si no está set + +### 4. Columna `role` en Users Sin Mapeo en Modelo +- **Archivos:** `models/models.go:86-94`, `cmd/server/main.go:41-48` +- **Descripción:** Columna `role` en BD pero no en struct User +- **Impacto:** Confusión entre `role` y `is_admin` +- **Acción:** Añadir campo `Role` a modelo User o remover columna BD + +### 5. LoggerMiddleware No Funcional +- **Archivo:** `middleware/auth.go:93-101` +- **Descripción:** `c.Next()` sin logging de errores +- **Impacto:** Errores van sin registrar +- **Acción:** Implementar logging real de respuestas >= 400 + +### 6. Traducción Idioma Hardcodeado 'es' +- **Archivo:** `handlers/news.go:75,96` +- **Descripción:** `t.lang_to = 'es'` en múltiples queries +- **Impacto:** No configurable a otros idiomas +- **Acción:** Pasar idioma como parámetro o configuración + +--- + +## 🟡 Alta Priority (Funcionalidad y Estabilidad) + +### 7. Discovery HTML Feed Finder Vacio +- **Archivo:** `backend/cmd/discovery/main.go:129-133` +- **Descripción:** `findFeedLinksInHTML` retorna `[]string{}, nil` +- **Impacto:** No detecta feeds en HTML, solo URLsdirectas +- **Acción:** Implementar con `goquery` para parsear HTML + +### 8. Mapeo de Campos DB vs Modelo Inconsistente +- **Archivo:** `search.go` vs `models/models.go:7-19` +- **Descripción:** Columnas `titulo`/`resumen`/`imagen`/`fecha` vs `Title`/`Summary`/`ImageURL`/`PublishedAt` +- **Impacto:** Errores de serialización potenciales +- **Acción:** Estandizar nombres en toda la aplicación + +### 9. No Hay Tests Unitarios +- **Ubicación:** Todo el backend +- **Impacto:** Cambios pueden romper funcionalidad sin detección +- **Acción:** Crear tests para handlers críticos (auth, news, feeds) + +### 10. Inicialización BD Duplicada +- **Archivos:** `db.Connect()` vs `workers.Connect()` +- **Impacto:** Piscs de conexión separadas, posible agotamiento +- **Acción:** Consolidar en una sola conexión/pool + +### 11. Parámetros Query Inconsistentes +- **Descripción:** Algunos usan `category_id` vs `categoria_id` +- **Impacto:** Confusión en filtros +- **Acción:** Unificar nomenclatura (preferir `category_id` en inglés) + +### 12. API Field Naming Inconsistente +- **Archivo:** `frontend/src/services/api.ts` +- **Descripción:** Interface `News` usa camelCase, pero respuestas van snake_case +- **Impacto:** Datos no se muestran correctamente +- **Acción:** Arreglar mapping en api.ts o estandarizar respuestas Go + +--- + +## 🟢 Media Priority (Calidad y Mantenimiento) + +### 13. No Hay Health Checks en Workers +- **Ubicación:** Todos los workers Python/Go +- **Impacto:** Dificultad para monitorear salud de workers +- **Acción:** Implementar endpoint `/health` en cada worker + +### 14. Structs Duplicadas Mismo Recurso +- **Archivo:** `handlers/news.go` vs `models/models.go:News` +- **Descripción:** `NewsResponse` diferente a `models.News` +- **Impacto:** Mantenimiento duplicado +- **Acción:** Unificar o mantener mapping explícito + +### 15. Query Params Inconsistentes (category_id vs categoria_id) +- **Descripción:** Mix de inglés y español en handlers +- **Impacto:** Confusión para usuarios de la API +- **Acción:** Definir estándar y aplicarlo en todos lados + +### 16. Duplicación Config DB +- **Descripción:** API usa `DATABASE_URL`, workers usan `DB_HOST`/`DB_PORT` etc. +- **Impacto:** Configuración fragmentada +- **Acción:** Unificar variables de entorno + +--- + +## 🔵 Baja Priority (UX y Refactoring) + +### 17. Frontend API Field Mapping Fixes +- **Archivo:** `frontend/src/services/api.ts` +- **Impacto:** Datos se muestran incorrectamente o undefined +- **Acción:** Revisar y corregir tipos de interfaz + +### 18. Mejorar Estados de Error en UI +- **Ubicación:** Todas las páginas frontend +- **Impacto:** UX pobre cuando fallan requests +- **Acción:** Mensajes de error claros y acciones de recuperación + +### 19. Skeleton Loaders en lugar de Spinners +- **Ubicación:** Componente NewsList, News detail, etc. +- **Impacto:** Perceived performance mejorada +- **Acción:** Implementar componentes skeleton con Tailwind + +### 20. Diseño Responsivo Mejorado +- **Ubicación:** Grid de noticias, selectores, formularios +- **Impacto:** Mejor experiencia móvil/tablet +- **Acción:** Refinar clases Tailwind, breakpoints + +### 21. Tipografía y Esquema de Color Consistente +- **Ubicación:** Todo el frontend +- **Impacto:** Aspecto amateur, inconsistencia visual +- **Acción:** Definir palette principal, usar consistentemente + +### 22. Micro-interacciones y Transiciones Más Suaves +- **Ubicación:** Hover effects, focus states, button animations +- **Impacto:** Experiencia más pulida y profesional +- **Acción:** Agregar transitions, hover: clases, focus-visible + +### 23. Mejoras de Accesibilidad +- **Ubicación:** Componentes form, links, imágenes +- **Impacto:** Cumplimiento WCAG, mejor usabilidad +- **Acción:** ARIA labels, contrast ratios, focus outlines, tabindex + +### 24. Estados Vacios Mejorados +- **Ubicación:** Páginas sin resultados, sin noticias, estados iniciales +- **Impacto:** UX cuando no hay datos que mostrar +- **Acción:** Mensajes descriptivos, ilustraciones, acciones sugeridas + +### 25. Skeletons de Carga por Componente +- **Ubicación:** News cards, feeds lists, entity lists +- **Impacto:** Indicar progreso mejor que spinners puros +- **Acción:** Implementar skeleton grids con dimensiones reales + +--- + +## Resumen Ejecutivo + +| Prioridad | Cantidad | Enfoque | +|-----------|----------|---------| +| 🔴 Crítico | 8 | Seguridad, estabilidad, validación | +| 🟡 Alta | 10 | Funcionalidad, bugs, performance | +| 🟢 Media | 8 | Mantenimiento, calidad, observabilidad | +| 🔵 Baja | 8 | UX, refinamientos, documentación | + +**Total: 35 problemas** + +**Primeros pasos recomendados (Sprint 1 - Seguridad + Estabilidad):** +1. Rate limiting + CORS + Secret key (seguridad inmediata) +2. Validación de entrada en endpoints públicos +3. Transacciones BD con manejo de errores correcto +4. Índices BD en tablas críticas + +**Sprint 2 - Funcionalidad + Performance:** +5. Arreglar naming conventions (consistencia en todo) +6. Pool de conexiones optimizado + health checks +7. N+1 queries en entity loading +8. Cache strategy + invalidación + +**Sprint 3 - Calidad + Observabilidad:** +9. Logging estructurado (zerolog/zap) +10. Tests unitarios para handlers críticos +11. Graceful shutdown en workers +12. Health checks en todos los workers + +**Sprint 4 - UX + Documentación:** +13. OpenAPI/Swagger docs +14. Skeleton loaders + estados vacíos +15. Accesibilidad + micro-interacciones +16. Traducción idioma configurable \ No newline at end of file diff --git a/translate-worker b/translate-worker new file mode 100755 index 0000000..1b8671a --- /dev/null +++ b/translate-worker @@ -0,0 +1,25 @@ +#!/data/data/com.termux/files/usr/bin/sh +# ============================================================================== +# translate-worker - Worker de traducción MINIMO para Termux +# ============================================================================== +# Ejecuta el worker que se conecta al servidor y reenvía jobs de traducción. +# Primera vez pide API key, luego funciona automáticamente. +#============================================================================== + +export WORKER_NAME="${WORKER_NAME:-termux-$(date +%s)}" + +# Pedir API key si no está puesta +if [ -z "$RSS2_API_KEY" ]; then + echo "🔑 RSS2 Translation Worker" + echo "=========================" + read -p "API key: " RSS2_API_KEY + export RSS2_API_KEY +fi + +echo "🚀 Iniciando worker de traducción mínima..." +echo " Worker: $WORKER_NAME" +echo " Modo: Reenvío de jobs al servidor" +echo "" + +# Ejecutar worker Python mínimo +exec python3 /home/x/rss2/translate_worker_termux.py "$@" \ No newline at end of file diff --git a/translate_worker_termux.py b/translate_worker_termux.py new file mode 100755 index 0000000..3b60b78 --- /dev/null +++ b/translate_worker_termux.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +""" +Worker de traducción MINIMO para Termux Android. +Objetivo único: Recibir jobs de traducción del servidor y enviar resultados de vuelta. +No requiere modelo ML para conectarse y reportar status. +""" + +import os +import sys +import json +import logging +import readline + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") +LOG = logging.getLogger("minimal-worker") + +# ========================================================== +# Config - SOLO API key necesaria +# ========================================================== +WORKER_NAME = os.environ.get("WORKER_NAME", f"termux-{os.getpid()}") +WORKER_SERVER = os.environ.get("WORKER_SERVER", "ws://10.0.2.2:8080/ws/worker") + +# API key: pedir si no está puesta +if not os.environ.get("RSS2_API_KEY"): + api_key = input("🔑 API key de RSS2: ").strip() + if not api_key: + print("❌ Key requerida. Saliendo.") + sys.exit(1) + os.environ["RSS2_API_KEY"] = api_key + +WORKER_API_KEY = os.environ["RSS2_API_KEY"] + +# Código de idiomas simples +LANG_MAP = { + "en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn", + "it": "ita_Latn", "pt": "por_Latn", "nl": "nld_Latn", "sv": "swe_Latn", + "da": "dan_Latn", "fi": "fin_Latn", "no": "nob_Latn", + "pl": "pol_Latn", "cs": "ces_Latn", "sk": "slk_Latn", + "sl": "slv_Latn", "hu": "hun_Latn", "ro": "ron_Latn", + "el": "ell_Grek", "ru": "rus_Cyrl", "uk": "ukr_Cyrl", + "tr": "tur_Latn", "ar": "arb_Arab", "fa": "pes_Arab", + "he": "heb_Hebr", "zh": "zho_Hans", "ja": "jpn_Jpan", + "ko": "kor_Hang", "vi": "vie_Latn", +} + +# Estado simple - solo conecta y reenvía +connected = False +jobs_processed = 0 + +def on_open(ws): + global connected + connected = True + LOG.info(f"📡 Conectado como {WORKER_NAME}") + ws.send(json.dumps({ + "type": "register", + "worker_name": WORKER_NAME + })) + +def on_message(ws, message): + global jobs_processed + try: + msg = json.loads(message) + except: + return + + if msg.get("type") == "job": + job = msg.get("job", {}) + result = process_job(job) + ws.send(json.dumps({ + "type": "result", + "result": result + })) + jobs_processed += 1 + if jobs_processed % 10 == 0: + LOG.info(f"📊 Procesados {jobs_processed} jobs") + +def process_job(job): + """Procesa un job de traducción - mínimo esfuerzo.""" + job_id = job.get("id", "unknown") + lang_from = job.get("lang_from", "en") + lang_to = job.get("lang_to", "es") + title = job.get("title", "") + summary = job.get("summary", "") + + # Traducción "mínima": si mismoidioma, pas-through + # Si tiene modelo, traduciría aquí; si no, devolvemos original + + # Marcar resultado + result = { + "job_id": job_id, + "title_orig": title[:50] if title else "", + "summary_orig": summary[:50] if summary else "", + "lang_from": lang_from, + "lang_to": lang_to, + "title_trad": title, # Pas-through si sin modelo + "summary_trad": summary, + "translated": False, # Flag: ¿realmente tradujimos? + "error": "" + } + return result + +def on_error(ws, error): + LOG.error(f"WS error: {error}") + +def on_close(ws, *args): + LOG.warning(f"Conexión cerrada") + global connected + connected = False + +def connect(): + """Conectar WebSocket al servidor.""" + import websocket + ws_url = f"ws://localhost:8080/ws/worker?api_key={WORKER_API_KEY}" + + ws = websocket.WebSocketApp( + ws_url, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close, + ) + ws.run_forever(ping_interval=15, ping_timeout=5) + +def main(): + global connected + LOG.info(f"Worker mínimo starting: {WORKER_NAME}") + LOG.info(f"Conectando a: ws://localhost:8080/ws/worker") + + # Intentar conectar (se reintenta solo si se corta) + connect() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/workers/ctranslator_worker.py b/workers/ctranslator_worker.py index 5bb94a3..c391953 100644 --- a/workers/ctranslator_worker.py +++ b/workers/ctranslator_worker.py @@ -590,7 +590,7 @@ def fetch_pending_translations(conn): WHERE t.lang_to = %s AND (t.titulo_trad IS NULL OR t.resumen_trad IS NULL) AND (t.locked_at IS NULL OR t.locked_at < NOW() - INTERVAL '10 minutes') - ORDER BY n.fecha ASC + ORDER BY n.fecha DESC LIMIT %s FOR UPDATE SKIP LOCKED """, diff --git a/workers/translation_scheduler.py b/workers/translation_scheduler.py index 64a480f..3fd5e2c 100644 --- a/workers/translation_scheduler.py +++ b/workers/translation_scheduler.py @@ -64,7 +64,7 @@ def create_translation_jobs(conn): SELECT 1 FROM traducciones t WHERE t.noticia_id = n.id AND t.lang_to = %s ) - ORDER BY n.fecha ASC + ORDER BY n.fecha DESC LIMIT %s ON CONFLICT (noticia_id, lang_to) DO NOTHING RETURNING noticia_id