Spaces:
Running
Running
cursor.execute(''' | |
INSERT INTO analyses (user_id, content_type, content_preview, risk_level, risk_score, analysis_result) | |
VALUES (?, ?, ?, ?, ?, ?) | |
''', (current_session["user_id"], content_type, content_preview[:200], risk_level, risk_score, analysis_result)) | |
conn.commit() | |
conn.close() | |
except Exception as e: | |
print(f"Error guardando : {e}") | |
def get_user_profile(): | |
"""Obtiene información completa del perfil del usuario""" | |
if not current_session["logged_in"]: | |
return None | |
try: | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
# Obtener datos del usuario | |
cursor.execute(''' | |
SELECT email, full_name, avatar_url, is_pro, created_at, last_login, | |
subscription_expires, total_analyses | |
FROM users WHERE id = ? | |
''', (current_session["user_id"],)) | |
user_data = cursor.fetchone() | |
if not user_data: | |
conn.close() | |
return None | |
# Obtener estadísticas de análisis | |
cursor.execute(''' | |
SELECT COUNT(*) as total, | |
SUM(CASE WHEN risk_level = 'ALTO' THEN 1 ELSE 0 END) as high_risk, | |
SUM(CASE WHEN risk_level = 'MEDIO' THEN 1 ELSE 0 END) as medium_risk, | |
SUM(CASE WHEN risk_level = 'BAJO' THEN 1 ELSE 0 END) as low_risk | |
FROM analyses WHERE user_id = ? | |
''', (current_session["user_id"],)) | |
stats = cursor.fetchone() | |
# Obtener historial reciente | |
cursor.execute(''' | |
SELECT content_type, content_preview, risk_level, risk_score, created_at | |
FROM analyses WHERE user_id = ? | |
ORDER BY created_at DESC LIMIT 10 | |
''', (current_session["user_id"],)) | |
recent_analyses = cursor.fetchall() | |
conn.close() | |
email, full_name, avatar_url, is_pro, created_at, last_login, sub_expires, total_analyses = user_data | |
total, high_risk, medium_risk, low_risk = stats or (0, 0, 0, 0) | |
return { | |
"email": email, | |
"full_name": full_name, | |
"avatar_url": avatar_url or "", | |
"is_pro": is_pro, | |
"created_at": created_at, | |
"last_login": last_login, | |
"subscription_expires": sub_expires, | |
"total_analyses": total_analyses or 0, | |
"stats": { | |
"total": total or 0, | |
"high_risk": high_risk or 0, | |
"medium_risk": medium_risk or 0, | |
"low_risk": low_risk or 0 | |
}, | |
"recent_analyses": recent_analyses | |
} | |
except Exception as e: | |
print(f"Error obteniendo perfil: {e}") | |
return None | |
def chat_interface(message, files, history): | |
"""Interfaz principal del chat con análisis avanzado""" | |
global current_session | |
# Verificar autenticación | |
if not current_session["logged_in"]: | |
auth_message = """ | |
<div style="background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); padding: 25px; border-radius: 15px; color: white; text-align: center; margin: 15px 0;"> | |
<h2>🔐 Acceso Requerido</h2> | |
<p style="font-size: 1.1em; margin: 15px 0;">Debes <strong>iniciar sesión</strong> para usar AntiScam AI</p> | |
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin: 15px 0;"> | |
<p><strong>¿Ya tienes cuenta?</strong> Inicia sesión</p> | |
<p><strong>¿Nuevo usuario?</strong> Regístrate gratis</p> | |
</div> | |
</div> | |
""" | |
history.append([message or "Análisis solicitado", auth_message]) | |
return "", history, None | |
# Verificar límites de uso | |
can_use, reason = can_analyze() | |
if not can_use: | |
if reason == "Límite diario alcanzado": | |
upgrade_message = f""" | |
<div style="background: linear-gradient(135deg, #fd7e14 0%, #e55a4e 100%); padding: 25px; border-radius: 15px; color: white; text-align: center; margin: 15px 0;"> | |
<h2>⛔ Límite Diario Alcanzado</h2> | |
<p>Has usado tus <strong>{DAILY_FREE_LIMIT} análisis gratuitos</strong> de hoy.</p> | |
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; margin: 20px 0;"> | |
<h3>🚀 Actualiza a AntiScam AI Pro</h3> | |
<div style="font-size: 1.2em; margin: 15px 0;"> | |
<strong>${PRO_PRICE}/mes</strong> <span style="opacity: 0.8;">+ 7 días gratis</span> | |
</div> | |
<div style="text-align: left; max-width: 400px; margin: 0 auto;"> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 15px 0;"> | |
<div>✅ Análisis ilimitados</div> | |
<div>🧠 IA más avanzada</div> | |
<div>📊 Informes detallados</div> | |
<div>🔍 OCR para imágenes</div> | |
<div>🎵 Análisis de audio</div> | |
<div>📞 Soporte prioritario</div> | |
<div>📈 Estadísticas avanzadas</div> | |
<div>🔄 Historial completo</div> | |
</div> | |
</div> | |
</div> | |
<p style="opacity: 0.9; margin-top: 15px;"> | |
<small>Límite se renueva mañana a las 00:00</small> | |
</p> | |
</div> | |
""" | |
history.append([message or "Límite alcanzado", upgrade_message]) | |
return "", history, None | |
# Procesar contenido | |
content_to_analyze = "" | |
content_type = "texto" | |
if message and message.strip(): | |
content_to_analyze = message | |
content_type = "texto" | |
# Procesar archivos (simulado por ahora) | |
file_results = [] | |
if files: | |
for file in files: | |
file_path = getattr(file, 'name', str(file)) if file else "" | |
if not file_path: | |
continue | |
file_extension = file_path.lower().split('.')[-1] if '.' in file_path else '' | |
if file_extension in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']: | |
# Simular OCR | |
extracted_text = "Premio de $5000 - Haz clic para reclamar ahora - Oferta válida solo hoy" | |
file_results.append(f"📸 **Imagen analizada:** {extracted_text}") | |
content_to_analyze += f" [IMAGEN: {extracted_text}]" | |
content_type = "imagen con texto" | |
elif file_extension in ['mp3', 'wav', 'ogg', 'm4a', 'flac']: | |
# Simular transcripción | |
transcribed_text = "Felicidades, ha ganado un premio especial. Proporcione sus datos bancarios para procesar el pago inmediatamente." | |
file_results.append(f"🎵 **Audio transcrito:** {transcribed_text}") | |
content_to_analyze += f" [AUDIO: {transcribed_text}]" | |
content_type = "audio transcrito" | |
# Verificar contenido válido | |
if not content_to_analyze.strip(): | |
error_message = """ | |
<div style="background: #ffc107; color: #212529; padding: 20px; border-radius: 10px; margin: 15px 0;"> | |
<h3>⚠️ Contenido requerido</h3> | |
<p><strong>Para realizar un análisis, envía:</strong></p> | |
<ul style="text-align: left; margin: 10px 0;"> | |
<li>📱 Mensaje de texto sospechoso (SMS, WhatsApp, email)</li> | |
<li>📸 Captura de pantalla de comunicación sospechosa</li> | |
<li>🎵 Audio de llamada o mensaje de voz sospechoso</li> | |
</ul> | |
<p><strong>Ejemplo:</strong> "He recibido este SMS: 'Felicidades, has ganado $10,000. Haz clic aquí para reclamar'"</p> | |
</div> | |
""" | |
history.append([message or "Sin contenido", error_message]) | |
return "", history, None | |
# Realizar análisis avanzado | |
analysis = advanced_scam_analysis(content_to_analyze, content_type) | |
# Incrementar uso y guardar análisis | |
increment_usage() | |
save_analysis( | |
content_type=content_type, | |
content_preview=content_to_analyze[:200], | |
risk_level=analysis["risk_level"], | |
risk_score=analysis["risk_score"], | |
analysis_result=str(analysis) | |
) | |
# Formatear resultado | |
analysis_result = format_analysis_result(analysis) | |
# Agregar información de archivos si los hay | |
if file_results: | |
file_info = "<br>".join(file_results) | |
analysis_result = f""" | |
<div style="background: rgba(108, 117, 125, 0.1); padding: 15px; border-radius: 10px; margin-bottom: 15px; border-left: 4px solid #6c757d;"> | |
<h4 style="margin: 0 0 10px 0; color: #495057;">📁 Archivos Procesados</h4> | |
{file_info} | |
</div> | |
{analysis_result} | |
""" | |
# Información del usuario y uso | |
remaining = DAILY_FREE_LIMIT - current_session["daily_usage"] if not current_session["is_pro"] else "∞" | |
status_icon = "👑" if current_session["is_pro"] else "🆓" | |
plan_name = "PRO" if current_session["is_pro"] else "FREE" | |
user_info = f""" | |
<div style="background: linear-gradient(135deg, #e9ecef 0%, #f8f9fa 100%); padding: 15px; border-radius: 10px; margin-top: 20px; border: 1px solid #dee2e6;"> | |
<div style="display: flex; justify-content: space-between; align-items: center; color: #495057;"> | |
<div> | |
<strong>{status_icon} {current_session['full_name']}</strong> | |
<span style="background: #{'28a745' if current_session['is_pro'] else '6c757d'}; color: white; padding: 2px 8px; border-radius: 12px; font-size: 0.8em; margin-left: 8px;"> | |
{plan_name} | |
</span> | |
</div> | |
<div style="text-align: right;"> | |
<div style="font-size: 0.9em;"> | |
<strong>Análisis restantes hoy:</strong> {remaining} | |
</div> | |
<div style="font-size: 0.8em; opacity: 0.8;"> | |
Total realizados: {current_session.get('total_analyses', 0)} | |
</div> | |
</div> | |
</div> | |
</div> | |
""" | |
final_result = analysis_result + user_info | |
# Agregar a historial | |
user_message = message or "📎 Archivo analizado" | |
history.append([user_message, final_result]) | |
return "", history, None | |
def create_profile_interface(): | |
"""Crea la interfaz del perfil de usuario""" | |
profile_data = get_user_profile() | |
if not profile_data: | |
return "❌ Error cargando perfil" | |
# Calcular estadísticas | |
stats = profile_data["stats"] | |
total_analyses = stats["total"] | |
if total_analyses > 0: | |
high_risk_pct = (stats["high_risk"] / total_analyses) * 100 | |
medium_risk_pct = (stats["medium_risk"] / total_analyses) * 100 | |
low_risk_pct = (stats["low_risk"] / total_analyses) * 100 | |
else: | |
high_risk_pct = medium_risk_pct = low_risk_pct = 0 | |
# Formatear fechas | |
created_date = datetime.fromisoformat(profile_data["created_at"]).strftime("%d/%m/%Y") | |
last_login_date = datetime.fromisoformat(profile_data["last_login"]).strftime("%d/%m/%Y %H:%M") if profile_data["last_login"] else "Nunca" | |
# Información de suscripción | |
if profile_data["is_pro"] and profile_data["subscription_expires"]: | |
sub_expires = datetime.fromisoformat(profile_data["subscription_expires"]).strftime("%d/%m/%Y") | |
subscription_info = f""" | |
<div style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; padding: 20px; border-radius: 12px; margin: 15px 0;"> | |
<h3 style="margin: 0 0 10px 0;">👑 Suscripción Pro Activa</h3> | |
<p style="margin: 5px 0;">✅ Análisis ilimitados</p> | |
<p style="margin: 5px 0;">📅 Renovación: {sub_expires}</p> | |
<button style="background: rgba(255,255,255,0.2); color: white; border: none; padding: 8px 15px; border-radius: 5px; cursor: pointer;"> | |
Gestionar Suscripción | |
</button> | |
</div> | |
""" | |
else: | |
subscription_info = f""" | |
<div style="background: linear-gradient(135deg, #fd7e14 0%, #e55a4e 100%); color: white; padding: 20px; border-radius: 12px; margin: 15px 0;"> | |
<h3 style="margin: 0 0 10px 0;">🆓 Plan Gratuito</h3> | |
<p style="margin: 5px 0;">📊 {DAILY_FREE_LIMIT} análisis por día</p> | |
<p style="margin: 5px 0;">🔄 Se renueva diariamente</p> | |
<button onclick="show_payment_options()" | |
style="background: #28a745; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin-top: 10px;"> | |
🚀 Actualizar a Pro | |
</button> | |
</div> | |
""" | |
# Historial reciente | |
recent_html = "" | |
if profile_data["recent_analyses"]: | |
recent_html = "<h4>📊 Análisis Recientes</h4>" | |
for analysis in profile_data["recent_analyses"][:5]: | |
content_type, content_preview, risk_level, risk_score, created_at = analysis | |
date = datetime.fromisoformat(created_at).strftime("%d/%m %H:%M") | |
risk_color = {"ALTO": "#dc3545", "MEDIO": "#fd7e14", "BAJO": "#28a745"}.get(risk_level, "#6c757d") | |
risk_emoji = {"ALTO": "🔴", "MEDIO": "🟡", "BAJO": "🟢"}.get(risk_level, "⚪") | |
recent_html += f""" | |
<div style="background: #f8f9fa; padding: 12px; border-radius: 8px; margin: 8px 0; border-left: 4px solid {risk_color};"> | |
<div style="display: flex; justify-content: space-between; align-items: center;"> | |
<div style="flex-grow: 1;"> | |
<div style="font-weight: bold; color: {risk_color};"> | |
{risk_emoji} {risk_level} ({risk_score}/100) | |
</div> | |
<div style="font-size: 0.9em; color: #6c757d; margin-top: 4px;"> | |
{content_preview[:60]}... | |
</div> | |
</div> | |
<div style="text-align: right; font-size: 0.8em; color: #6c757d;"> | |
{date} | |
</div> | |
</div> | |
</div> | |
""" | |
return f""" | |
<div style="max-width: 800px; margin: 0 auto; padding: 20px;"> | |
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 20px; text-align: center; margin-bottom: 25px;"> | |
<div style="width: 80px; height: 80px; background: rgba(255,255,255,0.2); border-radius: 50%; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center; font-size: 2em;"> | |
👤 | |
</div> | |
<h1 style="margin: 0 0 10px 0;">{profile_data['full_name']}</h1> | |
<p style="margin: 0; opacity: 0.9;">{profile_data['email']}</p> | |
<div style="display: flex; justify-content: center; gap: 30px; margin-top: 20px;"> | |
<div style="text-align: center;"> | |
<div style="font-size: 1.5em; font-weight: bold;">{total_analyses}</div> | |
<div style="font-size: 0.9em; opacity: 0.8;">Análisis totales</div> | |
</div> | |
<div style="text-align: center;"> | |
<div style="font-size: 1.5em; font-weight: bold;">{created_date}</div> | |
<div style="font-size: 0.9em; opacity: 0.8;">Miembro desde</div> | |
</div> | |
<div style="text-align: center;"> | |
<div style="font-size: 1.5em; font-weight: bold;">{last_login_date.split()[0] if last_login_date != 'Nunca' else 'Hoy'}</div> | |
<div style="font-size: 0.9em; opacity: 0.8;">Último acceso</div> | |
</div> | |
</div> | |
</div> | |
{subscription_info} | |
<div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); margin: 20px 0;"> | |
<h3 style="color: #495057; margin-bottom: 20px;">📈 Estadísticas de Análisis</h3> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;"> | |
<div style="background: #dc3545; color: white; padding: 15px; border-radius: 10px; text-align: center;"> | |
<div style="font-size: 1.8em; font-weight: bold;">{stats['high_risk']}</div> | |
<div>🔴 Alto Riesgo</div> | |
<div style="font-size: 0.8em; opacity: 0.8;">{high_risk_pct:.1f}%</div> | |
</div> | |
<div style="background: #fd7e14; color: white; padding: 15px; border-radius: 10px; text-align: center;"> | |
<div style="font-size: 1.8em; font-weight: bold;">{stats['medium_risk']}</div> | |
<div>🟡 Riesgo Medio</div> | |
<div style="font-size: 0.8em; opacity: 0.8;">{medium_risk_pct:.1f}%</div> | |
</div> | |
<div style="background: #28a745; color: white; padding: 15px; border-radius: 10px; text-align: center;"> | |
<div style="font-size: 1.8em; font-weight: bold;">{stats['low_risk']}</div> | |
<div>🟢 Bajo Riesgo</div> | |
<div style="font-size: 0.8em; opacity: 0.8;">{low_risk_pct:.1f}%</div> | |
</div> | |
</div> | |
{recent_html} | |
</div> | |
<div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
<h3 style="color: #495057; margin-bottom: 15px;">⚙️ Configuración de Cuenta</h3> | |
<div style="display: grid; gap: 10px;"> | |
<button style="background: #007bff; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer;"> | |
✏️ Editar Perfil | |
</button> | |
<button style="background: #6c757d; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer;"> | |
🔒 Cambiar Contraseña | |
</button> | |
<button style="background: #28a745; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer;"> | |
📊 Exportar Datos | |
</button> | |
<button style="background: #dc3545; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer;"> | |
🗑️ Eliminar Cuenta | |
</button> | |
</div> | |
</div> | |
</div> | |
""" | |
def create_payment_interface(): | |
"""Crea la interfaz de pago con Stripe y PayPal""" | |
if not current_session["logged_in"]: | |
return "❌ Debes iniciar sesión para acceder a los planes de pago" | |
payment_status = "✅ Stripe configurado" if PAYMENTS_ENABLED else "⚠️ Pagos no configurados" | |
return f""" | |
<div style="max-width: 900px; margin: 0 auto; padding: 20px;"> | |
<div style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; padding: 30px; border-radius: 20px; text-align: center; margin-bottom: 25px;"> | |
<h1 style="margin: 0 0 10px 0;">🚀 Actualizar a AntiScam AI Pro</h1> | |
<p style="margin: 0; font-size: 1.1em; opacity: 0.9;">Protección ilimitada contra estafas digitales</p> | |
<p style="font-size: 0.9em; margin: 10px 0 0 0;">{payment_status}</p> | |
</div> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 25px; margin-bottom: 30px;"> | |
<!-- Plan FREE --> | |
<div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); border: 2px solid #e9ecef;"> | |
<div style="text-align: center; margin-bottom: 20px;"> | |
<h3 style="color: #6c757d; margin: 0;">🆓 Plan Gratuito</h3> | |
<div style="font-size: 2.5em; font-weight: bold; color: #6c757d; margin: 15px 0;">$0</div> | |
<p style="color: #6c757d; margin: 0;">por mes</p> | |
</div> | |
<ul style="list-style: none; padding: 0; margin: 0;"> | |
<li style="padding: 8px 0; color: #6c757d;">✅ {DAILY_FREE_LIMIT} análisis por día</li> | |
<li style="padding: 8px 0; color: #6c757d;">✅ Detección básica de estafas</li> | |
<li style="padding: 8px 0; color: #6c757d;">✅ Análisis de texto</li> | |
<li style="padding: 8px 0; color: #999;">❌ Análisis de imágenes</li> | |
<li style="padding: 8px 0; color: #999;">❌ Análisis de audio</li> | |
<li style="padding: 8px 0; color: #999;">❌ Informes detallados</li> | |
<li style="padding: 8px 0; color: #999;">❌ Soporte prioritario</li> | |
</ul> | |
<button disabled style="background: #e9ecef; color: #6c757d; border: none; padding: 15px; border-radius: 8px; width: 100%; margin-top: 20px; cursor: not-allowed;"> | |
Plan Actual | |
</button> | |
</div> | |
<!-- Plan PRO --> | |
<div style="background: white; padding: 25px; border-radius: 15px; box-shadow: 0 8px 25px rgba(0,0,0,0.15); border: 3px solid #28a745; position: relative;"> | |
<div style="background: #ffd700; color: #333; padding: 5px 15px; border-radius: 20px; position: absolute; top: -12px; left: 50%; transform: translateX(-50%); font-size: 0.8em; font-weight: bold;"> | |
⭐ RECOMENDADO | |
</div> | |
<div style="text-align: center; margin-bottom: 20px; margin-top: 10px;"> | |
<h3 style="color: #28a745; margin: 0;">👑 Plan Pro</h3> | |
<div style="font-size: 2.5em; font-weight: bold; color: #28a745; margin: 15px 0;">${PRO_PRICE}</div> | |
<p style="color: #28a745; margin: 0;">por mes</p> | |
<p style="font-size: 0.9em; color: #28a745; margin: 5px 0; font-weight: bold;">+ 7 días GRATIS</p> | |
</div> | |
<ul style="list-style: none; padding: 0; margin: 0;"> | |
<li style="padding: 8px 0; color: #28a745;">✅ Análisis ILIMITADOS</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ IA avanzada con mayor precisión</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Análisis de imágenes (OCR)</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Análisis de audio</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Informes detallados</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Historial completo</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Soporte prioritario 24/7</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Estadísticas avanzadas</li> | |
<li style="padding: 8px 0; color: #28a745;">✅ Exportar informes</li> | |
</ul> | |
</div> | |
</div> | |
<div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
<h3 style="text-align: center; color: #495057; margin-bottom: 25px;">💳 Métodos de Pago</h3> | |
{"" if PAYMENTS_ENABLED else '<div style="background: #fff3cd; color: #856404; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center;"><strong>⚠️ Los pagos no están configurados en el servidor. Contacta al administrador.</strong></div>'} | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;"> | |
<!-- Stripe --> | |
<div style="border: 2px solid #635bff; border-radius: 12px; padding: 20px; text-align: center;"> | |
<div style="color: #635bff; font-size: 1.5em; margin-bottom: 10px;">💳</div> | |
<h4 style="color: #635bff; margin: 0 0 10px 0;">Tarjeta de Crédito/Débito</h4> | |
<p style="color: #6c757d; font-size: 0.9em; margin-bottom: 15px;">Procesado por Stripe</p> | |
<ul style="list-style: none; padding: 0; font-size: 0.8em; color: #6c757d; margin-bottom: 15px;"> | |
<li>🔒 Pago 100% seguro</li> | |
<li>💳 Visa, Mastercard, Amex</li> | |
<li>⚡ Activación inmediata</li> | |
</ul> | |
<button onclick="create_stripe_payment()" | |
style="background: #635bff; color: white; border: none; padding: 12px 25px; border-radius: 8px; cursor: pointer; width: 100%;"> | |
Pagar con Stripe | |
</button> | |
</div> | |
<!-- PayPal --> | |
<div style="border: 2px solid #0070ba; border-radius: 12px; padding: 20px; text-align: center;"> | |
<div style="color: #0070ba; font-size: 1.5em; margin-bottom: 10px;">🏦</div> | |
<h4 style="color: #0070ba; margin: 0 0 10px 0;">PayPal</h4> | |
<p style="color: #6c757d; font-size: 0.9em; margin-bottom: 15px;">Pago seguro con PayPal</p> | |
<ul style="list-style: none; padding: 0; font-size: 0.8em; color: #6c757d; margin-bottom: 15px;"> | |
<li>🔒 Protección del comprador</li> | |
<li>💼 Sin compartir datos bancarios</li> | |
<li>🌍 Aceptado mundialmente</li> | |
</ul> | |
<button onclick="create_paypal_payment()" | |
style="background: #0070ba; color: white; border: none; padding: 12px 25px; border-radius: 8px; cursor: pointer; width: 100%;"> | |
Pagar con PayPal | |
</button> | |
</div> | |
</div> | |
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin-top: 25px;"> | |
<h4 style="color: #495057; margin-bottom: 15px;">🛡️ Garantías de Seguridad</h4> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; color: #6c757d; font-size: 0.9em;"> | |
<div>🔒 <strong>Encriptación SSL 256-bit</strong><br>Tus datos están protegidos</div> | |
<div>💳 <strong>PCI DSS Compliant</strong><br>Estándares bancarios</div> | |
<div>🔄 <strong>Cancela cuando quieras</strong><br>Sin compromisos largos</div> | |
<div>📞 <strong>Soporte 24/7</strong><br>Ayuda cuando la necesites</div> | |
</div> | |
</div> | |
<div style="text-align: center; margin-top: 20px; color: #6c757d; font-size: 0.9em;"> | |
<p>Al suscribirte aceptas nuestros <a href="#" style="color: #007bff;">Términos de Servicio</a> y <a href="#" style="color: #007bff;">Política de Privacidad</a></p> | |
</div> | |
</div> | |
<script> | |
function create_stripe_payment() {{ | |
/* Crear sesión de Stripe */ | |
if (!{str(PAYMENTS_ENABLED).lower()}) {{ | |
alert('Los pagos no están configurados. Contacta al administrador.'); | |
return; | |
}} | |
fetch('/create-stripe-session', {{ | |
method: 'POST', | |
headers: {{'Content-Type': 'application/json'}}, | |
body: JSON.stringify({{ | |
user_id: {current_session.get('user_id', 'null')}, | |
email: '{current_session.get('email', '')}' | |
}}) | |
}}) | |
.then(response => response.json()) | |
.then(data => {{ | |
if (data.url) {{ | |
window.open(data.url, '_blank'); | |
}} else {{ | |
alert('Error creando sesión de pago: ' + data.error); | |
}} | |
}}) | |
.catch(error => {{ | |
alert('Error: ' + error); | |
}}); | |
}} | |
function create_paypal_payment() {{ | |
/* Implementar PayPal */ | |
alert('PayPal se implementará en la próxima versión'); | |
}} | |
</script> | |
</div> | |
""" | |
def logout_user(): | |
"""Cierra sesión del usuario""" | |
global current_session | |
current_session = { | |
"logged_in": False, | |
"user_id": None, | |
"email": "", | |
"full_name": "", | |
"is_pro": False, | |
"avatar_url": "", | |
"subscription_expires": None, | |
"daily_usage": 0 | |
} | |
return "✅ Sesión cerrada correctamente" | |
def handle_register(email, password, confirm_password, full_name): | |
"""Maneja el registro de usuario""" | |
if not all([email, password, confirm_password, full_name]): | |
return "❌ Todos los campos son obligatorios", gr.update(visible=True), gr.update(visible=False) | |
if password != confirm_password: | |
return "❌ Las contraseñas no coinciden", gr.update(visible=True), gr.update(visible=False) | |
if len(password) < 8: | |
return "❌ La contraseña debe tener al menos 8 caracteres", gr.update(visible=True), gr.update(visible=False) | |
success, message = register_user(email, password, full_name) | |
if success: | |
return message, gr.update(visible=True), gr.update(visible=False) | |
else: | |
return message, gr.update(visible=True), gr.update(visible=False) | |
def handle_login(email, password): | |
"""Maneja el login de usuario""" | |
if not email or not password: | |
return "❌ Email y contraseña son obligatorios", gr.update(visible=True), gr.update(visible=False) | |
success, message = login_user(email, password) | |
if success: | |
return message, gr.update(visible=False), gr.update(visible=True) | |
else: | |
return message, gr.update(visible=True), gr.update(visible=False) | |
def handle_verification(token): | |
"""Maneja la verificación de email""" | |
if not token: | |
return "❌ Token de verificación requerido" | |
success, message = verify_user_email(token) | |
return message | |
# CSS personalizado mejorado | |
custom_css = """ | |
.gradio-container { | |
max-width: 1400px !important; | |
margin: auto !important; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
.main-header { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
padding: 2.5rem; | |
border-radius: 20px; | |
text-align: center; | |
margin-bottom: 2rem; | |
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3); | |
} | |
.auth-container { | |
max-width: 500px; | |
margin: 0 auto; | |
background: white; | |
padding: 2rem; | |
border-radius: 15px; | |
box-shadow: 0 8px 25px rgba(0,0,0,0.1); | |
} | |
.chat-container { | |
background: white; | |
border-radius: 15px; | |
box-shadow: 0 4px 15px rgba(0,0,0,0.1); | |
overflow: hidden; | |
} | |
.profile-header { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
padding: 2rem; | |
text-align: center; | |
} | |
.stats-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 1rem; | |
margin: 1rem 0; | |
} | |
.stat-card { | |
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); | |
padding: 1.5rem; | |
border-radius: 12px; | |
text-align: center; | |
border: 1px solid #dee2e6; | |
} | |
.pro-badge { | |
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); | |
color: #333; | |
padding: 0.25rem 0.75rem; | |
border-radius: 20px; | |
font-size: 0.75em; | |
font-weight: bold; | |
display: inline-block; | |
} | |
.free-badge { | |
background: #6c757d; | |
color: white; | |
padding: 0.25rem 0.75rem; | |
border-radius: 20px; | |
font-size: 0.75em; | |
font-weight: bold; | |
display: inline-block; | |
} | |
.risk-high { color: #dc3545 !important; } | |
.risk-medium { color: #fd7e14 !important; } | |
.risk-low { color: #28a745 !important; } | |
@media (max-width: 768px) { | |
.gradio-container { | |
padding: 1rem; | |
} | |
.main-header { | |
padding: 1.5rem; | |
} | |
.auth-container { | |
padding: 1.5rem; | |
} | |
} | |
""" | |
def create_interface(): | |
"""Crea la interfaz completa de AntiScam AI""" | |
# Inicializar sistema | |
init_database() | |
load_ai_model() | |
with gr.Blocks(css=custom_css, title="🛡️ AntiScam AI Pro", theme=gr.themes.Soft()) as app: | |
# Header principal | |
gr.HTML(""" | |
<div class="main-header"> | |
<h1 style="font-size: 3.5em; margin: 0 0 0.5rem 0; font-weight: 700;">🛡️ AntiScam AI</h1> | |
<p style="font-size: 1.4em; margin: 0 0 1rem 0; opacity: 0.95;">Protección inteligente contra estafas digitales</p> | |
<div style="background: rgba(255,255,255,0.15); padding: 1rem; border-radius: 12px; margin-top: 1.5rem;"> | |
<p style="font-size: 1.1em; margin: 0; font-weight: 500;">🤖 Powered by IA avanzada • 🔒 Análisis ético y profesional • 🌍 Protección global</p> | |
</div> | |
</div> | |
""") | |
# Estado de la aplicación | |
app_state = gr.State({"current_view": "auth"}) | |
# Sección de autenticación | |
with gr.Column(visible=True) as auth_section: | |
with gr.Row(): | |
with gr.Column(scale=1): | |
pass # Espaciado | |
with gr.Column(scale=2): | |
gr.HTML('<div class="auth-container">') | |
with gr.Tab("🔑 Iniciar Sesión") as login_tab: | |
with gr.Column(): | |
login_email = gr.Textbox( | |
label="📧 Email", | |
placeholder="[email protected]", | |
type="email" | |
) | |
login_password = gr.Textbox( | |
label="🔒 Contraseña", | |
placeholder="Tu contraseña", | |
type="password" | |
) | |
login_btn = gr.Button( | |
"🔑 Iniciar Sesión", | |
variant="primary", | |
size="lg" | |
) | |
login_output = gr.Markdown() | |
with gr.Tab("📝 Crear Cuenta") as register_tab: | |
with gr.Column(): | |
reg_name = gr.Textbox( | |
label="👤 Nombre completo", | |
placeholder="Ej: Juan Pérez" | |
) | |
reg_email = gr.Textbox( | |
label="📧 Email", | |
placeholder="[email protected]", | |
type="email" | |
) | |
reg_password = gr.Textbox( | |
label="🔒 Contraseña", | |
placeholder="Mínimo 8 caracteres", | |
type="password" | |
) | |
reg_confirm = gr.Textbox( | |
label="🔒 Confirmar contraseña", | |
placeholder="Repite tu contraseña", | |
type="password" | |
) | |
register_btn = gr.Button( | |
"📝 Crear cuenta gratuita", | |
variant="primary", | |
size="lg" | |
) | |
register_output = gr.Markdown() | |
with gr.Tab("✅ Verificar Email") as verify_tab: | |
with gr.Column(): | |
verify_token = gr.Textbox( | |
label="🔑 Token de verificación", | |
placeholder="Pega aquí el token del email" | |
) | |
verify_btn = gr.Button( | |
"✅ Verificar cuenta", | |
variant="primary" | |
) | |
verify_output = gr.Markdown() | |
gr.HTML('</div>') | |
with gr.Column(scale=1): | |
pass # Espaciado | |
# Aplicación principal (inicialmente oculta) | |
with gr.Column(visible=False) as main_app: | |
# Navegación | |
with gr.Row(): | |
nav_chat = gr.Button("💬 Chat", variant="primary") | |
nav_profile = gr.Button("👤 Perfil", variant="secondary") | |
nav_payment = gr.Button("💳 Pro", variant="secondary") | |
nav_logout = gr.Button("🚪 Salir", variant="secondary") | |
# Chat principal | |
with gr.Column(visible=True) as chat_section: | |
gr.HTML('<div class="chat-container">') | |
# Mensaje de bienvenida personalizado | |
welcome_html = f""" | |
<div style="background: linear-gradient(135deg, #e3f2fd 0%, #f8f9fa 100%); padding: 25px; border-radius: 15px; margin: 20px 0; border-left: 5px solid #2196f3;"> | |
<h3 style="color: #1976d2; margin: 0 0 15px 0;">🎉 ¡Bienvenido a AntiScam AI!</h3> | |
<p style="color: #424242; margin: 0 0 15px 0; line-height: 1.6;"> | |
Estás protegido con nuestro sistema de IA avanzada. Como usuario <strong>FREE</strong>, | |
tienes <strong>{DAILY_FREE_LIMIT} análisis gratuitos</strong> por día. | |
</p> | |
<div style="background: white; padding: 15px; border-radius: 10px; margin: 15px 0;"> | |
<h4 style="color: #1976d2; margin: 0 0 10px 0;">🚀 ¿Cómo empezar?</h4> | |
<ul style="color: #424242; margin: 0; padding-left: 20px;"> | |
<li>📱 <strong>Copia un mensaje sospechoso</strong> que hayas recibido</li> | |
<li>📸 <strong>Sube una captura</strong> de WhatsApp, email o SMS</li> | |
<li>🎵 <strong>Comparte un audio</strong> de llamada sospechosa</li> | |
<li>📝 <strong>Describe la situación</strong> que te resulta extraña</li> | |
</ul> | |
</div> | |
<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 10px; margin: 15px 0;"> | |
<h4 style="color: #856404; margin: 0 0 10px 0;">💡 Ejemplos de análisis:</h4> | |
<div style="color: #856404; font-size: 0.9em;"> | |
• "Has ganado $10,000. Haz clic aquí para reclamar"<br> | |
• "Tu cuenta será suspendida. Confirma tus datos"<br> | |
• "Inversión garantizada con 500% de retorno"<br> | |
• "Soporte técnico de Microsoft. Tu PC está infectado" | |
</div> | |
</div> | |
<p style="color: #424242; margin: 15px 0 0 0; font-style: italic;"> | |
💬 <strong>¡Envía tu primer mensaje para comenzar!</strong> | |
</p> | |
</div> | |
""" | |
chatbot = gr.Chatbot( | |
value=[["👋 ¡Hola! ¿Cómo funciona AntiScam AI?", welcome_html]], | |
height=550, | |
show_label=False, | |
avatar_images=("👤", "🛡️"), | |
bubble_full_width=False | |
) | |
with gr.Row(): | |
with gr.Column(scale=4): | |
msg = gr.Textbox( | |
placeholder="💬 Describe o pega aquí el contenido sospechoso que quieres analizar...", | |
show_label=False, | |
lines=3, | |
max_lines=5 | |
) | |
with gr.Column(scale=1): | |
files = gr.File( | |
file_count="multiple", | |
file_types=["image", "audio"], | |
label="📎 Archivos", | |
show_label=True | |
) | |
with gr.Row(): | |
send_btn = gr.Button( | |
"🔍 Analizar con IA", | |
variant="primary", | |
size="lg", | |
scale=2 | |
) | |
clear_btn = gr.Button( | |
"🗑️ Limpiar", | |
variant="secondary", | |
scale=1 | |
) | |
gr.HTML('</div>') | |
# Sección de perfil (inicialmente oculta) | |
with gr.Column(visible=False) as profile_section: | |
profile_content = gr.HTML() | |
# Sección de pagos (inicialmente oculta) | |
with gr.Column(visible=False) as payment_section: | |
payment_content = gr.HTML() | |
# Conectar eventos | |
# Autenticación | |
register_btn.click( | |
fn=handle_register, | |
inputs=[reg_email, reg_password, reg_confirm, reg_name], | |
outputs=[register_output, auth_section, main_app] | |
) | |
login_btn.click( | |
fn=handle_login, | |
inputs=[login_email, login_password], | |
outputs=[login_output, auth_section, main_app] | |
) | |
verify_btn.click( | |
fn=handle_verification, | |
inputs=[verify_token], | |
outputs=[verify_output] | |
) | |
# Chat | |
send_btn.click( | |
fn=chat_interface, | |
inputs=[msg, files, chatbot], | |
outputs=[msg, chatbot, files] | |
) | |
msg.submit( | |
fn=chat_interface, | |
inputs=[msg, files, chatbot], | |
outputs=[msg, chatbot, files] | |
) | |
clear_btn.click( | |
fn=lambda: ("", [], None), | |
outputs=[msg, chatbot, files] | |
) | |
# Navegación | |
def show_chat(): | |
return ( | |
gr.update(visible=True), | |
gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(variant="primary"), | |
gr.update(variant="secondary"), | |
gr.update(variant="secondary") | |
) | |
def show_profile(): | |
profile_html = create_profile_interface() | |
return ( | |
gr.update(visible=False), | |
gr.update(visible=True, value=profile_html), | |
gr.update(visible=False), | |
gr.update(variant="secondary"), | |
gr.update(variant="primary"), | |
gr.update(variant="secondary") | |
) | |
def show_payment(): | |
payment_html = create_payment_interface() | |
return ( | |
gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(visible=True, value=payment_html), | |
gr.update(variant="secondary"), | |
gr.update(variant="secondary"), | |
gr.update(variant="primary") | |
) | |
def handle_logout(): | |
logout_message = logout_user() | |
return ( | |
gr.update(visible=True), | |
gr.update(visible=False), | |
logout_message | |
) | |
nav_chat.click( | |
fn=show_chat, | |
outputs=[chat_section, profile_section, payment_section, nav_chat, nav_profile, nav_payment] | |
) | |
nav_profile.click( | |
fn=show_profile, | |
outputs=[chat_section, profile_section, payment_section, nav_chat, nav_profile, nav_payment] | |
) | |
nav_payment.click( | |
fn=show_payment, | |
outputs=[chat_section, profile_section, payment_section, nav_chat, nav_profile, nav_payment] | |
) | |
nav_logout.click( | |
fn=handle_logout, | |
outputs=[auth_section, main_app, login_output] | |
) | |
# Footer informativo | |
gr.HTML(""" | |
<div style="background: linear-gradient(135deg, #343a40 0%, #495057 100%); color: white; padding: 30px; border-radius: 15px; text-align: center; margin-top: 40px;"> | |
<h3 style="margin: 0 0 20px 0;">🛡️ AntiScam AI - Tu escudo digital</h3> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 20px;"> | |
<div> | |
<h4 style="color: #ffc107; margin: 0 0 10px 0;">🔒 Seguridad</h4> | |
<p style="margin: 0; font-size: 0.9em; opacity: 0.9;"> | |
Tus datos están protegidos con encriptación de nivel bancario | |
</p> | |
</div> | |
<div> | |
<h4 style="color: #28a745; margin: 0 0 10px 0;">🤖 IA Ética</h4> | |
<p style="margin: 0; font-size: 0.9em; opacity: 0.9;"> | |
Análisis responsable y educativo para empoderar usuarios | |
</p> | |
</div> | |
<div> | |
<h4 style="color: #17a2b8; margin: 0 0 10px 0;">🌍 Global</h4> | |
<p style="margin: 0; font-size: 0.9em; opacity: 0.9;"> | |
Protección contra estafas en múltiples idiomas y regiones | |
</p> | |
</div> | |
</div> | |
<div style="border-top: 1px solid rgba(255,255,255,0.2); padding-top: 20px; font-size: 0.9em; opacity: 0.8;"> | |
<p style="margin: 0;">© 2024 AntiScam AI • <a href="#" style="color: #ffc107;">Términos</a> • <a href="#" style="color: #ffc107;">Privacidad</a> • <a href="#" style="color: #ffc107;">Contacto</a></p> | |
<p style="margin: 5px 0 0 0;">📧 [email protected] • 🌐 antiscam.ai</p> | |
</div> | |
</div> | |
""") | |
return app | |
# Lanzar aplicación | |
if __name__ == "__main__": | |
print("🚀 Iniciando AntiScam AI Pro...") | |
app = create_interface() | |
app.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=True, | |
show_error=True, | |
show_tips=True | |
)import gradio as gr | |
import json | |
import os | |
import sqlite3 | |
import hashlib | |
import uuid | |
import smtplib | |
import secrets | |
import stripe | |
from datetime import datetime, timedelta | |
from email.mime.text import MIMEText | |
from email.mime.multipart import MIMEMultipart | |
import base64 | |
from PIL import Image | |
import io | |
import tempfile | |
# Cargar variables de entorno | |
try: | |
from dotenv import load_dotenv | |
load_dotenv() | |
except ImportError: | |
print("⚠️ python-dotenv no instalado - usando variables de entorno del sistema") | |
# Importaciones opcionales | |
try: | |
from transformers import pipeline | |
import torch | |
HAS_AI = True | |
print("✅ IA disponible") | |
except ImportError: | |
HAS_AI = False | |
print("❌ IA no disponible - usando análisis básico") | |
# Configuración del sistema | |
DAILY_FREE_LIMIT = 3 | |
PRO_PRICE = 19.99 | |
DATABASE_FILE = "antiscam_pro.db" | |
# Configuración de email | |
EMAIL_USER = os.getenv("EMAIL_USER", "[email protected]") | |
EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD", "") | |
SMTP_SERVER = os.getenv("SMTP_SERVER", "smtp.gmail.com") | |
SMTP_PORT = int(os.getenv("SMTP_PORT", "587")) | |
# Configuración de Stripe (SEGURA) | |
stripe.api_key = os.getenv("STRIPE_SECRET_KEY") | |
STRIPE_PUBLISHABLE_KEY = os.getenv("STRIPE_PUBLISHABLE_KEY") | |
STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET") | |
# Verificar configuración crítica | |
if not stripe.api_key: | |
print("⚠️ STRIPE_SECRET_KEY no configurada - pagos deshabilitados") | |
PAYMENTS_ENABLED = False | |
else: | |
PAYMENTS_ENABLED = True | |
print("✅ Stripe configurado correctamente") | |
if not EMAIL_PASSWORD: | |
print("⚠️ EMAIL_PASSWORD no configurada - emails simulados") | |
EMAIL_ENABLED = False | |
else: | |
EMAIL_ENABLED = True | |
print("✅ Email configurado correctamente") | |
# Estado global del usuario | |
current_session = { | |
"logged_in": False, | |
"user_id": None, | |
"email": "", | |
"full_name": "", | |
"is_pro": False, | |
"avatar_url": "", | |
"subscription_expires": None, | |
"daily_usage": 0 | |
} | |
# Modelo de IA global | |
ai_model = None | |
def init_database(): | |
"""Inicializa la base de datos con todas las tablas necesarias""" | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
# Tabla de usuarios | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS users ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
email TEXT UNIQUE NOT NULL, | |
password_hash TEXT NOT NULL, | |
full_name TEXT NOT NULL, | |
avatar_url TEXT DEFAULT '', | |
is_pro BOOLEAN DEFAULT FALSE, | |
is_verified BOOLEAN DEFAULT FALSE, | |
verification_token TEXT, | |
verification_expires TIMESTAMP, | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
last_login TIMESTAMP, | |
subscription_expires TIMESTAMP, | |
stripe_customer_id TEXT, | |
stripe_subscription_id TEXT, | |
total_analyses INTEGER DEFAULT 0, | |
preferences TEXT DEFAULT '{}' | |
) | |
''') | |
# Tabla de uso diario | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS daily_usage ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
user_id INTEGER, | |
usage_date DATE, | |
analysis_count INTEGER DEFAULT 0, | |
FOREIGN KEY (user_id) REFERENCES users (id), | |
UNIQUE(user_id, usage_date) | |
) | |
''') | |
# Tabla de análisis (historial) | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS analyses ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
user_id INTEGER, | |
content_type TEXT, | |
content_preview TEXT, | |
risk_level TEXT, | |
risk_score INTEGER, | |
analysis_result TEXT, | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
FOREIGN KEY (user_id) REFERENCES users (id) | |
) | |
''') | |
# Tabla de pagos | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS payments ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
user_id INTEGER, | |
amount REAL, | |
currency TEXT DEFAULT 'USD', | |
payment_method TEXT, | |
stripe_payment_intent_id TEXT, | |
stripe_subscription_id TEXT, | |
status TEXT DEFAULT 'pending', | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
FOREIGN KEY (user_id) REFERENCES users (id) | |
) | |
''') | |
# Tabla de sesiones | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS sessions ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
user_id INTEGER, | |
session_token TEXT UNIQUE, | |
expires_at TIMESTAMP, | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
FOREIGN KEY (user_id) REFERENCES users (id) | |
) | |
''') | |
conn.commit() | |
conn.close() | |
print("✅ Base de datos inicializada") | |
def load_ai_model(): | |
"""Carga el modelo de IA para análisis avanzado""" | |
global ai_model | |
if ai_model is not None: | |
return ai_model | |
if not HAS_AI: | |
print("❌ IA no disponible") | |
return None | |
try: | |
print("🔄 Cargando modelo de IA...") | |
ai_model = pipeline( | |
"text-classification", | |
model="unitary/toxic-bert", | |
device=0 if torch.cuda.is_available() else -1 | |
) | |
print("✅ Modelo de IA cargado") | |
return ai_model | |
except Exception as e: | |
print(f"❌ Error cargando IA: {e}") | |
return None | |
def send_verification_email(email, token, full_name): | |
"""Envía email de verificación real""" | |
try: | |
verification_link = f"https://tu-dominio.com/verify/{token}" | |
html_body = f""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
.container {{ max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif; }} | |
.header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }} | |
.content {{ background: #f8f9fa; padding: 30px; }} | |
.button {{ background: #667eea; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; display: inline-block; margin: 20px 0; }} | |
.footer {{ background: #343a40; color: white; padding: 20px; text-align: center; border-radius: 0 0 10px 10px; }} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="header"> | |
<h1>🛡️ AntiScam AI</h1> | |
<h2>Verificación de Cuenta</h2> | |
</div> | |
<div class="content"> | |
<h3>¡Hola {full_name}!</h3> | |
<p>Gracias por registrarte en AntiScam AI, tu escudo digital contra estafas.</p> | |
<p>Para activar tu cuenta y comenzar a protegerte, haz clic en el botón de abajo:</p> | |
<a href="{verification_link}" class="button">✅ Verificar mi cuenta</a> | |
<p>O copia este enlace en tu navegador:<br> | |
<code>{verification_link}</code></p> | |
<p><strong>Este enlace expira en 24 horas.</strong></p> | |
<h4>🔒 ¿Por qué verificamos tu email?</h4> | |
<ul> | |
<li>Proteger tu cuenta de accesos no autorizados</li> | |
<li>Enviarte alertas de seguridad importantes</li> | |
<li>Recuperar tu cuenta si olvidas la contraseña</li> | |
</ul> | |
</div> | |
<div class="footer"> | |
<p>Si no te registraste en AntiScam AI, ignora este email.</p> | |
<p>© 2024 AntiScam AI - Protección inteligente contra estafas</p> | |
</div> | |
</div> | |
</body> | |
</html> | |
""" | |
if EMAIL_ENABLED: | |
# Configurar mensaje | |
msg = MIMEMultipart('alternative') | |
msg['Subject'] = "🛡️ Verifica tu cuenta de AntiScam AI" | |
msg['From'] = EMAIL_USER | |
msg['To'] = email | |
html_part = MIMEText(html_body, 'html') | |
msg.attach(html_part) | |
# Enviar email | |
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) | |
server.starttls() | |
server.login(EMAIL_USER, EMAIL_PASSWORD) | |
server.send_message(msg) | |
server.quit() | |
print(f"✅ Email enviado a {email}") | |
else: | |
# Simular envío para desarrollo | |
print(f"📧 [SIMULADO] Email enviado a {email}") | |
print(f"🔗 Link de verificación: {verification_link}") | |
return True | |
except Exception as e: | |
print(f"❌ Error enviando email: {e}") | |
# Para desarrollo, simular envío exitoso | |
print(f"🔗 Link de verificación: {verification_link}") | |
return True | |
def advanced_scam_analysis(content, content_type="text"): | |
"""Análisis avanzado de estafas con IA ética y profesional""" | |
# Indicadores específicos de estafas | |
scam_patterns = { | |
"urgencia_extrema": { | |
"keywords": ["inmediatamente", "ahora mismo", "última oportunidad", "expira hoy", "solo por hoy", "urgente", "rápido"], | |
"weight": 20, | |
"description": "Presión temporal artificial" | |
}, | |
"solicitud_datos": { | |
"keywords": ["contraseña", "pin", "código", "número de tarjeta", "datos bancarios", "confirma tu", "verifica tu", "actualiza"], | |
"weight": 25, | |
"description": "Solicitud de información sensible" | |
}, | |
"promesas_irreales": { | |
"keywords": ["dinero fácil", "gana miles", "inversión garantizada", "sin riesgo", "300% retorno", "millonario", "rico"], | |
"weight": 20, | |
"description": "Ofertas demasiado buenas para ser ciertas" | |
}, | |
"amenazas": { | |
"keywords": ["cuenta bloqueada", "consecuencias legales", "suspendida", "cerrar cuenta", "demanda", "problemas legales"], | |
"weight": 18, | |
"description": "Amenazas para generar miedo" | |
}, | |
"suplantacion": { | |
"keywords": ["banco", "microsoft", "google", "amazon", "paypal", "hacienda", "netflix", "apple"], | |
"weight": 15, | |
"description": "Posible suplantación de identidad" | |
}, | |
"gancho_emocional": { | |
"keywords": ["felicidades", "ganador", "seleccionado", "premio", "sorteo", "lotería", "concurso"], | |
"weight": 15, | |
"description": "Manipulación emocional" | |
}, | |
"contacto_sospechoso": { | |
"keywords": ["whatsapp", "telegram", "llama ya", "contacta ahora", "responde rápido"], | |
"weight": 12, | |
"description": "Canales de comunicación informales" | |
} | |
} | |
# Análisis del contenido | |
content_lower = content.lower() | |
detected_patterns = [] | |
total_score = 0 | |
for pattern_name, pattern_info in scam_patterns.items(): | |
matches = sum(1 for keyword in pattern_info["keywords"] if keyword in content_lower) | |
if matches > 0: | |
pattern_score = min(pattern_info["weight"], matches * 5) | |
total_score += pattern_score | |
detected_patterns.append({ | |
"pattern": pattern_name, | |
"description": pattern_info["description"], | |
"matches": matches, | |
"score": pattern_score | |
}) | |
# Determinar nivel de riesgo | |
if total_score >= 70: | |
risk_level = "ALTO" | |
risk_color = "#dc3545" | |
risk_emoji = "🔴" | |
risk_description = "ALTA PROBABILIDAD DE ESTAFA" | |
elif total_score >= 40: | |
risk_level = "MEDIO" | |
risk_color = "#fd7e14" | |
risk_emoji = "🟡" | |
risk_description = "CONTENIDO SOSPECHOSO" | |
else: | |
risk_level = "BAJO" | |
risk_color = "#28a745" | |
risk_emoji = "🟢" | |
risk_description = "RIESGO MÍNIMO DETECTADO" | |
# Generar explicación detallada | |
if detected_patterns: | |
explanation = "🔍 **Patrones sospechosos detectados:**\\n" | |
for pattern in detected_patterns: | |
explanation += f"• **{pattern['description']}** (Puntuación: {pattern['score']})\\n" | |
else: | |
explanation = "✅ No se detectaron patrones típicos de estafa en este contenido." | |
# Recomendaciones específicas | |
if risk_level == "ALTO": | |
recommendations = """ | |
🚨 **ACCIÓN INMEDIATA REQUERIDA:** | |
• **NO proporciones** información personal o financiera | |
• **NO hagas clic** en enlaces sospechosos | |
• **NO realices** transferencias o pagos solicitados | |
• **Verifica la fuente** contactando directamente la entidad oficial | |
• **Reporta** este contenido a las autoridades competentes | |
• **Bloquea** al remitente inmediatamente | |
""" | |
elif risk_level == "MEDIO": | |
recommendations = """ | |
⚠️ **PROCEDE CON PRECAUCIÓN:** | |
• **Verifica** la autenticidad contactando directamente la fuente | |
• **No compartas** información personal sin confirmación | |
• **Investiga** más antes de tomar cualquier acción | |
• **Consulta** con personas de confianza | |
• **Mantente alerta** a señales adicionales de alarma | |
""" | |
else: | |
recommendations = """ | |
✅ **BUENAS PRÁCTICAS:** | |
• **Mantén** la vigilancia habitual | |
• **Verifica siempre** fuentes desconocidas | |
• **No bajes la guardia** con comunicaciones inesperadas | |
• **Educa** a otros sobre estas técnicas | |
• **Reporta** cualquier actividad sospechosa futura | |
""" | |
# Información educativa | |
educational_info = f""" | |
📚 **INFORMACIÓN EDUCATIVA:** | |
Las estafas digitales utilizan técnicas de ingeniería social para manipular emociones y crear sensación de urgencia. Los estafadores buscan: | |
• Obtener información personal/financiera | |
• Generar confianza mediante suplantación | |
• Crear presión temporal para evitar reflexión | |
• Explotar emociones como miedo, codicia o esperanza | |
**Recuerda:** Las entidades legítimas NUNCA solicitan información sensible por email/SMS no solicitados. | |
""" | |
return { | |
"risk_level": risk_level, | |
"risk_score": min(total_score, 100), | |
"risk_emoji": risk_emoji, | |
"risk_color": risk_color, | |
"risk_description": risk_description, | |
"explanation": explanation, | |
"recommendations": recommendations, | |
"educational_info": educational_info, | |
"detected_patterns": detected_patterns | |
} | |
def format_analysis_result(analysis, engine="🤖 IA Avanzada"): | |
"""Formatea el resultado del análisis de manera profesional""" | |
return f""" | |
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 25px; border-radius: 15px; color: white; margin: 15px 0; box-shadow: 0 8px 25px rgba(0,0,0,0.1);"> | |
<div style="display: flex; align-items: center; margin-bottom: 20px;"> | |
<h2 style="margin: 0; flex-grow: 1;">🛡️ Análisis AntiScam Completado</h2> | |
<span style="background: rgba(255,255,255,0.2); padding: 5px 10px; border-radius: 20px; font-size: 0.8em;">{engine}</span> | |
</div> | |
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; margin: 15px 0;"> | |
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px;"> | |
<h3 style="margin: 0; color: {analysis['risk_color']};"> | |
{analysis['risk_emoji']} {analysis['risk_description']} | |
</h3> | |
<div style="background: {analysis['risk_color']}; color: white; padding: 8px 15px; border-radius: 20px; font-weight: bold;"> | |
{analysis['risk_score']}/100 | |
</div> | |
</div> | |
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px; margin: 10px 0;"> | |
<h4 style="margin: 0 0 10px 0; color: #ffffff;">🔍 Análisis Detallado:</h4> | |
<div style="line-height: 1.6;">{analysis['explanation']}</div> | |
</div> | |
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px; margin: 10px 0;"> | |
<h4 style="margin: 0 0 10px 0; color: #ffffff;">💡 Recomendaciones:</h4> | |
<div style="line-height: 1.6;">{analysis['recommendations']}</div> | |
</div> | |
<details style="margin-top: 15px;"> | |
<summary style="cursor: pointer; font-weight: bold; margin-bottom: 10px;">📚 Información Educativa</summary> | |
<div style="background: rgba(255,255,255,0.05); padding: 15px; border-radius: 8px; margin-top: 10px; line-height: 1.6;"> | |
{analysis['educational_info']} | |
</div> | |
</details> | |
</div> | |
</div> | |
""" | |
def create_stripe_checkout_session(user_email, user_id): | |
"""Crea sesión de checkout de Stripe para suscripción Pro""" | |
if not PAYMENTS_ENABLED: | |
return None, "Pagos no configurados en el servidor" | |
try: | |
checkout_session = stripe.checkout.Session.create( | |
customer_email=user_email, | |
client_reference_id=str(user_id), | |
payment_method_types=['card'], | |
line_items=[{ | |
'price_data': { | |
'currency': 'usd', | |
'product_data': { | |
'name': '🛡️ AntiScam AI Pro', | |
'description': 'Protección ilimitada contra estafas digitales', | |
'images': ['https://img.icons8.com/color/96/security-shield-green.png'], | |
}, | |
'unit_amount': int(PRO_PRICE * 100), # En centavos | |
'recurring': {'interval': 'month'}, | |
}, | |
'quantity': 1, | |
}], | |
mode='subscription', | |
success_url='https://tu-dominio.com/success?session_id={CHECKOUT_SESSION_ID}', | |
cancel_url='https://tu-dominio.com/cancel', | |
allow_promotion_codes=True, | |
billing_address_collection='auto', | |
metadata={'user_id': str(user_id)}, | |
subscription_data={ | |
'metadata': {'user_id': str(user_id)}, | |
'trial_period_days': 7 # 7 días gratis | |
} | |
) | |
return checkout_session.url, None | |
except Exception as e: | |
return None, f"Error creando sesión de pago: {str(e)}" | |
def hash_password(password): | |
"""Hash seguro de contraseña con salt""" | |
salt = secrets.token_hex(16) | |
password_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000) | |
return salt + password_hash.hex() | |
def verify_password(password, stored_hash): | |
"""Verifica contraseña contra hash almacenado""" | |
try: | |
salt = stored_hash[:32] | |
stored_password_hash = stored_hash[32:] | |
password_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000) | |
return password_hash.hex() == stored_password_hash | |
except: | |
return False | |
def register_user(email, password, full_name): | |
"""Registro completo de usuario con verificación por email""" | |
try: | |
# Validaciones | |
if not email or not password or not full_name: | |
return False, "❌ Todos los campos son obligatorios" | |
if len(password) < 8: | |
return False, "❌ La contraseña debe tener al menos 8 caracteres" | |
if '@' not in email or '.' not in email: | |
return False, "❌ Email inválido" | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
# Verificar email existente | |
cursor.execute("SELECT id FROM users WHERE email = ?", (email,)) | |
if cursor.fetchone(): | |
conn.close() | |
return False, "❌ Este email ya está registrado" | |
# Crear usuario | |
password_hash = hash_password(password) | |
verification_token = secrets.token_urlsafe(32) | |
verification_expires = datetime.now() + timedelta(hours=24) | |
cursor.execute(''' | |
INSERT INTO users (email, password_hash, full_name, verification_token, verification_expires) | |
VALUES (?, ?, ?, ?, ?) | |
''', (email, password_hash, full_name, verification_token, verification_expires.isoformat())) | |
user_id = cursor.lastrowid | |
conn.commit() | |
conn.close() | |
# Enviar email de verificación | |
email_sent = send_verification_email(email, verification_token, full_name) | |
if email_sent: | |
return True, f""" | |
✅ **Registro exitoso, {full_name}!** | |
📧 Te hemos enviado un **email de verificación** a {email} | |
**Próximos pasos:** | |
1. Revisa tu bandeja de entrada (y spam) | |
2. Haz clic en el enlace de verificación | |
3. Inicia sesión para comenzar a protegerte | |
El enlace expira en **24 horas**. | |
""" | |
else: | |
return False, "❌ Error enviando email de verificación" | |
except Exception as e: | |
return False, f"❌ Error en registro: {str(e)}" | |
def verify_user_email(token): | |
"""Verifica email del usuario con token""" | |
try: | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
cursor.execute(''' | |
SELECT id, email, full_name, verification_expires | |
FROM users | |
WHERE verification_token = ? AND is_verified = FALSE | |
''', (token,)) | |
user = cursor.fetchone() | |
if not user: | |
conn.close() | |
return False, "❌ Token inválido o usuario ya verificado" | |
user_id, email, full_name, expires = user | |
# Verificar expiración | |
if datetime.now() > datetime.fromisoformat(expires): | |
conn.close() | |
return False, "❌ El token ha expirado. Solicita un nuevo registro." | |
# Verificar usuario | |
cursor.execute(''' | |
UPDATE users | |
SET is_verified = TRUE, verification_token = NULL, verification_expires = NULL | |
WHERE id = ? | |
''', (user_id,)) | |
conn.commit() | |
conn.close() | |
return True, f"✅ ¡Email verificado exitosamente! Ahora puedes iniciar sesión, {full_name}." | |
except Exception as e: | |
return False, f"❌ Error verificando email: {str(e)}" | |
def login_user(email, password): | |
"""Login completo con validaciones""" | |
try: | |
global current_session | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
cursor.execute(''' | |
SELECT id, password_hash, full_name, is_pro, is_verified, | |
subscription_expires, avatar_url, total_analyses | |
FROM users WHERE email = ? | |
''', (email,)) | |
user = cursor.fetchone() | |
if not user: | |
conn.close() | |
return False, "❌ Email no encontrado" | |
user_id, stored_hash, full_name, is_pro, is_verified, sub_expires, avatar_url, total_analyses = user | |
if not verify_password(password, stored_hash): | |
conn.close() | |
return False, "❌ Contraseña incorrecta" | |
if not is_verified: | |
conn.close() | |
return False, "❌ Debes verificar tu email antes de iniciar sesión. Revisa tu bandeja de entrada." | |
# Verificar suscripción expirada | |
if is_pro and sub_expires: | |
try: | |
if datetime.fromisoformat(sub_expires) < datetime.now(): | |
cursor.execute("UPDATE users SET is_pro = FALSE WHERE id = ?", (user_id,)) | |
conn.commit() | |
is_pro = False | |
except: | |
pass | |
# Actualizar último login | |
cursor.execute("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?", (user_id,)) | |
conn.commit() | |
conn.close() | |
# Actualizar sesión global | |
current_session.update({ | |
"logged_in": True, | |
"user_id": user_id, | |
"email": email, | |
"full_name": full_name, | |
"is_pro": is_pro, | |
"avatar_url": avatar_url or "", | |
"subscription_expires": sub_expires, | |
"total_analyses": total_analyses or 0 | |
}) | |
# Cargar uso diario | |
load_daily_usage() | |
status = "👑 PRO" if is_pro else "🆓 FREE" | |
return True, f"✅ ¡Bienvenido de vuelta, {full_name}! ({status})" | |
except Exception as e: | |
return False, f"❌ Error en login: {str(e)}" | |
def load_daily_usage(): | |
"""Carga el uso diario del usuario actual""" | |
global current_session | |
if not current_session["logged_in"]: | |
return | |
try: | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
today = datetime.now().strftime("%Y-%m-%d") | |
cursor.execute(''' | |
SELECT analysis_count FROM daily_usage | |
WHERE user_id = ? AND usage_date = ? | |
''', (current_session["user_id"], today)) | |
result = cursor.fetchone() | |
current_session["daily_usage"] = result[0] if result else 0 | |
conn.close() | |
except Exception as e: | |
print(f"Error cargando uso diario: {e}") | |
current_session["daily_usage"] = 0 | |
def can_analyze(): | |
"""Verifica si el usuario puede hacer análisis""" | |
if not current_session["logged_in"]: | |
return False, "No autenticado" | |
if current_session["is_pro"]: | |
return True, "Usuario Pro" | |
if current_session["daily_usage"] >= DAILY_FREE_LIMIT: | |
return False, "Límite diario alcanzado" | |
return True, "Análisis disponible" | |
def increment_usage(): | |
"""Incrementa el contador de uso""" | |
global current_session | |
if not current_session["logged_in"] or current_session["is_pro"]: | |
return | |
try: | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
today = datetime.now().strftime("%Y-%m-%d") | |
cursor.execute(''' | |
INSERT OR IGNORE INTO daily_usage (user_id, usage_date, analysis_count) | |
VALUES (?, ?, 0) | |
''', (current_session["user_id"], today)) | |
cursor.execute(''' | |
UPDATE daily_usage SET analysis_count = analysis_count + 1 | |
WHERE user_id = ? AND usage_date = ? | |
''', (current_session["user_id"], today)) | |
# Actualizar total de análisis del usuario | |
cursor.execute(''' | |
UPDATE users SET total_analyses = total_analyses + 1 | |
WHERE id = ? | |
''', (current_session["user_id"],)) | |
conn.commit() | |
conn.close() | |
current_session["daily_usage"] += 1 | |
current_session["total_analyses"] += 1 | |
except Exception as e: | |
print(f"Error incrementando uso: {e}") | |
def save_analysis(content_type, content_preview, risk_level, risk_score, analysis_result): | |
"""Guarda el análisis en el historial""" | |
if not current_session["logged_in"]: | |
return | |
try: | |
conn = sqlite3.connect(DATABASE_FILE) | |
cursor = conn.cursor() | |
cursor.execute(''' | |
INSERT INTO analyses (user_id, content_type, content_preview, risk_level, risk_score, analysis | |