From 206c4db2788fd626bd5316178bd21cc8ce7ddb34 Mon Sep 17 00:00:00 2001 From: Ekaropolus Date: Tue, 20 May 2025 04:00:13 -0600 Subject: [PATCH] Telegram bot exception for sending messages by chat id --- pxy_bots/views.py | 132 ++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/pxy_bots/views.py b/pxy_bots/views.py index c0306a7..39506df 100644 --- a/pxy_bots/views.py +++ b/pxy_bots/views.py @@ -1,11 +1,10 @@ import json import logging + from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt -from asgiref.sync import sync_to_async from telegram import Update, Bot from django.utils import timezone -from django.conf import settings from .models import TelegramBot, TelegramConversation, TelegramMessage from pxy_langchain.services import LangchainAIService @@ -14,102 +13,107 @@ from .handlers import dream_city_command, start, help_command, handle_location logger = logging.getLogger(__name__) @csrf_exempt -async def telegram_webhook(request, bot_name): +def telegram_webhook(request, bot_name): """ - Telegram webhook handler that logs inbound/outbound messages and - responds with AI or commands, resilient to Neo4j or send failures. - Always returns 200 OK to prevent Telegram retries. + Webhook view for Telegram that logs inbound/outbound messages, + handles commands and AI fallback, and always returns 200 OK. """ try: - logger.info(f"Webhook called for bot: {bot_name}") - - # 1) Load bot configuration - try: - bot_instance = await sync_to_async(TelegramBot.objects.get)( - name=bot_name, is_active=True - ) - except TelegramBot.DoesNotExist: - logger.error(f"Bot '{bot_name}' not found or inactive.") - return JsonResponse({"status": "ok", "error": f"Bot '{bot_name}' not found."}, status=200) - - # 2) Ensure POST + # 1) Sólo POST if request.method != "POST": logger.warning("Received non-POST request to Telegram webhook.") - return JsonResponse({"status": "ok"}, status=200) + return JsonResponse({"status": "ok"}) - # 3) Parse the update JSON + # 2) Cargar configuración del bot + try: + bot_instance = TelegramBot.objects.get(name=bot_name, is_active=True) + except TelegramBot.DoesNotExist: + msg = f"Bot '{bot_name}' not found or inactive." + logger.error(msg) + return JsonResponse({"status": "ok", "error": msg}) + + # 3) Parsear el update try: payload = json.loads(request.body.decode("utf-8")) update = Update.de_json(payload, Bot(token=bot_instance.token)) - logger.info(f"Parsed update: {update}") except Exception as e: logger.error(f"Failed to parse update JSON: {e}") - return JsonResponse({"status": "ok", "error": "Invalid JSON payload"}, status=200) + return JsonResponse({"status": "ok", "error": "Invalid JSON payload"}) - # 4) Log inbound message - user_id = str(update.effective_user.id) - conv, _ = await sync_to_async(TelegramConversation.objects.get_or_create)( - bot=bot_instance, - user_id=user_id, - defaults={'started_at': timezone.now()} - ) - incoming_text = update.message.text or "" - try: - await sync_to_async(TelegramMessage.objects.create)( - conversation=conv, - direction=TelegramMessage.IN, - content=incoming_text + # 4) Loggear mensaje entrante + if update.message and update.effective_user: + user_id = str(update.effective_user.id) + conv, _ = TelegramConversation.objects.get_or_create( + bot=bot_instance, + user_id=user_id, + defaults={"started_at": timezone.now()} ) - except Exception as log_in_err: - logger.error(f"Error logging inbound message: {log_in_err}") - - # 5) Handle commands or AI response - if update.message: + incoming_text = update.message.text or "" try: - text = update.message.text or "" + TelegramMessage.objects.create( + conversation=conv, + direction=TelegramMessage.IN, + content=incoming_text + ) + except Exception as log_in_err: + logger.error(f"Error logging inbound message: {log_in_err}") + else: + logger.warning("Update has no message or user info.") + + # 5) Manejar comandos y fallback AI + reply = None + response_time = 0 + if update.message: + text = update.message.text or "" + try: + # Comandos predefinidos if text == "/start": - await start(update) + start(update) elif text == "/help": - await help_command(update) + help_command(update) elif text == "/dream_city": - await dream_city_command(update) + dream_city_command(update) elif update.message.location: - await handle_location(update) + handle_location(update) else: - # AI fallback with resilience + # Fallback AI try: - assistant = await sync_to_async(LangchainAIService)(bot_instance.assistant) - start_time = timezone.now() - bot_response = await sync_to_async(assistant.generate_response)(text) - response_time = int((timezone.now() - start_time).total_seconds() * 1000) + assistant = LangchainAIService(bot_instance.assistant) + start_ts = timezone.now() + reply = assistant.generate_response(text) + response_time = int((timezone.now() - start_ts).total_seconds() * 1000) except Exception as ai_err: logger.error(f"AI service error: {ai_err}") - bot_response = "Lo siento, el servicio de IA no está disponible." + reply = "Lo siento, el servicio de IA no está disponible." response_time = 0 - # Send reply (skipped in DEBUG) - if not settings.DEBUG: - try: - await update.message.reply_text(bot_response) - except Exception as send_err: - logger.error(f"Error sending message to Telegram: {send_err}") - - # Log outbound message + # Enviar respuesta + chat_id = update.message.chat.id try: - await sync_to_async(TelegramMessage.objects.create)( + Bot(token=bot_instance.token).send_message( + chat_id=chat_id, + text=reply + ) + except Exception as send_err: + logger.error(f"Error sending message to Telegram (chat {chat_id}): {send_err}") + + # Loggear mensaje saliente + try: + TelegramMessage.objects.create( conversation=conv, direction=TelegramMessage.OUT, - content=bot_response, + content=reply, response_time_ms=response_time ) except Exception as log_out_err: logger.error(f"Error logging outbound message: {log_out_err}") + except Exception as cmd_err: logger.error(f"Error processing Telegram commands: {cmd_err}") - # 6) Always return 200 OK - return JsonResponse({"status": "ok"}, status=200) + # 6) Devolver siempre 200 OK + return JsonResponse({"status": "ok"}) except Exception as e: logger.error(f"Unexpected error in telegram_webhook: {e}") - return JsonResponse({"status": "ok", "error": str(e)}, status=200) + return JsonResponse({"status": "ok", "error": str(e)})