fork para inversionitas con bot simple
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
entorno
|
entorno
|
||||||
__pypchache__
|
__pypchache__
|
||||||
*.pyc
|
*.pyc
|
||||||
|
venv
|
@ -9,8 +9,8 @@ load_dotenv()
|
|||||||
|
|
||||||
# Configuración de MySQL desde variables de entorno
|
# Configuración de MySQL desde variables de entorno
|
||||||
MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql")
|
MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql")
|
||||||
MYSQL_USER = os.getenv("MYSQL_USER", "nick")
|
MYSQL_USER = os.getenv("MYSQL_USER", "investionitas")
|
||||||
MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "nick")
|
MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "inversionitas")
|
||||||
MYSQL_DATABASE = os.getenv("MYSQL_DATABASE", "news")
|
MYSQL_DATABASE = os.getenv("MYSQL_DATABASE", "news")
|
||||||
MYSQL_PORT = os.getenv("MYSQL_PORT", "3306")
|
MYSQL_PORT = os.getenv("MYSQL_PORT", "3306")
|
||||||
|
|
||||||
|
@ -12,74 +12,28 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Obtener variables de entorno
|
# Obtener variables de entorno
|
||||||
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://host.docker.internal:11434/api/generate")
|
# OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://host.docker.internal:11434/api/generate")
|
||||||
OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3")
|
# OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3")
|
||||||
|
|
||||||
def is_security_related(prompt):
|
# def is_economy_related(prompt):
|
||||||
data = {
|
# data = {
|
||||||
"model": OLLAMA_MODEL,
|
# "model": OLLAMA_MODEL,
|
||||||
"prompt": f"Does the following topic relate to national defense, armed forces, police, espionage, or intelligence? Answer only with 'true' or 'false'. Topic: {prompt}",
|
# "prompt": f"Does the following topic relate to economy, investing or financial issues? Answer only with 'true' or 'false'. Topic: {prompt}",
|
||||||
}
|
# }
|
||||||
|
|
||||||
try:
|
# try:
|
||||||
response = requests.post(OLLAMA_URL, json=data)
|
# response = requests.post(OLLAMA_URL, json=data)
|
||||||
response.raise_for_status() # Lanza una excepción si la solicitud falla
|
# response.raise_for_status() # Lanza una excepción si la solicitud falla
|
||||||
|
|
||||||
for line in response.text.strip().split("\n"):
|
# for line in response.text.strip().split("\n"):
|
||||||
json_data = json.loads(line)
|
# json_data = json.loads(line)
|
||||||
if "response" in json_data and json_data["response"].strip():
|
# if "response" in json_data and json_data["response"].strip():
|
||||||
result = json_data["response"].strip().lower() == "true"
|
# result = json_data["response"].strip().lower() == "true"
|
||||||
return result
|
# return result
|
||||||
|
|
||||||
except requests.RequestException as e:
|
# except requests.RequestException as e:
|
||||||
logging.error(f"Request error: {e}")
|
# logging.error(f"Request error: {e}")
|
||||||
except json.JSONDecodeError as e:
|
# except json.JSONDecodeError as e:
|
||||||
logging.error(f"JSON Decode Error: {e}")
|
# logging.error(f"JSON Decode Error: {e}")
|
||||||
|
|
||||||
return False
|
# return False
|
||||||
|
|
||||||
def is_critico(prompt):
|
|
||||||
data = {
|
|
||||||
"model": OLLAMA_MODEL,
|
|
||||||
"prompt": f"Does the following text criticizes the armed forces, security forces as Guardia Civil or Police, intelligence agencies such as CNI? Answer only with 'true' or 'false'. Topic: {prompt}",
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.post(OLLAMA_URL, json=data)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
for line in response.text.strip().split("\n"):
|
|
||||||
json_data = json.loads(line)
|
|
||||||
if "response" in json_data and json_data["response"].strip():
|
|
||||||
result = json_data["response"].strip().lower() == "true"
|
|
||||||
return result
|
|
||||||
|
|
||||||
except requests.RequestException as e:
|
|
||||||
logging.error(f"Request error: {e}")
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logging.error(f"JSON Decode Error: {e}")
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_favorable(prompt):
|
|
||||||
data = {
|
|
||||||
"model": OLLAMA_MODEL,
|
|
||||||
"prompt": f"Does the following text favor the armed forces, security forces as Guardia Civil or Police, intelligence agencies such as CNI? Answer only with 'true' or 'false'. Topic: {prompt}",
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.post(OLLAMA_URL, json=data)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
for line in response.text.strip().split("\n"):
|
|
||||||
json_data = json.loads(line)
|
|
||||||
if "response" in json_data and json_data["response"].strip():
|
|
||||||
result = json_data["response"].strip().lower() == "true"
|
|
||||||
return result
|
|
||||||
|
|
||||||
except requests.RequestException as e:
|
|
||||||
logging.error(f"Request error: {e}")
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logging.error(f"JSON Decode Error: {e}")
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
23
app/main.py
23
app/main.py
@ -2,6 +2,8 @@ from fastapi import FastAPI
|
|||||||
from database import Base, engine
|
from database import Base, engine
|
||||||
from routes import router
|
from routes import router
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from telegrambot import enviar_mensaje_sync
|
||||||
#from webscrapper import ejecutar_scrapper
|
#from webscrapper import ejecutar_scrapper
|
||||||
|
|
||||||
# Crear las tablas en MySQL si no existen
|
# Crear las tablas en MySQL si no existen
|
||||||
@ -11,14 +13,17 @@ Base.metadata.create_all(bind=engine)
|
|||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
scheduler = BackgroundScheduler()
|
scheduler = BackgroundScheduler()
|
||||||
|
|
||||||
|
def tarea_programada():
|
||||||
|
enviar_mensaje_sync("¡Buenos días! Aquí tienes tu mensaje diario.")
|
||||||
|
|
||||||
|
scheduler.add_job(tarea_programada, "cron", hour=8, minute=0)
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
scheduler.start() # Iniciar el scheduler cuando la app arranca
|
||||||
|
yield
|
||||||
|
scheduler.shutdown() # Apagar el scheduler al cerrar la app
|
||||||
|
|
||||||
|
|
||||||
# Incluir rutas
|
# Incluir rutas
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
|
||||||
# @app.on_event("startup")
|
|
||||||
# def startup_event():
|
|
||||||
# scheduler.add_job(ejecutar_scrapper, "interval", hours=24)
|
|
||||||
# scheduler.start()
|
|
||||||
|
|
||||||
# @app.on_event("shutdown")
|
|
||||||
# def shutdown_event():
|
|
||||||
# scheduler.shutdown()
|
|
||||||
|
@ -12,6 +12,4 @@ class NewsItem(Base):
|
|||||||
fuente = Column(String(255), nullable=True)
|
fuente = Column(String(255), nullable=True)
|
||||||
fecha = Column(DateTime, default=datetime.utcnow)
|
fecha = Column(DateTime, default=datetime.utcnow)
|
||||||
link = Column(String(500), unique=True, nullable=False)
|
link = Column(String(500), unique=True, nullable=False)
|
||||||
critico = Column(Boolean, default=False)
|
|
||||||
favorable = Column(Boolean, default=False)
|
|
||||||
keyword = Column(String(255), unique=False, nullable=True)
|
keyword = Column(String(255), unique=False, nullable=True)
|
||||||
|
319
app/routes.py
319
app/routes.py
@ -1,4 +1,4 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
from database import get_db
|
from database import get_db
|
||||||
@ -25,157 +25,8 @@ class NewsItemCreate(BaseModel):
|
|||||||
fuente: str | None = None
|
fuente: str | None = None
|
||||||
fecha: datetime | None = None
|
fecha: datetime | None = None
|
||||||
link: str
|
link: str
|
||||||
critico:bool
|
|
||||||
favorable:bool
|
|
||||||
keyword: str | None = None
|
keyword: str | None = None
|
||||||
|
|
||||||
@router.post("/news/")
|
|
||||||
def create_news_item(item: NewsItemCreate, db: Session = Depends(get_db)):
|
|
||||||
# Verificar si el título ya existe
|
|
||||||
existing_item = db.query(NewsItem).filter(NewsItem.titulo == item.titulo).first()
|
|
||||||
if existing_item:
|
|
||||||
logging.info("Título ya en la base de datos")
|
|
||||||
|
|
||||||
# Crear nuevo objeto
|
|
||||||
new_item = NewsItem(
|
|
||||||
titulo=item.titulo,
|
|
||||||
contenido=item.contenido,
|
|
||||||
autor=item.autor,
|
|
||||||
fuente=item.fuente,
|
|
||||||
fecha=item.fecha or datetime.utcnow(),
|
|
||||||
link=item.link,
|
|
||||||
critico=item.critico,
|
|
||||||
favorable=item.favorable,
|
|
||||||
keyword=item.keyword
|
|
||||||
)
|
|
||||||
|
|
||||||
db.add(new_item)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(new_item)
|
|
||||||
|
|
||||||
return {"message": "Noticia creada con éxito", "id": new_item.id, "titulo": new_item.titulo}
|
|
||||||
|
|
||||||
@router.get("/news/count/by-source/date-range")
|
|
||||||
def count_news_by_source_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
|
||||||
.filter(NewsItem.fecha >= fecha_inicio, NewsItem.fecha <= fecha_fin)
|
|
||||||
.group_by(NewsItem.fuente)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"count_by_source_in_range": [{"fuente": fuente, "count": count} for fuente, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/count/by-author/date-range")
|
|
||||||
def count_news_by_author_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.autor, func.count(NewsItem.id))
|
|
||||||
.filter(NewsItem.fecha >= fecha_inicio, NewsItem.fecha <= fecha_fin)
|
|
||||||
.group_by(NewsItem.autor)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"count_by_author_in_range": [{"autor": autor, "count": count} for autor, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/count/favorable/by-author/date-range")
|
|
||||||
def count_favorable_news_by_author_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.autor, func.count(NewsItem.id))
|
|
||||||
.filter(
|
|
||||||
NewsItem.favorable == True,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.group_by(NewsItem.autor)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"favorable_count_by_author_in_range": [{"autor": autor, "count": count} for autor, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/count/unfavorable/by-author/date-range")
|
|
||||||
def count_unfavorable_news_by_author_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.autor, func.count(NewsItem.id))
|
|
||||||
.filter(
|
|
||||||
NewsItem.critico == True,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.group_by(NewsItem.autor)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"unfavorable_count_by_author_in_range": [{"autor": autor, "count": count} for autor, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/count/favorable/by-source/date-range")
|
|
||||||
def count_favorable_news_by_source_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
|
||||||
.filter(
|
|
||||||
NewsItem.favorable == True,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.group_by(NewsItem.fuente)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"favorable_count_by_source_in_range": [{"fuente": fuente, "count": count} for fuente, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/count/unfavorable/by-source/date-range")
|
|
||||||
def count_unfavorable_news_by_source_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
|
||||||
.filter(
|
|
||||||
NewsItem.critico == True,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.group_by(NewsItem.fuente)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {"unfavorable_count_by_source_in_range": [{"fuente": fuente, "count": count} for fuente, count in results]}
|
|
||||||
|
|
||||||
@router.get("/news/neutral/date-range")
|
|
||||||
def get_neutral_news_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem)
|
|
||||||
.filter(
|
|
||||||
NewsItem.favorable == False,
|
|
||||||
NewsItem.critico == False,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/news/mixed/date-range")
|
|
||||||
def get_mixed_news_in_range(
|
|
||||||
fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
results = (
|
|
||||||
db.query(NewsItem)
|
|
||||||
.filter(
|
|
||||||
NewsItem.favorable == True,
|
|
||||||
NewsItem.critico == True,
|
|
||||||
NewsItem.fecha >= fecha_inicio,
|
|
||||||
NewsItem.fecha <= fecha_fin,
|
|
||||||
)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return results
|
|
||||||
|
|
||||||
@router.get("/news/count/by-source")
|
@router.get("/news/count/by-source")
|
||||||
def count_news_by_source(db: Session = Depends(get_db)):
|
def count_news_by_source(db: Session = Depends(get_db)):
|
||||||
results = (
|
results = (
|
||||||
@ -202,62 +53,142 @@ def count_news_by_author(db: Session = Depends(get_db)):
|
|||||||
|
|
||||||
return {"count_by_author": response}
|
return {"count_by_author": response}
|
||||||
|
|
||||||
@router.get("/news/count/favorable/by-author")
|
@router.get("/news/{news_id}")
|
||||||
def count_favorable_news_by_author(db: Session = Depends(get_db)):
|
def get_news_by_id(news_id: int, db: Session = Depends(get_db)):
|
||||||
results = (
|
news_item = db.query(NewsItem).filter(NewsItem.id == news_id).first()
|
||||||
db.query(NewsItem.autor, func.count(NewsItem.id))
|
|
||||||
.filter(NewsItem.favorable == True)
|
if not news_item:
|
||||||
.group_by(NewsItem.autor)
|
raise HTTPException(status_code=404, detail="Noticia no encontrada")
|
||||||
.all()
|
|
||||||
)
|
return {
|
||||||
return {"favorable_count_by_author": results}
|
"id": news_item.id,
|
||||||
|
"titulo": news_item.titulo,
|
||||||
|
"contenido": news_item.contenido,
|
||||||
|
"autor": news_item.autor,
|
||||||
|
"fuente": news_item.fuente,
|
||||||
|
"fecha": news_item.fecha,
|
||||||
|
"link": news_item.link,
|
||||||
|
"critico": news_item.critico,
|
||||||
|
"favorable": news_item.favorable,
|
||||||
|
"keyword": news_item.keyword,
|
||||||
|
}
|
||||||
|
|
||||||
@router.get("/news/count/unfavorable/by-author")
|
@router.get("/news/count/by-keyword")
|
||||||
def count_unfavorable_news_by_author(db: Session = Depends(get_db)):
|
def count_news_by_keyword(
|
||||||
results = (
|
fechaInicio: datetime | None = None,
|
||||||
db.query(NewsItem.autor, func.count(NewsItem.id))
|
fechaFin: datetime | None = None,
|
||||||
.filter(NewsItem.critico == True)
|
db: Session = Depends(get_db)):
|
||||||
.group_by(NewsItem.autor)
|
query = db.query(NewsItem.keyword, func.count(NewsItem.id)).filter(NewsItem.keyword.isnot(None))
|
||||||
.all()
|
if fechaInicio and fechaFin:
|
||||||
)
|
query = query.filter(NewsItem.fecha >= fechaInicio, NewsItem.fecha <= fechaFin)
|
||||||
return {"unfavorable_count_by_author": results}
|
results = query.group_by(NewsItem.keyword).all()
|
||||||
|
|
||||||
@router.get("/news/count/favorable/by-source")
|
return {keyword: count for keyword, count in results}
|
||||||
def count_favorable_news_by_source(db: Session = Depends(get_db)):
|
|
||||||
|
@router.get("/news/keywords/")
|
||||||
|
def get_all_keywords(db: Session = Depends(get_db)):
|
||||||
|
results = db.query(NewsItem.keyword).distinct().all()
|
||||||
|
|
||||||
|
# Extraer solo las keywords y filtrar valores nulos
|
||||||
|
keywords = [keyword[0] for keyword in results if keyword[0] is not None]
|
||||||
|
|
||||||
|
return {"keywords": keywords}
|
||||||
|
|
||||||
|
@router.get("/news/count/by-source/keyword/date-range")
|
||||||
|
def count_news_by_source_for_keyword_in_range(
|
||||||
|
keyword: str, fechaInicio: datetime, fechaFin: datetime, db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
results = (
|
results = (
|
||||||
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
||||||
.filter(NewsItem.favorable == True)
|
.filter(
|
||||||
|
NewsItem.keyword == keyword,
|
||||||
|
NewsItem.fecha >= fechaInicio,
|
||||||
|
NewsItem.fecha <= fechaFin
|
||||||
|
)
|
||||||
.group_by(NewsItem.fuente)
|
.group_by(NewsItem.fuente)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
return {"favorable_count_by_source": results}
|
formatted_results = {fuente: count for fuente, count in results}
|
||||||
|
return {"count_by_source_keyword": formatted_results}
|
||||||
|
|
||||||
@router.get("/news/count/unfavorable/by-source")
|
@router.get("/news/titles/by-keyword/date-range")
|
||||||
def count_unfavorable_news_by_source(db: Session = Depends(get_db)):
|
def get_titles_by_keyword_in_range(
|
||||||
|
keyword: str, fecha_inicio: datetime, fecha_fin: datetime, db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
results = (
|
results = (
|
||||||
db.query(NewsItem.fuente, func.count(NewsItem.id))
|
db.query(NewsItem.titulo)
|
||||||
.filter(NewsItem.critico == True)
|
.filter(
|
||||||
.group_by(NewsItem.fuente)
|
NewsItem.keyword == keyword,
|
||||||
|
NewsItem.fecha >= fecha_inicio,
|
||||||
|
NewsItem.fecha <= fecha_fin
|
||||||
|
)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
return {"unfavorable_count_by_source": results}
|
|
||||||
|
|
||||||
@router.get("/news/neutral")
|
# Convertir resultados en una sola cadena separada por comas
|
||||||
def get_neutral_news(db: Session = Depends(get_db)):
|
titles = ", ".join([titulo[0] for titulo in results if titulo[0]])
|
||||||
results = (
|
|
||||||
db.query(NewsItem)
|
|
||||||
.filter(NewsItem.favorable == False, NewsItem.critico == False)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
return {"titles": titles}
|
||||||
|
|
||||||
@router.get("/news/mixed")
|
# @router.get("/news-summary")
|
||||||
def get_mixed_news(db: Session = Depends(get_db)):
|
# def get_news_summary(
|
||||||
results = (
|
# keyword: str,
|
||||||
db.query(NewsItem)
|
# fecha_inicio: datetime,
|
||||||
.filter(NewsItem.favorable == True, NewsItem.critico == True)
|
# fecha_fin: datetime,
|
||||||
.all()
|
# db: Session = Depends(get_db)
|
||||||
)
|
# ):
|
||||||
return results
|
# results = get_titles_by_keyword_in_range(keyword,fecha_inicio, fecha_fin, db)
|
||||||
|
# data = {
|
||||||
|
# "model":"llama3",
|
||||||
|
# "prompt": f"A continuación tienes los titulares de muchas noticias, resume lo que encuentres más destacado. Noticias: {results}",
|
||||||
|
# }
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# response = requests.post(
|
||||||
|
# "http://host.docker.internal:11434/api/generate", json=data
|
||||||
|
# )
|
||||||
|
# response.raise_for_status()
|
||||||
|
|
||||||
|
# except requests.RequestException as e:
|
||||||
|
# logging.error(f"Request error: {e}")
|
||||||
|
# return response
|
||||||
|
|
||||||
|
@router.get("/news/titles/")
|
||||||
|
def get_news_titles(
|
||||||
|
fechaInicio: datetime | None = None,
|
||||||
|
fechaFin: datetime | None = None,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
query = db.query(NewsItem.titulo)
|
||||||
|
|
||||||
|
if fechaInicio and fechaFin:
|
||||||
|
query = query.filter(NewsItem.fecha >= fechaInicio, NewsItem.fecha <= fechaFin)
|
||||||
|
|
||||||
|
results = query.all()
|
||||||
|
return {"titles": [titulo[0] for titulo in results]}
|
||||||
|
|
||||||
|
@router.get("/news/")
|
||||||
|
def get_all_news(
|
||||||
|
fechaInicio: datetime | None = None,
|
||||||
|
fechaFin: datetime | None = None,
|
||||||
|
page:int = Query(1, alias="page", ge=1),
|
||||||
|
pageSize: int = Query(10, alias="pageSize", ge=1, le=100),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
query = db.query(NewsItem.id, NewsItem.titulo, NewsItem.link, NewsItem.favorable, NewsItem.critico) # Solo seleccionamos id y titulo
|
||||||
|
|
||||||
|
if fechaInicio and fechaFin:
|
||||||
|
query = query.filter(NewsItem.fecha >= fechaInicio, NewsItem.fecha <= fechaFin)
|
||||||
|
|
||||||
|
totalCount = query.count()
|
||||||
|
|
||||||
|
results = query.offset((page - 1) * pageSize).limit(pageSize).all()
|
||||||
|
return {
|
||||||
|
"totalCount": totalCount,
|
||||||
|
"page": page,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
"totalPages": (totalCount + pageSize - 1) // pageSize,
|
||||||
|
"news": [{"id": news_id, "titulo": titulo, "url":link, "critico":critico, "favorable": favorable} for
|
||||||
|
news_id, titulo, link, critico, favorable in results] # Devolvemos solo id y titulo
|
||||||
|
|
||||||
|
}
|
15
app/telegrambot.py
Normal file
15
app/telegrambot.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from telegram import Bot
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Configura tu bot con el token de BotFather
|
||||||
|
TOKEN = "7626026035:AAHEMp_iIN3y8AwywL0R6OTQvNi7EcJZ0iY"
|
||||||
|
CHAT_ID = "-123456789" # Reemplaza con el ID del grupo
|
||||||
|
|
||||||
|
bot = Bot(token=TOKEN)
|
||||||
|
|
||||||
|
async def enviar_mensaje(mensaje: str):
|
||||||
|
await bot.send_message(chat_id=CHAT_ID, text=mensaje)
|
||||||
|
|
||||||
|
# Para ejecutar la función de forma síncrona cuando sea necesario
|
||||||
|
def enviar_mensaje_sync(mensaje: str):
|
||||||
|
asyncio.run(enviar_mensaje(mensaje))
|
@ -2,7 +2,7 @@ import requests
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import time
|
import time
|
||||||
from googlenewsdecoder import gnewsdecoder
|
from googlenewsdecoder import gnewsdecoder
|
||||||
from iacorrector import is_security_related, is_critico, is_favorable # Importa la función desde iacorrector.py
|
# from iacorrector import is_economy_related # Importa la función desde iacorrector.py
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
import pytz
|
||||||
import logging
|
import logging
|
||||||
@ -103,8 +103,6 @@ def search_news(query):
|
|||||||
date = article.pubDate.get_text(strip=True) if article.pubDate else "Fecha no disponible"
|
date = article.pubDate.get_text(strip=True) if article.pubDate else "Fecha no disponible"
|
||||||
date_parsed = datetime.strptime(date, '%a, %d %b %Y %H:%M:%S GMT')
|
date_parsed = datetime.strptime(date, '%a, %d %b %Y %H:%M:%S GMT')
|
||||||
date_parsed = date_parsed.replace(tzinfo=pytz.UTC)
|
date_parsed = date_parsed.replace(tzinfo=pytz.UTC)
|
||||||
critico = False
|
|
||||||
favorable = False
|
|
||||||
|
|
||||||
# Obtener la URL final del artículo
|
# Obtener la URL final del artículo
|
||||||
final_url = get_url_from_google_news(link)
|
final_url = get_url_from_google_news(link)
|
||||||
@ -112,24 +110,22 @@ def search_news(query):
|
|||||||
# Obtener el autor usando autorsearcher.py
|
# Obtener el autor usando autorsearcher.py
|
||||||
author = get_author_from_script(final_url)
|
author = get_author_from_script(final_url)
|
||||||
content = get_article_content(final_url)
|
content = get_article_content(final_url)
|
||||||
critico = is_critico(content)
|
|
||||||
favorable = is_favorable(content)
|
|
||||||
|
|
||||||
|
|
||||||
# Verificar si el artículo es válido usando iacorrector
|
# Verificar si el artículo es válido usando iacorrector
|
||||||
if is_security_related(content): # Solo si el artículo es válido
|
# if is_economy_related(content): # Solo si el artículo es válido
|
||||||
news_item = {
|
# news_item = {
|
||||||
"titulo": title,
|
# "titulo": title,
|
||||||
"contenido": content,
|
# "contenido": content,
|
||||||
"autor": author,
|
# "autor": author,
|
||||||
"fuente": source_info,
|
# "fuente": source_info,
|
||||||
"fecha": date_parsed.isoformat(),
|
# "fecha": date_parsed.isoformat(),
|
||||||
"link": final_url, # Guardamos la URL final en lugar de la de Google News,
|
# "link": final_url, # Guardamos la URL final en lugar de la de Google News,
|
||||||
"critico": critico,
|
# "critico": critico,
|
||||||
"favorable":favorable,
|
# "favorable":favorable,
|
||||||
"keyword": query
|
# "keyword": query
|
||||||
}
|
# }
|
||||||
insertar_datos(news_item)
|
# insertar_datos(news_item)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info(f"Error al procesar un artículo para '{query}': {e}")
|
logging.info(f"Error al procesar un artículo para '{query}': {e}")
|
||||||
|
@ -9,3 +9,6 @@ sqlalchemy
|
|||||||
pydantic
|
pydantic
|
||||||
python-dotenv
|
python-dotenv
|
||||||
mysql-connector-python
|
mysql-connector-python
|
||||||
|
python-telegram-bot
|
||||||
|
apscheduler
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user