Implementación inicial del sistema de automatización de redes sociales
- Estructura completa del proyecto con FastAPI - Modelos de base de datos (productos, servicios, posts, calendario, interacciones) - Publishers para X, Threads, Instagram, Facebook - Generador de contenido con DeepSeek API - Worker de Celery con tareas programadas - Dashboard básico con templates HTML - Docker Compose para despliegue - Documentación completa Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
226
app/api/routes/interactions.py
Normal file
226
app/api/routes/interactions.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
API Routes para gestión de Interacciones.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.models.interaction import Interaction
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ===========================================
|
||||
# SCHEMAS
|
||||
# ===========================================
|
||||
|
||||
class InteractionResponse(BaseModel):
|
||||
content: str
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ===========================================
|
||||
# ENDPOINTS
|
||||
# ===========================================
|
||||
|
||||
@router.get("/")
|
||||
async def list_interactions(
|
||||
platform: Optional[str] = Query(None),
|
||||
interaction_type: Optional[str] = Query(None),
|
||||
responded: Optional[bool] = Query(None),
|
||||
is_lead: Optional[bool] = Query(None),
|
||||
is_read: Optional[bool] = Query(None),
|
||||
limit: int = Query(50, le=100),
|
||||
offset: int = Query(0),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Listar interacciones con filtros."""
|
||||
query = db.query(Interaction)
|
||||
|
||||
if platform:
|
||||
query = query.filter(Interaction.platform == platform)
|
||||
if interaction_type:
|
||||
query = query.filter(Interaction.interaction_type == interaction_type)
|
||||
if responded is not None:
|
||||
query = query.filter(Interaction.responded == responded)
|
||||
if is_lead is not None:
|
||||
query = query.filter(Interaction.is_lead == is_lead)
|
||||
if is_read is not None:
|
||||
query = query.filter(Interaction.is_read == is_read)
|
||||
|
||||
interactions = query.order_by(
|
||||
Interaction.interaction_at.desc()
|
||||
).offset(offset).limit(limit).all()
|
||||
|
||||
return [i.to_dict() for i in interactions]
|
||||
|
||||
|
||||
@router.get("/pending")
|
||||
async def list_pending_interactions(db: Session = Depends(get_db)):
|
||||
"""Listar interacciones sin responder."""
|
||||
interactions = db.query(Interaction).filter(
|
||||
Interaction.responded == False,
|
||||
Interaction.is_archived == False
|
||||
).order_by(
|
||||
Interaction.priority.desc(),
|
||||
Interaction.interaction_at.desc()
|
||||
).all()
|
||||
|
||||
return [i.to_dict() for i in interactions]
|
||||
|
||||
|
||||
@router.get("/leads")
|
||||
async def list_leads(db: Session = Depends(get_db)):
|
||||
"""Listar interacciones marcadas como leads."""
|
||||
interactions = db.query(Interaction).filter(
|
||||
Interaction.is_lead == True
|
||||
).order_by(Interaction.interaction_at.desc()).all()
|
||||
|
||||
return [i.to_dict() for i in interactions]
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_interaction_stats(db: Session = Depends(get_db)):
|
||||
"""Obtener estadísticas de interacciones."""
|
||||
total = db.query(Interaction).count()
|
||||
pending = db.query(Interaction).filter(
|
||||
Interaction.responded == False,
|
||||
Interaction.is_archived == False
|
||||
).count()
|
||||
leads = db.query(Interaction).filter(Interaction.is_lead == True).count()
|
||||
|
||||
# Por plataforma
|
||||
by_platform = {}
|
||||
for platform in ["x", "threads", "instagram", "facebook"]:
|
||||
by_platform[platform] = db.query(Interaction).filter(
|
||||
Interaction.platform == platform
|
||||
).count()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"pending": pending,
|
||||
"leads": leads,
|
||||
"by_platform": by_platform
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{interaction_id}")
|
||||
async def get_interaction(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Obtener una interacción por ID."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
return interaction.to_dict()
|
||||
|
||||
|
||||
@router.post("/{interaction_id}/respond")
|
||||
async def respond_to_interaction(
|
||||
interaction_id: int,
|
||||
response_data: InteractionResponse,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Responder a una interacción."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
# TODO: Enviar respuesta a la plataforma correspondiente
|
||||
# from app.publishers import get_publisher
|
||||
# publisher = get_publisher(interaction.platform)
|
||||
# result = await publisher.reply(interaction.external_id, response_data.content)
|
||||
|
||||
interaction.responded = True
|
||||
interaction.response_content = response_data.content
|
||||
interaction.responded_at = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"message": "Respuesta guardada",
|
||||
"interaction_id": interaction_id,
|
||||
"note": "La publicación a la plataforma está en desarrollo"
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{interaction_id}/suggest-response")
|
||||
async def suggest_response(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Generar sugerencia de respuesta con IA."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
# TODO: Implementar generación con DeepSeek
|
||||
# from app.services.content_generator import generate_response_suggestion
|
||||
# suggestions = await generate_response_suggestion(interaction)
|
||||
|
||||
# Respuestas placeholder
|
||||
suggestions = [
|
||||
f"¡Gracias por tu comentario! Nos da gusto que te interese nuestro contenido.",
|
||||
f"¡Hola! Gracias por escribirnos. ¿En qué podemos ayudarte?",
|
||||
f"¡Excelente pregunta! Te respondemos por DM para darte más detalles."
|
||||
]
|
||||
|
||||
return {
|
||||
"suggestions": suggestions,
|
||||
"note": "La generación con IA está en desarrollo"
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{interaction_id}/mark-as-lead")
|
||||
async def mark_as_lead(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Marcar interacción como lead potencial."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
interaction.is_lead = not interaction.is_lead
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"message": f"{'Marcado' if interaction.is_lead else 'Desmarcado'} como lead",
|
||||
"is_lead": interaction.is_lead
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{interaction_id}/mark-as-read")
|
||||
async def mark_as_read(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Marcar interacción como leída."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
interaction.is_read = True
|
||||
db.commit()
|
||||
|
||||
return {"message": "Marcado como leído", "interaction_id": interaction_id}
|
||||
|
||||
|
||||
@router.post("/{interaction_id}/archive")
|
||||
async def archive_interaction(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Archivar una interacción."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
interaction.is_archived = True
|
||||
db.commit()
|
||||
|
||||
return {"message": "Interacción archivada", "interaction_id": interaction_id}
|
||||
|
||||
|
||||
@router.delete("/{interaction_id}")
|
||||
async def delete_interaction(interaction_id: int, db: Session = Depends(get_db)):
|
||||
"""Eliminar una interacción."""
|
||||
interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first()
|
||||
if not interaction:
|
||||
raise HTTPException(status_code=404, detail="Interacción no encontrada")
|
||||
|
||||
db.delete(interaction)
|
||||
db.commit()
|
||||
|
||||
return {"message": "Interacción eliminada", "interaction_id": interaction_id}
|
||||
Reference in New Issue
Block a user