- Python FastAPI backend (chatbot-api/) with Claude Sonnet 4.6, prompt injection protection, rate limiting (30 msg/session), off-topic filtering, Redis session storage - Rocket.Chat integration for live monitoring and human takeover - Lead capture via n8n webhook - React chat widget: floating bubble, auto-greeting after 30s, glassmorphism chat window, mobile responsive, lazy loaded, Mixpanel analytics - Nginx proxy /api/chat → chatbot-api:8000 - Docker: chatbot-api + Redis services added to docker-compose Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
80 lines
2.7 KiB
Python
80 lines
2.7 KiB
Python
import httpx
|
|
from config import settings
|
|
|
|
HEADERS: dict[str, str] = {}
|
|
|
|
|
|
def _get_headers() -> dict[str, str]:
|
|
if not HEADERS:
|
|
HEADERS.update({
|
|
"X-Auth-Token": settings.rocketchat_auth_token,
|
|
"X-User-Id": settings.rocketchat_user_id,
|
|
"Content-Type": "application/json",
|
|
})
|
|
return HEADERS
|
|
|
|
|
|
async def get_or_create_room(session_id: str, visitor_name: str = "Website Visitor") -> str | None:
|
|
"""Get or create a Rocket.Chat livechat room for a session."""
|
|
if not settings.rocketchat_auth_token:
|
|
return None
|
|
try:
|
|
async with httpx.AsyncClient() as http:
|
|
# Register visitor
|
|
resp = await http.post(
|
|
f"{settings.rocketchat_url}/api/v1/livechat/visitor",
|
|
headers=_get_headers(),
|
|
json={
|
|
"visitor": {
|
|
"token": session_id,
|
|
"name": visitor_name,
|
|
}
|
|
},
|
|
timeout=10,
|
|
)
|
|
resp.raise_for_status()
|
|
|
|
# Get or create room
|
|
resp = await http.get(
|
|
f"{settings.rocketchat_url}/api/v1/livechat/room",
|
|
headers=_get_headers(),
|
|
params={"token": session_id},
|
|
timeout=10,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json().get("room", {}).get("_id")
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
async def send_message(room_id: str, session_id: str, text: str, sender: str = "bot") -> None:
|
|
"""Send a message to a Rocket.Chat livechat room."""
|
|
if not room_id or not settings.rocketchat_auth_token:
|
|
return
|
|
try:
|
|
async with httpx.AsyncClient() as http:
|
|
if sender == "visitor":
|
|
await http.post(
|
|
f"{settings.rocketchat_url}/api/v1/livechat/message",
|
|
headers={"Content-Type": "application/json"},
|
|
json={
|
|
"token": session_id,
|
|
"rid": room_id,
|
|
"msg": text,
|
|
},
|
|
timeout=10,
|
|
)
|
|
else:
|
|
await http.post(
|
|
f"{settings.rocketchat_url}/api/v1/chat.sendMessage",
|
|
headers=_get_headers(),
|
|
json={
|
|
"message": {
|
|
"rid": room_id,
|
|
"msg": f"[{sender.upper()}] {text}",
|
|
}
|
|
},
|
|
timeout=10,
|
|
)
|
|
except Exception:
|
|
pass
|