| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| SLIDEMAP API DOCUMENTATION |
| HF Space: https://Mafia2008-sar.hf.space |
| API Docs UI: https://Mafia2008-sar.hf.space/api-docs |
| CORS: Enabled (any frontend can call these APIs) |
| Protocol: REST (JSON) + SSE (Server-Sent Events) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| BASE URL |
| ββββββββ |
| https://Mafia2008-sar.hf.space |
|
|
| HOW IT WORKS (Flow) |
| βββββββββββββββββββ |
| 1. Teacher creates a session β gets a 4-digit room code |
| 2. Students look up session by room code β get sessionId |
| 3. Both Teacher & Students subscribe to /api/events (SSE stream) |
| 4. Teacher uploads slides β students see them live via SSE |
| 5. Teacher creates polls β students vote via API |
| 6. Students submit doubts β teacher resolves via API |
| 7. Teacher ends session β all students notified via SSE |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 1. SESSION API |
| POST /api/session |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| ββ 1.1 Create Session (Teacher) βββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "create", // Required |
| "teacherId": "string", // Required - unique teacher ID |
| "teacherName": "string", // Required - display name |
| "sessionName": "string", // Required - class/session name |
| "description": "string" // Optional |
| } |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "id": "uuid-string", |
| "roomCode": "4821", // 4-digit code for students |
| "name": "Physics Ch5", |
| "teacherId": "teacher-001", |
| "teacherName": "Mr. Sharma", |
| "isActive": true, |
| "currentSlide": 0, |
| "totalSlides": 0, |
| "studentCount": 0, |
| "createdAt": "2026-04-14T16:00:00.000Z" |
| } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/session', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', |
| teacherId: 'teacher-001', |
| teacherName: 'Mr. Sharma', |
| sessionName: 'Physics β Chapter 5' |
| }) |
| }); |
|
|
|
|
| ββ 1.2 Lookup Session by Room Code ββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "lookup", // Required |
| "roomCode": "4821" // Required - 4-digit room code |
| } |
|
|
| Response: |
| { |
| "success": true, |
| "data": { "id": "uuid", "roomCode": "4821", ... } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/session', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ action: 'lookup', roomCode: '4821' }) |
| }); |
|
|
|
|
| ββ 1.3 Join Session (Student) βββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "join", // Required |
| "sessionId": "4821", // Required - sessionId OR room code |
| "userId": "student-xyz", // Required - unique student ID |
| "userName": "Rahul Kumar" // Required - display name |
| } |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "user": { "id": "...", "name": "Rahul Kumar", "role": "student" }, |
| "session": { ... }, |
| "currentSlide": 0, |
| "activePoll": null, |
| "whiteboardStrokes": [], |
| "reactions": {} |
| } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/session', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'join', |
| sessionId: '4821', // room code accepted |
| userId: 'student-xyz', |
| userName: 'Rahul Kumar' |
| }) |
| }); |
|
|
|
|
| ββ 1.4 End Session (Teacher) ββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "end", // Required |
| "sessionId": "uuid" // Required |
| } |
|
|
| Response: |
| { "success": true, "message": "Session ended successfully" } |
|
|
| SSE broadcast: session_ended β all connected clients |
|
|
|
|
| ββ 1.5 List All Active Sessions βββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { "action": "list" } |
|
|
| Response: |
| { |
| "success": true, |
| "data": [ { "id": "...", "roomCode": "4821", ... } ] |
| } |
|
|
|
|
| ββ 1.6 Get Session State ββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/session |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "get", // Required |
| "sessionId": "uuid" // Required |
| } |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "session": { ... }, |
| "currentSlide": 2, |
| "activePoll": { ... } | null, |
| "doubts": [ ... ], |
| "recentChats": [ ... ], |
| "whiteboardStrokes": [ ... ], |
| "reactions": { "π": 3, "β€οΈ": 1 }, |
| "onlineUsers": [ ... ] |
| } |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 2. EVENTS API (SSE - Server-Sent Events) |
| GET /api/events |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| Opens a persistent SSE stream. Connect immediately after joining a session. |
| Both teacher AND students should connect to this endpoint. |
|
|
| URL: |
| GET /api/events?sessionId=UUID&userId=YOUR_USER_ID |
|
|
| Query Parameters: |
| sessionId string Required The session ID |
| userId string Required Your unique user ID |
|
|
| Response Headers: |
| Content-Type: text/event-stream |
| Cache-Control: no-cache |
| Access-Control-Allow-Origin: * |
|
|
| ββ SSE Events Table ββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| Event Name | Triggered When | Data Fields |
| ββββββββββββββββββ|ββββββββββββββββββββββββββββββββββββ|ββββββββββββββββββββ |
| connected | Stream opens | sessionId, userId, full session state |
| slide_change | Teacher changes/uploads slide | slideNumber, imageBase64, totalSlides, timestamp |
| new_poll | Teacher creates a poll | id, question, options[], isActive, ... |
| poll_update | A student votes | pollId, results{}, totalVotes, timestamp |
| poll_ended | Teacher ends poll | pollId, results{}, totalVotes, correctAnswer |
| correct_answer | Teacher reveals correct answer | pollId, correctOptionIds[], correctAnswer |
| new_doubt | Student submits a doubt | id, studentName, question, slideNumber, status |
| doubt_solved | Teacher resolves a doubt | id, status, teacherResponse, annotatedImage |
| doubt_updated | Doubt status changes | id, status |
| student_joined | A new student joins | userId, userName, studentCount |
| student_left | A student disconnects | userId, userName, studentCount |
| session_ended | Teacher ends the session | sessionId, endedAt |
| chat_message | Someone sends a chat message | id, userId, userName, message, isTeacher |
| reaction | Someone reacts | reactions{}, lastReaction |
| state_sync | Reconnect (sends full state again) | full session state |
|
|
| Example (JS): |
| const es = new EventSource( |
| 'https://Mafia2008-sar.hf.space/api/events?sessionId=UUID&userId=STUDENT_ID' |
| ); |
|
|
| // Connected - get initial state |
| es.addEventListener('connected', e => { |
| const state = JSON.parse(e.data); |
| console.log('Session state:', state); |
| }); |
|
|
| // Slide changed by teacher |
| es.addEventListener('slide_change', e => { |
| const { slideNumber, imageBase64, totalSlides } = JSON.parse(e.data); |
| document.getElementById('slide-img').src = imageBase64; |
| }); |
|
|
| // New poll created |
| es.addEventListener('new_poll', e => { |
| const poll = JSON.parse(e.data); |
| showPollUI(poll); |
| }); |
|
|
| // Live vote update |
| es.addEventListener('poll_update', e => { |
| const { pollId, results, totalVotes } = JSON.parse(e.data); |
| updatePollResults(results); |
| }); |
|
|
| // Poll ended |
| es.addEventListener('poll_ended', e => { |
| const { pollId, results, correctAnswer } = JSON.parse(e.data); |
| showFinalResults(results, correctAnswer); |
| }); |
|
|
| // Teacher reveals correct answer |
| es.addEventListener('correct_answer', e => { |
| const { pollId, correctOptionIds } = JSON.parse(e.data); |
| highlightCorrectAnswer(correctOptionIds); |
| }); |
|
|
| // Student submitted a doubt (Teacher listens to this) |
| es.addEventListener('new_doubt', e => { |
| const doubt = JSON.parse(e.data); |
| addDoubtCard(doubt); |
| }); |
|
|
| // Teacher resolved a doubt (Student listens to this) |
| es.addEventListener('doubt_solved', e => { |
| const doubt = JSON.parse(e.data); |
| updateDoubtStatus(doubt); |
| }); |
|
|
| // Session ended |
| es.addEventListener('session_ended', e => { |
| alert('Session has ended'); |
| es.close(); |
| }); |
|
|
| // Heartbeat (keep-alive, every 15s) - no action needed |
| es.onerror = err => { |
| console.error('SSE error:', err); |
| // Reconnect logic here |
| }; |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 3. SLIDE API |
| POST /api/slide |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| ββ 3.1 Upload & Broadcast Slide (Teacher β Students) ββββββββββββββββββββββββ |
|
|
| POST /api/slide |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "upload", // Required |
| "sessionId": "uuid", // Required |
| "slideImage": "data:image/png;base64,iVBOR...", // Required - base64 image |
| "slideNumber": 3 // Optional - replaces slide at index; omit to append |
| } |
|
|
| Notes: |
| - slideImage can be a data URL ("data:image/png;base64,...") or raw base64 |
| - Automatically broadcasts slide_change SSE to ALL connected students |
| - Students receive the imageBase64 directly β no separate fetch needed |
|
|
| Response: |
| { |
| "success": true, |
| "data": { "slideNumber": 3, "totalSlides": 4 } |
| } |
|
|
| Example (Electron / SlideMap app): |
| const canvas = document.getElementById('myCanvas'); |
| const base64 = canvas.toDataURL('image/png'); |
|
|
| fetch('https://Mafia2008-sar.hf.space/api/slide', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'upload', |
| sessionId: SESSION_ID, |
| slideImage: base64, |
| slideNumber: currentSlideIndex // optional |
| }) |
| }); |
|
|
|
|
| ββ 3.2 Go to Slide (Teacher) ββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/slide |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "goto", // Required |
| "sessionId": "uuid", // Required |
| "slideNumber": 2 // Required - 0-indexed slide number |
| } |
|
|
| Response: |
| { "success": true, "data": { "slideNumber": 2, "totalSlides": 5 } } |
|
|
|
|
| ββ 3.3 Next Slide βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/slide |
| Body: { "action": "next", "sessionId": "uuid" } |
|
|
| Response: |
| { "success": true, "data": { "slideNumber": 3, "totalSlides": 5 } } |
|
|
|
|
| ββ 3.4 Previous Slide βββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/slide |
| Body: { "action": "prev", "sessionId": "uuid" } |
|
|
| Response: |
| { "success": true, "data": { "slideNumber": 1, "totalSlides": 5 } } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 4. POLL API |
| POST /api/poll (create/vote/end/setCorrectAnswers) |
| GET /api/poll (fetch polls) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| ββ 4.1 Create Poll (Teacher) ββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/poll |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "create", // Required |
| "sessionId": "uuid", // Required |
| "question": "string", // Required |
| "options": ["A","B","C"], // Required - min 2 items |
| "type": "single", // Optional - "single"|"multiple" (default: "single") |
| "correctAnswer": "A", // Optional - marks correct option |
| "duration": 0 // Optional - auto-end after N seconds (0 = manual) |
| } |
|
|
| Notes: |
| - Any existing active poll is auto-ended before creating a new one |
| - Broadcasts new_poll SSE to ALL students immediately |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "id": "poll-uuid", |
| "sessionId": "uuid", |
| "question": "What is Newton's 2nd Law?", |
| "options": [ |
| { "id": "option_0", "text": "F=ma", "isCorrect": true, "votes": 0 }, |
| { "id": "option_1", "text": "E=mcΒ²", "isCorrect": false, "votes": 0 } |
| ], |
| "type": "single", |
| "isActive": true, |
| "results": { "F=ma": 0, "E=mcΒ²": 0 }, |
| "totalVotes": 0, |
| "votedStudents": [], |
| "createdAt": "2026-04-14T16:00:00.000Z" |
| } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/poll', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', |
| sessionId: SESSION_ID, |
| question: "What is Newton's 2nd Law?", |
| options: ['F=ma', 'E=mcΒ²', 'PV=nRT', 'V=IR'], |
| correctAnswer: 'F=ma', |
| duration: 0 |
| }) |
| }); |
|
|
|
|
| ββ 4.2 Vote on Poll (Student) βββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/poll |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "vote", // Required |
| "sessionId": "uuid", // Required |
| "pollId": "poll-uuid", // Required |
| "option": "F=ma", // Required - the option text student selected |
| "studentId": "student-xyz" // Required - student unique ID |
| } |
|
|
| Notes: |
| - Each student can vote only ONCE per poll |
| - Broadcasts poll_update SSE to ALL clients with live vote counts |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "pollId": "poll-uuid", |
| "results": { "F=ma": 5, "E=mcΒ²": 2 }, |
| "totalVotes": 7, |
| "voted": true |
| } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/poll', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'vote', |
| sessionId: SESSION_ID, |
| pollId: POLL_ID, |
| option: 'F=ma', |
| studentId: MY_STUDENT_ID |
| }) |
| }); |
|
|
|
|
| ββ 4.3 End Poll (Teacher) βββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/poll |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "end", // Required |
| "sessionId": "uuid", // Required |
| "pollId": "poll-uuid" // Required |
| } |
|
|
| Notes: |
| - Broadcasts poll_ended SSE with final results + correctAnswer |
|
|
| Response: |
| { "success": true, "message": "Poll ended successfully" } |
|
|
|
|
| ββ 4.4 Reveal Correct Answer (Teacher) ββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/poll |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "setCorrectAnswers", // Required |
| "sessionId": "uuid", // Required |
| "pollId": "poll-uuid", // Required |
| "correctAnswer": "F=ma", // Optional - option text |
| "correctOptionIds": ["option_0"] // Optional - option id array |
| } |
|
|
| Notes: |
| - Broadcasts correct_answer SSE so students can highlight the correct option |
| - Use this AFTER ending the poll to reveal the answer |
|
|
| Response: |
| { "success": true, "message": "Correct answers broadcast to students" } |
|
|
|
|
| ββ 4.5 Get All Polls ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| GET /api/poll?sessionId=UUID |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "polls": [ { ... }, { ... } ], |
| "activePoll": { ... } | null, |
| "totalPolls": 3 |
| } |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 5. DOUBT API |
| POST /api/doubt (submit/resolve/update) |
| GET /api/doubt (fetch doubts) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| ββ 5.1 Submit Doubt (Student β Teacher) βββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/doubt |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "create", // Required |
| "sessionId": "uuid", // Required |
| "studentId": "student-xyz", // Required |
| "studentName": "Rahul Kumar", // Required |
| "question": "string", // Required - the doubt text |
| "image": "data:image/png;base64,...", // Optional |
| "slideNumber": 3 // Optional - slide where doubt occurred |
| } |
|
|
| Notes: |
| - Broadcasts new_doubt SSE to the teacher (and all connected clients) |
| - slideNumber auto-filled from current slide if not provided |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "id": "doubt-uuid", |
| "sessionId": "uuid", |
| "studentId": "student-xyz", |
| "studentName": "Rahul Kumar", |
| "question": "Why F=ma and not F=mv?", |
| "slideNumber": 3, |
| "status": "pending", |
| "createdAt": "2026-04-14T16:00:00.000Z" |
| } |
| } |
|
|
| Example (JS): |
| fetch('https://Mafia2008-sar.hf.space/api/doubt', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', |
| sessionId: SESSION_ID, |
| studentId: MY_STUDENT_ID, |
| studentName: MY_NAME, |
| question: "I don't understand why F=ma and not F=mv", |
| slideNumber: 3 |
| }) |
| }); |
|
|
|
|
| ββ 5.2 Resolve Doubt (Teacher) ββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/doubt |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "solve", // Required |
| "sessionId": "uuid", // Required |
| "doubtId": "doubt-uuid", // Required |
| "teacherResponse": "string", // Optional - text explanation |
| "annotatedImage": "data:image/..." // Optional - annotated image |
| } |
|
|
| Notes: |
| - Broadcasts doubt_solved SSE so the student sees the resolution |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "id": "doubt-uuid", |
| "status": "solved", |
| "teacherResponse": "Because acceleration = rate of change of velocity...", |
| "solvedAt": "2026-04-14T16:05:00.000Z" |
| } |
| } |
|
|
|
|
| ββ 5.3 Update Doubt Status (Teacher) ββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/doubt |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "action": "update", // Required |
| "sessionId": "uuid", // Required |
| "doubtId": "doubt-uuid", // Required |
| "status": "in_progress" // Required: "pending"|"in_progress"|"solved" |
| } |
|
|
| Notes: |
| - Broadcasts doubt_updated SSE to all clients |
|
|
| Response: |
| { "success": true, "data": { "id": "...", "status": "in_progress" } } |
|
|
|
|
| ββ 5.4 Get All Doubts (Teacher) βββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| GET /api/doubt?sessionId=UUID |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "doubts": [ { ... }, { ... } ], |
| "summary": { |
| "total": 7, |
| "pending": 2, |
| "inProgress": 1, |
| "solved": 4 |
| } |
| } |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 6. CHAT API |
| POST /api/chat (send message) |
| GET /api/chat (get history) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| ββ 6.1 Send Chat Message ββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| POST /api/chat |
| Content-Type: application/json |
|
|
| Request Body: |
| { |
| "sessionId": "uuid", // Required |
| "userId": "user-xyz", // Required |
| "userName": "Rahul", // Required |
| "message": "Can you explain again?", // Required |
| "isTeacher": false // Optional (default: false) |
| } |
|
|
| Notes: |
| - Broadcasts chat_message SSE to ALL participants |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "id": "msg-uuid", |
| "userId": "user-xyz", |
| "userName": "Rahul", |
| "message": "Can you explain again?", |
| "isTeacher": false, |
| "timestamp": "2026-04-14T16:10:00.000Z" |
| } |
| } |
|
|
|
|
| ββ 6.2 Get Chat History βββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| GET /api/chat?sessionId=UUID&limit=100 |
|
|
| Query Parameters: |
| sessionId string Required Session ID |
| limit number Optional Max messages to return (default: 100, max stored: 500) |
|
|
| Response: |
| { |
| "success": true, |
| "data": { |
| "messages": [ { "id": "...", "message": "...", ... } ], |
| "count": 23 |
| } |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| ERROR RESPONSES |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| All error responses follow this format: |
| { |
| "success": false, |
| "error": "Descriptive error message" |
| } |
|
|
| HTTP Status Codes: |
| 400 Bad Request - Missing or invalid fields |
| 404 Not Found - Session/Poll/Doubt not found |
| 500 Internal Error - Server error |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| COMPLETE INTEGRATION EXAMPLE (Student Frontend - Vanilla JS) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| const BASE = 'https://Mafia2008-sar.hf.space'; |
| let sessionId = null; |
| let myUserId = 'student-' + Math.random().toString(36).substr(2, 9); |
| let myName = 'Rahul Kumar'; |
|
|
| // STEP 1: Look up session by room code |
| async function joinByRoomCode(roomCode) { |
| // Lookup |
| const lookup = await fetch(`${BASE}/api/session`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ action: 'lookup', roomCode }) |
| }).then(r => r.json()); |
|
|
| if (!lookup.success) return alert('Room not found'); |
| sessionId = lookup.data.id; |
|
|
| // Join |
| await fetch(`${BASE}/api/session`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'join', |
| sessionId, |
| userId: myUserId, |
| userName: myName |
| }) |
| }); |
|
|
| // STEP 2: Subscribe to SSE |
| connectSSE(); |
| } |
|
|
| // STEP 2: SSE Connection |
| function connectSSE() { |
| const es = new EventSource(`${BASE}/api/events?sessionId=${sessionId}&userId=${myUserId}`); |
|
|
| es.addEventListener('slide_change', e => { |
| const { imageBase64 } = JSON.parse(e.data); |
| document.getElementById('slide').src = imageBase64; |
| }); |
|
|
| es.addEventListener('new_poll', e => { |
| const poll = JSON.parse(e.data); |
| showPoll(poll); |
| }); |
|
|
| es.addEventListener('correct_answer', e => { |
| const { correctOptionIds } = JSON.parse(e.data); |
| highlightAnswer(correctOptionIds); |
| }); |
|
|
| es.addEventListener('doubt_solved', e => { |
| const doubt = JSON.parse(e.data); |
| showDoubtResolution(doubt); |
| }); |
|
|
| es.addEventListener('session_ended', () => { |
| alert('Class ended'); |
| es.close(); |
| }); |
| } |
|
|
| // STEP 3: Vote on poll |
| async function vote(pollId, option) { |
| await fetch(`${BASE}/api/poll`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'vote', sessionId, pollId, |
| option, studentId: myUserId |
| }) |
| }); |
| } |
|
|
| // STEP 4: Submit doubt |
| async function submitDoubt(text) { |
| await fetch(`${BASE}/api/doubt`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', sessionId, |
| studentId: myUserId, studentName: myName, |
| question: text |
| }) |
| }); |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| COMPLETE INTEGRATION EXAMPLE (Teacher - Electron/SlideMap App) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| const BASE = 'https://Mafia2008-sar.hf.space'; |
| const teacherId = 'teacher-001'; |
| const teacherName = 'Mr. Sharma'; |
| let sessionId = null; |
|
|
| // STEP 1: Create session |
| async function createSession(sessionName) { |
| const res = await fetch(`${BASE}/api/session`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', teacherId, teacherName, sessionName |
| }) |
| }).then(r => r.json()); |
|
|
| sessionId = res.data.id; |
| const roomCode = res.data.roomCode; // Show this to students |
| console.log('Room Code:', roomCode); |
|
|
| // Connect SSE to receive doubts + student events |
| connectSSE(); |
| } |
|
|
| // STEP 2: Connect to SSE (teacher also connects to receive doubts) |
| function connectSSE() { |
| const es = new EventSource(`${BASE}/api/events?sessionId=${sessionId}&userId=${teacherId}`); |
|
|
| es.addEventListener('new_doubt', e => { |
| const doubt = JSON.parse(e.data); |
| showDoubtInPanel(doubt); // Teacher sees student doubt |
| }); |
|
|
| es.addEventListener('student_joined', e => { |
| const { userName, studentCount } = JSON.parse(e.data); |
| console.log(`${userName} joined. Total: ${studentCount}`); |
| }); |
| } |
|
|
| // STEP 3: Send slide when screen changes |
| async function sendSlide(base64Image, slideIndex) { |
| await fetch(`${BASE}/api/slide`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'upload', sessionId, |
| slideImage: base64Image, |
| slideNumber: slideIndex |
| }) |
| }); |
| } |
|
|
| // STEP 4: Create poll |
| async function createPoll(question, options, correctAnswer) { |
| const res = await fetch(`${BASE}/api/poll`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'create', sessionId, question, options, |
| correctAnswer, duration: 0 |
| }) |
| }).then(r => r.json()); |
| return res.data.id; // pollId |
| } |
|
|
| // STEP 5: End poll and reveal answer |
| async function endPoll(pollId, correctAnswer) { |
| await fetch(`${BASE}/api/poll`, { |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ action: 'end', sessionId, pollId }) |
| }); |
|
|
| await fetch(`${BASE}/api/poll`, { |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'setCorrectAnswers', sessionId, pollId, correctAnswer |
| }) |
| }); |
| } |
|
|
| // STEP 6: Resolve a doubt |
| async function resolveDoubt(doubtId, response) { |
| await fetch(`${BASE}/api/doubt`, { |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| action: 'solve', sessionId, doubtId, |
| teacherResponse: response |
| }) |
| }); |
| } |
|
|
| // STEP 7: End session |
| async function endSession() { |
| await fetch(`${BASE}/api/session`, { |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ action: 'end', sessionId }) |
| }); |
| } |
|
|
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| QUICK REFERENCE CHEATSHEET |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| Action Method Endpoint action field |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Create session (T) POST /api/session "create" |
| Lookup by room code (S) POST /api/session "lookup" |
| Join session (S) POST /api/session "join" |
| End session (T) POST /api/session "end" |
| List all sessions POST /api/session "list" |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Subscribe to live events GET /api/events (SSE stream) |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Upload + broadcast slide POST /api/slide "upload" |
| Go to slide number (T) POST /api/slide "goto" |
| Next slide (T) POST /api/slide "next" |
| Prev slide (T) POST /api/slide "prev" |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Create poll (T) POST /api/poll "create" |
| Vote on poll (S) POST /api/poll "vote" |
| End poll (T) POST /api/poll "end" |
| Reveal answer (T) POST /api/poll "setCorrectAnswers" |
| Get all polls GET /api/poll ?sessionId=UUID |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Submit doubt (S) POST /api/doubt "create" |
| Resolve doubt (T) POST /api/doubt "solve" |
| Update doubt status (T) POST /api/doubt "update" |
| Get all doubts (T) GET /api/doubt ?sessionId=UUID |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Send chat message POST /api/chat (no action field) |
| Get chat history GET /api/chat ?sessionId=UUID&limit=N |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|
| (T) = Teacher action (S) = Student action |
|
|
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| END OF DOCUMENTATION |
| Interactive UI: https://Mafia2008-sar.hf.space/api-docs |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
|