- Store lead info in Redis session meta so bot remembers name across messages - Create Twenty CRM lead immediately from form data (bypass tool-call flow) - Store room→session reverse mapping in Redis for RC webhook delivery - Update system prompt: don't re-ask for form-provided info - Fix "Most Popular" badge clipped on mobile (overflow: visible) - Fix contact form inputs overflowing on small screens (box-sizing) - Reduce chat tooltip size on mobile to avoid overlapping content Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
117 lines
4.4 KiB
Python
117 lines
4.4 KiB
Python
import httpx
|
|
import logging
|
|
from config import settings
|
|
from security import get_redis
|
|
|
|
logger = logging.getLogger("rocketchat")
|
|
|
|
HEADERS: dict[str, str] = {}
|
|
# Cache: session_id -> room_id
|
|
_room_cache: 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
|
|
|
|
# Return cached room
|
|
if session_id in _room_cache:
|
|
return _room_cache[session_id]
|
|
|
|
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()
|
|
room_id = resp.json().get("room", {}).get("_id")
|
|
if room_id:
|
|
_room_cache[session_id] = room_id
|
|
# Store reverse mapping for webhook delivery
|
|
r = await get_redis()
|
|
await r.set(f"chat:room_session:{room_id}", session_id, ex=86400)
|
|
return room_id
|
|
except Exception as e:
|
|
logger.error(f"RC get_or_create_room error: {e}")
|
|
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":
|
|
# Visitor sends via livechat visitor API
|
|
resp = 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,
|
|
)
|
|
if resp.status_code != 200:
|
|
logger.error(f"RC visitor msg error: {resp.status_code} {resp.text}")
|
|
else:
|
|
# Bot/agent sends via authenticated API
|
|
resp = await http.post(
|
|
f"{settings.rocketchat_url}/api/v1/chat.sendMessage",
|
|
headers=_get_headers(),
|
|
json={
|
|
"message": {
|
|
"rid": room_id,
|
|
"msg": f"🤖 {text}",
|
|
}
|
|
},
|
|
timeout=10,
|
|
)
|
|
if resp.status_code != 200:
|
|
logger.error(f"RC bot msg error: {resp.status_code} {resp.text}")
|
|
# Fallback: try livechat/message as agent
|
|
resp2 = await http.post(
|
|
f"{settings.rocketchat_url}/api/v1/livechat/message",
|
|
headers=_get_headers(),
|
|
json={
|
|
"token": session_id,
|
|
"rid": room_id,
|
|
"msg": f"🤖 {text}",
|
|
},
|
|
timeout=10,
|
|
)
|
|
if resp2.status_code != 200:
|
|
logger.error(f"RC bot msg fallback error: {resp2.status_code} {resp2.text}")
|
|
except Exception as e:
|
|
logger.error(f"RC send_message exception: {e}")
|