twilio_webhook / app.py
Moibe's picture
Fix costo listo
1064e7a
from fastapi import FastAPI, Request
from twilio.rest import Client
import os
# Inicializar FastAPI
app = FastAPI()
# Credenciales de Twilio
account_sid = 'AC70f4efa275c50cc260b07fa3b1c7e6c5'
# Leer auth_token: local desde bridges.py, o servidor desde secreto de Hugging Face
try:
import bridges
auth_token = bridges.au
except (ImportError, AttributeError):
# En servidor (Hugging Face), leer del secreto
auth_token = os.getenv('au')
if not auth_token:
raise ValueError("❌ Error: auth_token no configurado. Revisa que el secreto 'au' esté en Hugging Face o que bridges.py exista localmente.")
client = Client(account_sid, auth_token)
# Número de Twilio para enviar mensajes
TWILIO_NUMBER = 'whatsapp:+14155238886'
@app.post("/whatsapp")
async def receive_whatsapp(request: Request):
"""
Webhook que recibe mensajes de WhatsApp desde Twilio
"""
print(f"\n{'🔔 WEBHOOK /WHATSAPP - MENSAJE ENTRANTE':^60}")
# Obtener los datos del formulario que Twilio envía
form_data = await request.form()
# Información del mensaje
from_number = form_data.get('From')
message_body = form_data.get('Body')
message_sid = form_data.get('MessageSid')
print(f"De: {from_number} | Mensaje: {message_body} | SID: {message_sid}")
# Imprimir archivos adjuntos si los hay
num_media = form_data.get('NumMedia', '0')
if num_media and int(num_media) > 0:
for i in range(int(num_media)):
media_url = form_data.get(f'MediaUrl{i}')
media_type = form_data.get(f'MediaContentType{i}')
print(f" 📎 Archivo {i}: {media_type} - {media_url}")
# Ejemplo: Responder según lo que el usuario escribió
if message_body.lower() == "confirm":
response_text = "✅ Gracias por confirmar tu cita. Te esperamos!"
elif message_body.lower() == "cancel":
response_text = "❌ Tu cita ha sido cancelada."
else:
response_text = f"Recibimos tu mensaje: '{message_body}'"
# Enviar respuesta automática
message = client.messages.create(
from_=TWILIO_NUMBER,
body=response_text,
to=from_number
)
print(f"✉️ Respuesta enviada: {response_text}")
# Twilio necesita una respuesta vacía para confirmar que recibió el webhook
return {}
@app.get("/")
async def root():
"""
Ruta de prueba para verificar que el servidor está funcionando
"""
return {"status": "✅ Webhook de Twilio está activo"}
@app.post("/status")
async def status_callback(request: Request):
"""
Webhook que recibe actualizaciones de estado de mensajes enviados
Estados posibles: sent, delivered, undelivered, failed, read
"""
print(f"\n{'🔔 WEBHOOK /STATUS - ACTUALIZACIÓN DE ESTADO':^60}")
form_data = await request.form()
message_sid = form_data.get('MessageSid')
message_status = form_data.get('MessageStatus')
to_number = form_data.get('To')
account_sid = form_data.get('AccountSid')
print(f"SID: {message_sid} | Estado: {message_status} | Para: {to_number}")
# Consultar costo real de uso de WhatsApp del día (cobro por conversación)
if message_status == 'delivered':
try:
whatsapp_categories = [
'channels-whatsapp-service',
'channels-whatsapp-template-marketing',
'channels-whatsapp-template-utility',
'channels-whatsapp-template-authentication',
'channels-whatsapp-conversation-free',
'channels-messaging-inbound',
'channels-messaging-outbound',
]
print(f"\n💰 USO WHATSAPP HOY:")
for category in whatsapp_categories:
try:
records = client.usage.records.today.list(category=category)
for record in records:
if float(record.count) > 0 or float(record.price) > 0:
print(f" {record.description}:")
print(f" Cantidad: {record.count} {record.count_unit}")
print(f" Costo: {record.price} {record.price_unit}")
except Exception:
pass
except Exception as e:
print(f"⚠️ No se pudo obtener uso de WhatsApp: {str(e)}")
# Twilio necesita una respuesta vacía para confirmar que recibió el webhook
return {}
# Para ejecutar: uvicorn webhook:app --reload --host 0.0.0.0 --port 8000