First commit

This commit is contained in:
unknown
2026-06-09 21:18:13 -03:00
commit 5bff6b938b
66 changed files with 10922 additions and 0 deletions
+264
View File
@@ -0,0 +1,264 @@
"""
Mannage.py
Contiene los endpoints utilziados para adminitración o manejo de grupos o sesiones de telegram.
"""
from time import time
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from integrations.chats import TelegramChatSingleton
from dotenv import load_dotenv
import os
from typing import Dict, Optional
import requests
import asyncio
from integrations.api_implementations import post_api_sync, create_telegram_group
from telethon import TelegramClient
from telethon.sessions import StringSession
from telethon.errors import SessionPasswordNeededError
import httpx
import uuid
from auth import get_current_user
#Almacenes temporales
login_flows = {} # flow_id -> {"client": TelegramClient, "phone": str}
user_sessions = {} # token -> session_string
class VerifyCodeRequest(BaseModel):
flow_id: str
code: str
password: Optional[str] = None
class VerifyCodeResponse(BaseModel):
token: str
message: str
router = APIRouter()
load_dotenv()
API_BASE_URL = os.getenv('API_URL')
HEADERS = {"Content-Type": "application/json"}
TIMEOUT = 5.0
cache_status = {"connected": None, "last_check": 0}
TTL = 10 # segundos
router = APIRouter()
load_dotenv()
@router.post("/manage/", tags=['Manage'])
def add_pending_chat(channel: int, current_user: str = Depends(get_current_user)):
"""
Agrega un canal/grupo viendo si existe en telegram anteriormente.
Primero valida que el ID sea accesible en Telegram,
luego lo guarda con los datos reales (nombre, tipo).
"""
# 1. Verificar que el cliente de Telegram está disponible
try:
scraper = TelegramChatSingleton.get_scraper()
except Exception as e:
raise HTTPException(
status_code=503,
detail=f"El cliente de Telegram no está disponible: {str(e)}"
)
# 2. Validar el canal contra Telegram
try:
validation = scraper.validate_chat(channel)
except RuntimeError as e:
raise HTTPException(status_code=503, detail=str(e))
except Exception as e:
raise HTTPException(
status_code=503,
detail=f"Error al validar en Telegram: {str(e)}"
)
if not validation["valid"]:
raise HTTPException(
status_code=400,
detail=f"Canal no válido o inaccesible: {validation.get('error', 'desconocido')}"
)
info = validation["info"]
# 3. Guardar con datos reales de Telegram — sin pasar por estado pendiente
from integrations.api_implementations import refresh_telegram_group
tipo_interno = info["type"]
nombre = info["username"] if info["username"] != str(info["id_telegram"]) else info["title"] or str(info["id_telegram"])
# Intentar crear primero (caso nuevo)
new_group = {
"id_telegram": info["id_telegram"],
"name": nombre,
"description": info["description"] or info["title"] or "",
"type": tipo_interno,
"message_position": 0,
}
created_group = create_telegram_group(new_group)
if created_group:
return {
"status": "success",
"group": created_group,
"message": "Grupo validado y agregado correctamente",
"details": {
"title": info["title"],
"type": info["type"],
"id_telegram": info["id_telegram"],
}
}
# Si ya existía, actualizar sus datos con los de Telegram
update_data = {
"name": nombre,
"description": info["description"] or info["title"] or "",
"type": tipo_interno,
}
updated_group = refresh_telegram_group(info["id_telegram"], update_data)
if updated_group:
return {
"status": "updated",
"group": updated_group,
"message": "El grupo ya existía y fue actualizado con los datos de Telegram",
"details": {
"title": info["title"],
"type": info["type"],
"id_telegram": info["id_telegram"],
}
}
return {
"status": "exists",
"message": f"El grupo con ID {info['id_telegram']} ya existe en el sistema"
}
@router.get("/manage/connection-status", tags=['Manage'])
async def telegram_status():
ahora = time()
if ahora - cache_status["last_check"] < TTL and cache_status["connected"] is not None:
return {"connected": cache_status["connected"], "cached": True}
# Realizar la verificación real
try:
response = requests.get("https://api.telegram.org", timeout=2)
cache_status["connected"] = response.status_code == 200 or 302
except:
cache_status["connected"] = False
cache_status["last_check"] = ahora
return {"connected": cache_status["connected"], "cached": False}
@router.get("/manage/init-session", tags=['Manage'])
async def init_session(current_user: str = Depends(get_current_user)):
flow_id = str(uuid.uuid4())
session_dir = os.path.join(os.getcwd(), "telegram_sessions")
os.makedirs(session_dir, exist_ok=True)
# --- Limpiar sesiones anteriores de forma segura ---
active_session_files = {
f"{data['client'].session.filename}"
for data in login_flows.values()
if "client" in data
}
for filename in os.listdir(session_dir):
if not filename.endswith(".session"):
continue
filepath = os.path.join(session_dir, filename)
# Verificar que es un archivo real y no está en uso
if not os.path.isfile(filepath):
continue
if filepath in active_session_files:
print(f"[INFO] Sesión en uso, se omite: {filename}")
continue
try:
os.remove(filepath)
print(f"[INFO] Sesión eliminada: {filename}")
except Exception as e:
print(f"[WARN] No se pudo eliminar {filename}: {e}")
# Usar el flow_id como nombre de sesión para evitar conflictos entre flujos
session_file = os.path.join(session_dir, f"session_{flow_id}")
client = TelegramClient(
session_file,
int(os.getenv('TELEGRAM_API_ID')), # debe ser int
os.getenv('TELEGRAM_API_HASH')
)
# ✅ await directamente, nunca asyncio.run() dentro de async def
await client.connect()
if not await client.is_user_authorized():
try:
await client.send_code_request(phone=os.getenv('TELEGRAM_TELEPHONE'))
except Exception as e:
await client.disconnect()
raise HTTPException(status_code=400, detail=f"Error enviando código: {str(e)}")
login_flows[flow_id] = {
"client": client,
"phone": os.getenv('TELEGRAM_TELEPHONE')
}
return {
"flow_id": flow_id,
"message": "Código enviado. Usa /verify-code para completar la autenticación."
}
@router.post("/manage/verify-code", tags=['Manage'])
async def verify_code(request: VerifyCodeRequest, current_user: str = Depends(get_current_user)):
flow = login_flows.get(request.flow_id)
if not flow:
raise HTTPException(status_code=404, detail="Flow no encontrado o expirado")
client: TelegramClient = flow["client"]
phone = flow["phone"]
# Reconectar si el cliente se desconectó entre requests
if not client.is_connected():
await client.connect()
try:
await client.sign_in(phone=phone, code=request.code)
except SessionPasswordNeededError:
if not request.password:
raise HTTPException(
status_code=428,
detail="Se requiere contraseña de dos factores"
)
try:
await client.sign_in(password=request.password)
except Exception as e:
await client.disconnect()
del login_flows[request.flow_id]
raise HTTPException(status_code=400, detail=f"Error en contraseña 2FA: {str(e)}")
except Exception as e:
await client.disconnect()
del login_flows[request.flow_id]
raise HTTPException(status_code=400, detail=f"Error verificando código: {str(e)}")
# Éxito: guardar sesión y limpiar
session_string = client.session.save()
token = str(uuid.uuid4())
user_sessions[token] = session_string
await asyncio.sleep(1)
await client.disconnect()
del login_flows[request.flow_id]
# Limpiar la instancia singleton para que tome la nueva sesión
TelegramChatSingleton.cleanup()
#Inicializa scraper.
TelegramChatSingleton.get_scraper()
return {"token": token, "message": "Autenticación exitosa"}