smart-line-bot / app /api /v1 /endpoints /widget.py
Smiel2's picture
Initial commit
2eae977 verified
Raw
History Blame Contribute Delete
4.31 kB
"""
Web Chat Widget Service - embedded customer service widget.
"""
import logging
from typing import Dict, Any, Optional
from datetime import datetime
import uuid
from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
from pydantic import BaseModel
import json
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/widget", tags=["widget"])
# Active widget connections
active_connections: Dict[str, WebSocket] = {}
widget_sessions: Dict[str, Dict] = {}
class ChatMessage(BaseModel):
"""Chat message from widget."""
session_id: str
message: str
user_info: Optional[Dict] = None
class ChatResponse(BaseModel):
"""Chat response to widget."""
session_id: str
message: str
type: str = "message"
timestamp: str = None
@router.websocket("/ws/{session_id}")
async def websocket_chat(websocket: WebSocket, session_id: str):
"""WebSocket endpoint for real-time chat."""
await websocket.accept()
active_connections[session_id] = websocket
# Initialize session
widget_sessions[session_id] = {
"connected_at": datetime.utcnow().isoformat(),
"status": "active"
}
logger.info(f"Widget session connected: {session_id}")
try:
while True:
data = await websocket.receive_text()
message_data = json.loads(data)
# Handle different message types
msg_type = message_data.get("type", "message")
if msg_type == "message":
# Process message through AI service
user_message = message_data.get("message", "")
user_info = message_data.get("user_info", {})
# Here you would integrate with your AI/LLM service
# For now, return a simple response
response = f"Thanks for your message! We'll get back to you soon. (Session: {session_id})"
await websocket.send_json({
"type": "message",
"message": response,
"timestamp": datetime.utcnow().isoformat()
})
elif msg_type == "ping":
await websocket.send_json({"type": "pong"})
except WebSocketDisconnect:
logger.info(f"Widget session disconnected: {session_id}")
finally:
if session_id in active_connections:
del active_connections[session_id]
if session_id in widget_sessions:
del widget_sessions[session_id]
@router.post("/chat")
async def chat_endpoint(message: ChatMessage) -> Dict[str, Any]:
"""REST endpoint for chat (alternative to WebSocket)."""
session_id = message.session_id
# Check if user is connected via WebSocket
if session_id in active_connections:
websocket = active_connections[session_id]
try:
await websocket.send_json({
"type": "message",
"message": message.message,
"timestamp": datetime.utcnow().isoformat()
})
return {"status": "sent", "via": "websocket"}
except Exception as e:
logger.error(f"WebSocket send error: {e}")
# Return mock response (would integrate with AI/LLM)
return {
"status": "response",
"message": f"Thanks for your message! We'll respond shortly.",
"session_id": session_id
}
@router.get("/status")
async def widget_status() -> Dict[str, Any]:
"""Get widget status."""
return {
"status": "active",
"sessions": len(active_connections),
"platforms": ["line", "telegram", "web"]
}
@router.post("/broadcast")
async def broadcast_message(message: str) -> Dict[str, Any]:
"""Broadcast message to all connected widget sessions."""
count = 0
for session_id, websocket in active_connections.items():
try:
await websocket.send_json({
"type": "broadcast",
"message": message,
"timestamp": datetime.utcnow().isoformat()
})
count += 1
except Exception as e:
logger.error(f"Broadcast error: {e}")
return {"status": "success", "sent_to": count}