Untitled

 avatar
unknown
plain_text
9 months ago
10 kB
13
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