diff --git a/src/naliiabotapi/api/v1/webhooks/__init__.py b/src/naliiabotapi/api/v1/webhooks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/naliiabotapi/api/v1/webhooks/chat_hook.py b/src/naliiabotapi/api/v1/webhooks/chat_hook.py new file mode 100644 index 0000000..92ef1e0 --- /dev/null +++ b/src/naliiabotapi/api/v1/webhooks/chat_hook.py @@ -0,0 +1,84 @@ +from fastapi import APIRouter, Depends, Request +from langchain.messages import HumanMessage +from ...dependencies import get_agent +from ....logger import logger +import httpx +import json + +router = APIRouter() + +async def send_whatsapp_message(instance_url: str, instance_name: str, apikey: str, jid: str, text: str): + """ + Envía un mensaje de texto a través de la API de WhatsApp. + """ + endpoint = f"{instance_url}/message/sendText/{instance_name}" + headers = { + "apikey": apikey, + "Content-Type": "application/json" + } + + payload = { + "number": jid, + "text": text, + "delay": 1200, + "linkPreview": False + } + + async with httpx.AsyncClient() as client: + try: + response = await client.post(endpoint, json=payload, headers=headers) + response.raise_for_status() + return response.json() + except Exception as e: + logger.error(f"Error enviando mensaje a WhatsApp: {e}") + return None + + +@router.post("/webhook") +async def webhook_chat(request: Request, agent = Depends(get_agent)): + logger.info("Received webhook request") + body = await request.json() + + try: + data = body.get("data", {}) + key = data.get("key", {}) + + if key.get("fromMe") is True: + logger.info("Mensaje enviado por mí (bot), Ignorando.") + return {"status": "ignored", "reason": "message_from_me"} + + thread_id = key.get("remoteJid") + user_message = data.get("message", {}).get("conversation", "") + + if not user_message: + return {"reply": "No text message provided."} + + config = { + "configurable": { + "thread_id": thread_id + } + } + + messages = [HumanMessage(content=user_message)] + + # Se pasa el config como un argumento separado, no dentro del estado + # agent_response = agent.invoke({"messages": messages}, config=config) + + agent_response = agent.invoke({"messages": messages}) + agent_response_content = agent_response["messages"][-1].content + + clean_jid = thread_id.split('@')[0] + + await send_whatsapp_message( + instance_url="http://localhost:8080", + instance_name="ALEJANDRO AYALA", + apikey="98B6B820AFC3-4544-9A4C-011B0719C2D1", + jid=clean_jid, + text=agent_response_content + ) + + except Exception as e: + logger.error(f"Error processing webhook: {e}") + return {"error": str(e)} + + return {"status": "sent", "reply": agent_response_content} \ No newline at end of file diff --git a/src/naliiabotapi/main.py b/src/naliiabotapi/main.py index 5cdc684..ef9b9d8 100644 --- a/src/naliiabotapi/main.py +++ b/src/naliiabotapi/main.py @@ -1,5 +1,6 @@ from fastapi import FastAPI from naliiabotapi.api.v1.endpoints.chat import router as chat_router +from naliiabotapi.api.v1.webhooks.chat_hook import router as chat_hook_router app = FastAPI( title="NaliiaBot API", @@ -8,6 +9,7 @@ app = FastAPI( ) app.include_router(chat_router) +app.include_router(chat_hook_router) @app.get("/") def read_root(): diff --git a/tests/conftest.py b/tests/conftest.py index 13c70c7..7514e1e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,18 @@ from unittest.mock import Mock from src.naliiabot.bot.agent.agent import Agent, AgentConfig from src.naliiabot.bot.factories.llm_factory import LLMFactory +from httpx import ASGITransport, AsyncClient +from naliiabotapi.main import app + +@pytest.fixture(scope="function") +async def async_client(): + """Fixture para un cliente HTTP asíncrono.""" + + async with AsyncClient( + transport=ASGITransport(app=app), + base_url="http://localhost:8010") as client: + + yield client @pytest.fixture diff --git a/tests/test_webhook.py b/tests/test_webhook.py new file mode 100644 index 0000000..c461192 --- /dev/null +++ b/tests/test_webhook.py @@ -0,0 +1,12 @@ +import pytest + + +class TestWebhookChatBot: + """Tests for WebhookChatBot.""" + + @pytest.mark.anyio + async def tests_webhook_success(self, async_client): + """Test that the webhook endpoint returns a successful response.""" + response = await async_client.post("/webhook", json={"message": "Hello"}) + assert response.status_code == 200 + assert response.json() == {"reply": "Hello, how can I assist you?"} \ No newline at end of file