Untitled
unknown
plain_text
9 months ago
10 kB
12
Indexable
@app.websocket("/ws/chat-v3")
async def websocket_endpoint(websocket: WebSocket, db: Session = Depends(get_db)):
print("🟢 New WebSocket connection request")
sender_id = None
try:
await websocket.accept()
# Step 1: Authenticate sender
initial_data = await websocket.receive_json()
if not initial_data or "sender_id" not in initial_data:
raise ValueError("Missing sender_id in initial connection")
sender_id = int(initial_data["sender_id"])
sender_role = initial_data.get("sender_role", "student")
print(f"Authenticated sender_id={sender_id} with role={sender_role}")
# Step 2: Determine sender role
sender_login = db.query(Login_via_Active_session).filter(Login_via_Active_session.id == sender_id).first()
sender_role_base = sender_login.user_Expert_role if sender_login and not sender_role == "student" else None
if sender_role_base and sender_role == "student":
sender_role = "student"
elif sender_role_base in ["science", "math", "social_science", "english", "language"]:
sender_role = f"{sender_role_base}_expert_teacher"
else:
sender_role = sender_role_base or "general"
# Step 3: Fetch sender name
sender_user = db.query(User).filter(User.id == sender_id).first()
sender_name = f"{sender_user.first_name} {sender_user.last_name or ''}".strip() if sender_user else "Unknown Sender"
print(f"Sender role determined: {sender_role}")
# Step 4: Connect WebSocket client
await manager.connect(websocket, sender_id)
await websocket.send_json({"status": "connected"})
# Step 5: Listen for messages
while True:
data = await websocket.receive_json()
print("📥 Received WebSocket data:", data)
try:
receiver_id = int(data["receiver_id"])
message = data["message"]
# Fetch receiver role
receiver_login = db.query(Login_via_Active_session).filter(Login_via_Active_session.id == receiver_id).first()
receiver_role_base = receiver_login.user_Expert_role if receiver_login else "student"
receiver_role = (
f"{receiver_role_base}_expert_teacher"
if receiver_role_base in ["science", "math", "social_science", "english", "language"]
else receiver_role_base
)
# Determine sender role (overridable per-message)
msg_sender_role = data.get("sender_role", sender_role)
# Fetch receiver name
receiver_user = db.query(User).filter(User.id == receiver_id).first()
receiver_name = f"{receiver_user.first_name} {receiver_user.last_name or ''}".strip() if receiver_user else "Unknown Receiver"
except (KeyError, ValueError) as e:
print(f"❌ Error parsing data: {str(e)}")
await websocket.send_json({"error": "Invalid message format"})
continue
# Step 5.5: Ticket handling
ticket = None
# --- Common cleanup logic ---
def expire_old_tickets(sender_id, receiver_id, sender_role, receiver_role):
"""
Remove tickets where the opposite side hasn't replied within 24 hours.
"""
if sender_role.lower() == "parent" and receiver_role.lower() == "aqc":
# AQC hasn't responded in 24 hours
expired_tickets = db.query(ChatTicket).filter(
ChatTicket.parent_id == sender_id,
ChatTicket.receiver_id == receiver_id,
ChatTicket.is_active == True,
ChatTicket.created_at < datetime.utcnow() - timedelta(hours=24),
ChatTicket.last_response_by == "parent"
).all()
for old_ticket in expired_tickets:
old_ticket.is_active = False
db.commit()
print(f"🗑️ Removed expired Parent→AQC ticket ID {old_ticket.id} (no AQC response in 24h)")
elif sender_role.lower() == "aqc" and receiver_role.lower() == "parent":
# Parent hasn't responded in 24 hours
expired_tickets = db.query(ChatTicket).filter(
ChatTicket.parent_id == receiver_id,
ChatTicket.receiver_id == sender_id,
ChatTicket.is_active == True,
ChatTicket.created_at < datetime.utcnow() - timedelta(hours=24),
ChatTicket.last_response_by == "aqc"
).all()
for old_ticket in expired_tickets:
old_ticket.is_active = False
db.commit()
print(f"🗑️ Removed expired AQC→Parent ticket ID {old_ticket.id} (no Parent response in 24h)")
# --- Parent → AQC ---
if msg_sender_role.lower() == "parent" and receiver_role.lower() == "aqc":
expire_old_tickets(sender_id, receiver_id, msg_sender_role, receiver_role)
# Check if active ticket exists for this parent → AQC
ticket = db.query(ChatTicket).filter_by(
parent_id=sender_id, receiver_id=receiver_id, is_active=True
).first()
if not ticket:
ticket = ChatTicket(
parent_id=sender_id, # parent is sender
receiver_id=receiver_id, # AQC is receiver
ticket_name=f"Ticket from Parent {sender_id} to AQC {receiver_id}",
ticket_description="New chat initiated by Parent",
created_at=datetime.utcnow(),
is_active=True,
last_response_by="parent"
)
db.add(ticket)
db.commit()
db.refresh(ticket)
print(f"🎟️ Using ticket ID: {ticket.id} for Parent→AQC message")
# --- AQC → Parent ---
elif msg_sender_role.lower() == "aqc" and receiver_role.lower() == "parent":
expire_old_tickets(sender_id, receiver_id, msg_sender_role, receiver_role)
# Check if active ticket exists for this AQC → parent
ticket = db.query(ChatTicket).filter_by(
parent_id=receiver_id, receiver_id=sender_id, is_active=True
).first()
if not ticket:
ticket = ChatTicket(
parent_id=receiver_id, # parent is receiver
receiver_id=sender_id, # AQC is sender
ticket_name=f"Ticket from AQC {sender_id} to Parent {receiver_id}",
ticket_description="New chat initiated by AQC",
created_at=datetime.utcnow(),
is_active=True,
last_response_by="aqc"
)
db.add(ticket)
db.commit()
db.refresh(ticket)
print(f"🎟️ Using ticket ID: {ticket.id} for AQC→Parent message")
# --- Update last responder ---
if ticket:
ticket.last_response_by = msg_sender_role.lower()
ticket.last_message_at = datetime.utcnow() # optional for tracking
db.commit()
# Step 6: Save message to DB
new_msg = ChatMessage(
sender_id=sender_id,
sender_role=msg_sender_role,
receiver_id=receiver_id,
receiver_role=receiver_role,
message=message,
timestamp=datetime.now(ZoneInfo("UTC")),
sender_name=sender_name,
receiver_name=receiver_name,
ticket_id=ticket.id if ticket else None,
)
db.add(new_msg)
try:
db.commit()
db.refresh(new_msg)
print(f"✅ Message saved to ChatMessage table: {new_msg.id}")
except Exception as e:
db.rollback()
print(f"❌ Database commit failed: {str(e)}")
await websocket.send_json({"error": f"Failed to save message: {str(e)}"})
continue
# Step 7: Send message to both clients
payload = {
"sender_id": new_msg.sender_id,
"sender_role": new_msg.sender_role,
"sender_name": new_msg.sender_name,
"receiver_id": new_msg.receiver_id,
"receiver_role": new_msg.receiver_role,
"receiver_name": new_msg.receiver_name,
"message": new_msg.message,
"timestamp": new_msg.timestamp.astimezone(ZoneInfo("Asia/Kolkata")).strftime('%Y-%m-%d %H:%M:%S'),
}
await manager.send_to_sender_and_receiver(sender_id, receiver_id, payload)
print(f"📤 Message sent to clients: {payload}")
except WebSocketDisconnect:
print(f"🔌 WebSocket disconnected for sender_id={sender_id}")
if sender_id:
manager.disconnect(sender_id)
except (HTTPException, ValueError) as e:
if not websocket.client_state == 2:
print(f"⚠️ Error: {str(e)}")
await websocket.send_json({"error": str(e)})
await websocket.close(code=1008, reason=str(e))
except Exception as e:
print(f"❌ Unexpected error: {str(e)}")
if sender_id:
manager.disconnect(sender_id)
if not websocket.client_state == 2:
await websocket.send_json({"error": "Internal server error"})
await websocket.close(code=1011)
Editor is loading...
Leave a Comment