from flask_socketio import SocketIO, emit, join_room, leave_room from flask import request from app.services import SessionService import threading import json from redis import Redis class SocketController: def __init__( self, socketio: SocketIO, session_service: SessionService, ): self.socketio = socketio self.session_service = session_service self._register_events() def _register_events(self): @self.socketio.on("connect") def on_connect(): print(f"Client connected: {request.sid}") emit("connection_response", {"status": "connected", "sid": request.sid}) @self.socketio.on("disconnect") def on_disconnect(): print(f"Client disconnected: {request.sid}") @self.socketio.on("join_room") def handle_join_room(data): session_code = data.get("session_code") user_id = data.get("user_id") if not session_code or not user_id: emit("error", {"message": "session_code and user_id are required"}) return session = self.session_service.join_session( session_code=session_code, user_id=user_id, ) if session is None: emit("error", {"message": "Failed to join session or session inactive"}) return session_id = session["session_id"] join_room(session_id) if session["is_admin"]: message = "Admin has joined the room." else: message = f"User {session['username']} has joined the room." emit( "room_message", { "type": "join", "message": message, "room": session_id, "argument": "adm_update", "data": { "session_info": session["session_info"], "quiz_info": session["quiz_info"], }, }, to=request.sid, ) emit( "room_message", { "type": "participan_join", "message": message, "room": session_id, "argument": "adm_update", "data": { "participants": session["session_info"]["participants"], }, }, room=session_id, skip_sid=request.sid, ) # @self.socketio.on("submit_answer") # def handle_submit_answer(data): # session_code = data.get("session_id") # user_id = data.get("user_id") # question_index = data.get("question_index") # user_answer = data.get("answer") # if not all( # [ # session_code, # user_id, # question_index is not None, # user_answer is not None, # ] # ): # emit( # "error", # { # "message": "session_id, user_id, question_index, and answer are required" # }, # ) # return # quiz_service = QuizService(self.redis_repo) # question = quiz_service.get_question(session_code, question_index) # if question is None: # emit("error", {"message": "Question not found"}) # return # is_correct = quiz_service.is_correct(user_answer, question) # print( # f"User {user_id} answered Q{question_index} with '{user_answer}' -> {'✔' if is_correct else '✖'}" # ) # quiz_service.save_answer( # session_code, user_id, question_index, user_answer, is_correct # ) # scores = quiz_service.update_score(session_code, user_id, is_correct) # emit( # "answer_submitted", # { # "user_id": user_id, # "question_index": question_index, # "answer": user_answer, # "correct": is_correct, # }, # room=request.sid, # ) # admin_sid = self.admin_sids.get(session_code) # if admin_sid: # emit("score_update", scores, room=admin_sid) @self.socketio.on("leave_room") def handle_leave_room(data): session_id = data.get("session_id") user_id = data.get("user_id") username = data.get("username", "anonymous") leave_result = self.session_service.leave_session( session_id=session_id, user_id=user_id, ) leave_room(session_id) if leave_result["is_success"]: emit( "room_message", { "type": "participan_leave", "message": f"{username} has left the room.", "room": session_id, "argument": "adm_update", "data": { "participants": leave_result["participants"], }, }, room=session_id, skip_sid=request.sid, ) emit( "room_message", { "type": "leave", "message": f"{username} has left the room.", "room": session_id, "argument": "adm_update", "data": None, }, room=session_id, to=request.sid, ) @self.socketio.on("send_message") def on_send_message(data): session_code = data.get("session_id") message = data.get("message") username = data.get("username", "anonymous") emit( "receive_message", {"message": message, "from": username}, room=session_code, ) @self.socketio.on("end_session") def handle_end_session(data): session_code = data.get("session_id") user_id = data.get("user_id") if not session_code or not user_id: emit("error", {"message": "session_id and user_id required"}) return # Validasi user berhak mengakhiri session self.session_service.end_session(session_id=session_code, user_id=user_id) # Bersihkan semua data session di Redis for key in [ self._answers_key(session_code), self._scores_key(session_code), self._questions_key(session_code), ]: self.redis_repo.delete_key(key) emit( "room_closed", {"message": "Session has ended.", "room": session_code}, room=session_code, ) @self.socketio.on("start_quiz") def handle_start_quiz(data): session_id = data.get("session_id") if not session_id: emit("error", {"message": "session_id is required"}) return emit("quiz_started", {"message": "Quiz has started!"}, room=session_id) threading.Thread( target=self.session_service.simulate_quiz_flow, args=(session_id, self.socketio), daemon=True, ).start()