feat: init development branch with permanent access token and menu handlers
This commit is contained in:
442
src/main/java/com/rit/whatsapp/handler/FacultyMenuHandler.java
Normal file
442
src/main/java/com/rit/whatsapp/handler/FacultyMenuHandler.java
Normal file
@@ -0,0 +1,442 @@
|
||||
package com.rit.whatsapp.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rit.whatsapp.model.UserState;
|
||||
import com.rit.whatsapp.service.ImsApiService;
|
||||
import com.rit.whatsapp.service.SessionService;
|
||||
import com.rit.whatsapp.service.WhatsAppMessageBuilder;
|
||||
import com.rit.whatsapp.service.WhatsAppMessageService;
|
||||
import com.rit.whatsapp.util.MessageFormatter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public class FacultyMenuHandler {
|
||||
|
||||
private final WhatsAppMessageService messageService;
|
||||
private final SessionService sessionService;
|
||||
private final ImsApiService imsApiService;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Autowired
|
||||
public FacultyMenuHandler(WhatsAppMessageService messageService,
|
||||
SessionService sessionService,
|
||||
ImsApiService imsApiService) {
|
||||
this.messageService = messageService;
|
||||
this.sessionService = sessionService;
|
||||
this.imsApiService = imsApiService;
|
||||
}
|
||||
|
||||
public void sendMainMenu(String phoneNumber) {
|
||||
sessionService.setUserState(phoneNumber, UserState.MAIN_MENU);
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
String name = sessionService.getStudentName(phoneNumber, token);
|
||||
|
||||
String text = "Welcome back, *" + name + "*! 👨🏫\n\nWhat would you like to do today?";
|
||||
|
||||
java.util.Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
"RIT Digital Assistant",
|
||||
text,
|
||||
"Faculty Menu",
|
||||
getFacultyMenuRows()
|
||||
);
|
||||
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
}
|
||||
|
||||
public void sendFacultyMenuDirectly(String phoneNumber) {
|
||||
sendMainMenu(phoneNumber);
|
||||
}
|
||||
|
||||
public void handleMenuSelection(String phoneNumber, String selectedListId) {
|
||||
if (selectedListId.startsWith("faculty_markatt_select_")) {
|
||||
try {
|
||||
int index = Integer.parseInt(selectedListId.substring("faculty_markatt_select_".length()));
|
||||
handleClassSelected(phoneNumber, index);
|
||||
} catch (Exception e) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Invalid selection.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (selectedListId) {
|
||||
case "faculty_timetable":
|
||||
handleTimetable(phoneNumber);
|
||||
break;
|
||||
case "faculty_subjects":
|
||||
handleSubjects(phoneNumber);
|
||||
break;
|
||||
case "faculty_attendance":
|
||||
handleAttendanceSubjects(phoneNumber);
|
||||
break;
|
||||
case "faculty_logout":
|
||||
sessionService.logoutUser(phoneNumber);
|
||||
messageService.sendMessage(phoneNumber, "You have been logged out successfully. Type 'Hi' to log in again.");
|
||||
break;
|
||||
default:
|
||||
sendMainMenu(phoneNumber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTimetable(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
sendTimetableMenu(phoneNumber, token);
|
||||
}
|
||||
|
||||
public void sendTimetableMenu(String phoneNumber, String token) {
|
||||
String timetableJson = imsApiService.fetchFromIms("/api/faculty/timetable", token);
|
||||
if (timetableJson == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to fetch timetable. Please try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
String todaySchedule = MessageFormatter.formatFacultyTimetable(timetableJson);
|
||||
if (todaySchedule.length() > 1020) {
|
||||
todaySchedule = todaySchedule.substring(0, 1017) + "...";
|
||||
}
|
||||
|
||||
List<Map<String, String>> buttons = new ArrayList<>();
|
||||
Map<String, String> weeklyBtn = new HashMap<>();
|
||||
weeklyBtn.put("id", "faculty_weekly_timetable");
|
||||
weeklyBtn.put("title", "Weekly Timetable");
|
||||
buttons.add(weeklyBtn);
|
||||
|
||||
Map<String, String> menuBtn = new HashMap<>();
|
||||
menuBtn.put("id", "faculty_show_menu");
|
||||
menuBtn.put("title", "Faculty Menu");
|
||||
buttons.add(menuBtn);
|
||||
|
||||
Map<String, Object> buttonMessage = WhatsAppMessageBuilder.buildButtonMessage(todaySchedule, buttons);
|
||||
messageService.sendInteractiveMessage(phoneNumber, buttonMessage);
|
||||
sendMainMenu(phoneNumber);
|
||||
}
|
||||
|
||||
public void handleWeeklyTimetable(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
String rawTimetable = imsApiService.fetchFromIms("/api/faculty/timetable", token);
|
||||
String weeklyTimetable = MessageFormatter.formatFacultyWeeklyTimetable(rawTimetable);
|
||||
|
||||
messageService.sendMessage(phoneNumber, weeklyTimetable);
|
||||
sendTimetableMenu(phoneNumber, token);
|
||||
}
|
||||
|
||||
public void handleSubjects(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
String subjectsJson = imsApiService.fetchFromIms("/api/faculty/subjects", token);
|
||||
|
||||
if (subjectsJson != null) {
|
||||
String formattedSubjects = MessageFormatter.formatFacultySubjects(subjectsJson);
|
||||
sendResponseWithMenu(phoneNumber, formattedSubjects);
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to fetch subjects. Please try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleAttendanceSubjects(String phoneNumber) {
|
||||
sendResponseWithMenu(phoneNumber, "🚧 *Feature under development*");
|
||||
}
|
||||
|
||||
public void handleClassSelected(String phoneNumber, int index) {
|
||||
Map<String, Object> params = sessionService.getAttendanceSubjectParams(phoneNumber, index);
|
||||
if (params == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to retrieve class details. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
sessionService.setActiveAttendanceParams(phoneNumber, params);
|
||||
|
||||
String subjectName = (String) params.get("subjectName");
|
||||
Object periodsObj = params.get("periods");
|
||||
Object classesObj = params.get("classes");
|
||||
|
||||
String periodStr = (periodsObj instanceof List) ? String.join(", ", (List<String>) periodsObj) : String.valueOf(periodsObj);
|
||||
String classStr = (classesObj instanceof List) ? String.join(", ", (List<String>) classesObj) : String.valueOf(classesObj);
|
||||
|
||||
String body = "Selected Class:\n📚 *" + subjectName + "*\n📅 Period: *" + periodStr + "*\n🏫 Class: *" + classStr + "*\n\nChoose an action:";
|
||||
|
||||
List<Map<String, String>> buttons = new ArrayList<>();
|
||||
Map<String, String> presentBtn = new HashMap<>();
|
||||
presentBtn.put("id", "faculty_mark_present");
|
||||
presentBtn.put("title", "Mark All Present");
|
||||
buttons.add(presentBtn);
|
||||
|
||||
Map<String, String> absBtn = new HashMap<>();
|
||||
absBtn.put("id", "faculty_mark_absentees");
|
||||
absBtn.put("title", "Enter Absentees");
|
||||
buttons.add(absBtn);
|
||||
|
||||
Map<String, Object> buttonMessage = WhatsAppMessageBuilder.buildButtonMessage(body, buttons);
|
||||
messageService.sendInteractiveMessage(phoneNumber, buttonMessage);
|
||||
}
|
||||
|
||||
public void handleButtonSelection(String phoneNumber, String selectedButtonId) {
|
||||
switch (selectedButtonId) {
|
||||
case "faculty_mark_present":
|
||||
handleMarkAllPresent(phoneNumber);
|
||||
break;
|
||||
case "faculty_mark_absentees":
|
||||
handleStartAbsenteesFlow(phoneNumber);
|
||||
break;
|
||||
case "faculty_weekly_timetable":
|
||||
handleWeeklyTimetable(phoneNumber);
|
||||
break;
|
||||
case "faculty_show_menu":
|
||||
sendMainMenu(phoneNumber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> mapToApiParams(Map<String, Object> params) {
|
||||
Map<String, Object> apiParams = new HashMap<>();
|
||||
apiParams.put("date", params.get("date"));
|
||||
apiParams.put("period", params.get("periods"));
|
||||
apiParams.put("subject", params.get("subjectId"));
|
||||
apiParams.put("staff", params.get("staffId"));
|
||||
apiParams.put("classes", params.get("classes"));
|
||||
apiParams.put("special", params.get("specialSub") != null ? params.get("specialSub") : 0);
|
||||
apiParams.put("regular", params.get("regularSub") != null ? params.get("regularSub") : 1);
|
||||
apiParams.put("batch", "");
|
||||
return apiParams;
|
||||
}
|
||||
|
||||
public void handleMarkAllPresent(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
Map<String, Object> activeParams = sessionService.getActiveAttendanceParams(phoneNumber);
|
||||
if (activeParams == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Session expired or invalid class. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
messageService.sendMessage(phoneNumber, "Retrieving student list... ⏳");
|
||||
|
||||
String rawList = imsApiService.getStudentListForAttendance(token, mapToApiParams(activeParams));
|
||||
if (rawList == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to retrieve student list from IMS.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(rawList);
|
||||
if (root.has("success") && !root.get("success").asBoolean()) {
|
||||
String errorMsg = root.has("message") ? root.get("message").asText() : "Unknown IMS error";
|
||||
sendResponseWithMenu(phoneNumber, "❌ IMS Error: " + errorMsg);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Proceed to parse HTML if not JSON error
|
||||
}
|
||||
|
||||
List<MessageFormatter.StudentAttendanceInfo> students = MessageFormatter.parseStudents(rawList);
|
||||
if (students.isEmpty()) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ No students found in this class.");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> postData = new HashMap<>();
|
||||
postData.put("date", activeParams.get("date"));
|
||||
postData.put("period", activeParams.get("periods"));
|
||||
postData.put("subject", activeParams.get("subjectId"));
|
||||
|
||||
List<Map<String, String>> studentStatusList = new ArrayList<>();
|
||||
for (MessageFormatter.StudentAttendanceInfo s : students) {
|
||||
Map<String, String> sStatus = new HashMap<>();
|
||||
sStatus.put("id", s.id);
|
||||
sStatus.put("status", "present");
|
||||
studentStatusList.add(sStatus);
|
||||
}
|
||||
postData.put("students", studentStatusList);
|
||||
|
||||
messageService.sendMessage(phoneNumber, "Submitting attendance to IMS... ⏳");
|
||||
String submitRes = imsApiService.submitAttendance(token, postData);
|
||||
boolean isSuccess = false;
|
||||
String errorMsg = "Unknown error";
|
||||
try {
|
||||
if (submitRes != null) {
|
||||
JsonNode root = objectMapper.readTree(submitRes);
|
||||
if (root.has("success") && root.get("success").asBoolean()) {
|
||||
isSuccess = true;
|
||||
} else if (root.has("message")) {
|
||||
errorMsg = root.get("message").asText();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// fallback
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
sendResponseWithMenu(phoneNumber, "✅ Attendance marked successfully! (100% Present)");
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to submit attendance: " + errorMsg);
|
||||
}
|
||||
sessionService.clearActiveAttendanceParams(phoneNumber);
|
||||
}
|
||||
|
||||
public void handleStartAbsenteesFlow(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
Map<String, Object> activeParams = sessionService.getActiveAttendanceParams(phoneNumber);
|
||||
if (activeParams == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Session expired or invalid class. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
messageService.sendMessage(phoneNumber, "Retrieving student list... ⏳");
|
||||
|
||||
String rawList = imsApiService.getStudentListForAttendance(token, mapToApiParams(activeParams));
|
||||
if (rawList == null) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to retrieve student list from IMS.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(rawList);
|
||||
if (root.has("success") && !root.get("success").asBoolean()) {
|
||||
String errorMsg = root.has("message") ? root.get("message").asText() : "Unknown IMS error";
|
||||
sendResponseWithMenu(phoneNumber, "❌ IMS Error: " + errorMsg);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Proceed to parse HTML if not JSON error
|
||||
}
|
||||
|
||||
List<MessageFormatter.StudentAttendanceInfo> students = MessageFormatter.parseStudents(rawList);
|
||||
if (students.isEmpty()) {
|
||||
sendResponseWithMenu(phoneNumber, "❌ No students found in this class.");
|
||||
return;
|
||||
}
|
||||
|
||||
sessionService.storeActiveStudentList(phoneNumber, students);
|
||||
sessionService.setUserState(phoneNumber, UserState.AWAITING_ABSENTEES);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("📋 *Student List*\n");
|
||||
sb.append("Please reply with the Serial Numbers of the ABSENT students separated by commas (e.g., 2, 5).\n");
|
||||
sb.append("Type *'none'* if everyone is present.\n━━━━━━━━━━━━━━\n\n");
|
||||
|
||||
for (int i = 0; i < students.size(); i++) {
|
||||
MessageFormatter.StudentAttendanceInfo s = students.get(i);
|
||||
sb.append(i + 1).append(". `").append(s.regNo).append("` - ").append(s.name).append("\n");
|
||||
}
|
||||
|
||||
messageService.sendMessage(phoneNumber, sb.toString());
|
||||
}
|
||||
|
||||
public void handleAbsenteesInput(String phoneNumber, String messageBody) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
Map<String, Object> activeParams = sessionService.getActiveAttendanceParams(phoneNumber);
|
||||
List<MessageFormatter.StudentAttendanceInfo> students = sessionService.getActiveStudentList(phoneNumber);
|
||||
|
||||
if (activeParams == null || students == null) {
|
||||
sessionService.resetState(phoneNumber);
|
||||
sendResponseWithMenu(phoneNumber, "❌ Session expired. Please restart marking attendance.");
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Integer> absenteeIndices = new HashSet<>();
|
||||
String input = messageBody.trim().toLowerCase();
|
||||
|
||||
if (!input.equals("none") && !input.equals("nil") && !input.equals("no one")) {
|
||||
String[] parts = input.split(",");
|
||||
for (String p : parts) {
|
||||
try {
|
||||
int val = Integer.parseInt(p.trim());
|
||||
if (val >= 1 && val <= students.size()) {
|
||||
absenteeIndices.add(val - 1);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// Ignore non-numeric inputs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> postData = new HashMap<>();
|
||||
postData.put("date", activeParams.get("date"));
|
||||
postData.put("period", activeParams.get("periods"));
|
||||
postData.put("subject", activeParams.get("subjectId"));
|
||||
|
||||
List<Map<String, String>> studentStatusList = new ArrayList<>();
|
||||
int absentCount = 0;
|
||||
for (int i = 0; i < students.size(); i++) {
|
||||
MessageFormatter.StudentAttendanceInfo s = students.get(i);
|
||||
Map<String, String> sStatus = new HashMap<>();
|
||||
sStatus.put("id", s.id);
|
||||
if (absenteeIndices.contains(i)) {
|
||||
sStatus.put("status", "absent");
|
||||
absentCount++;
|
||||
} else {
|
||||
sStatus.put("status", "present");
|
||||
}
|
||||
studentStatusList.add(sStatus);
|
||||
}
|
||||
postData.put("students", studentStatusList);
|
||||
|
||||
messageService.sendMessage(phoneNumber, "Submitting attendance to IMS... ⏳");
|
||||
String submitRes = imsApiService.submitAttendance(token, postData);
|
||||
boolean isSuccess = false;
|
||||
String errorMsg = "Unknown error";
|
||||
try {
|
||||
if (submitRes != null) {
|
||||
JsonNode root = objectMapper.readTree(submitRes);
|
||||
if (root.has("success") && root.get("success").asBoolean()) {
|
||||
isSuccess = true;
|
||||
} else if (root.has("message")) {
|
||||
errorMsg = root.get("message").asText();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// fallback
|
||||
}
|
||||
|
||||
sessionService.resetState(phoneNumber);
|
||||
sessionService.clearActiveAttendanceParams(phoneNumber);
|
||||
|
||||
if (isSuccess) {
|
||||
sendResponseWithMenu(phoneNumber, "✅ Attendance marked successfully! (" + (students.size() - absentCount) + " Present, " + absentCount + " Absent)");
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "❌ Failed to submit attendance: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResponseWithMenu(String phoneNumber, String bodyText) {
|
||||
if (bodyText.length() > 1024) {
|
||||
bodyText = bodyText.substring(0, 1021) + "...";
|
||||
}
|
||||
|
||||
java.util.Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
null,
|
||||
bodyText,
|
||||
"Faculty Menu",
|
||||
getFacultyMenuRows()
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
}
|
||||
|
||||
private java.util.List<java.util.Map<String, String>> getFacultyMenuRows() {
|
||||
java.util.List<java.util.Map<String, String>> rows = new java.util.ArrayList<>();
|
||||
rows.add(createRow("faculty_timetable", "📅 View Timetable", "Check your daily schedule"));
|
||||
rows.add(createRow("faculty_subjects", "📚 View Subjects", "Subjects you are teaching"));
|
||||
rows.add(createRow("faculty_attendance", "📝 Mark Attendance", "Mark student attendance"));
|
||||
rows.add(createRow("faculty_logout", "🚪 Logout", "Sign out of your account"));
|
||||
return rows;
|
||||
}
|
||||
|
||||
private java.util.Map<String, String> createRow(String id, String title, String description) {
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
row.put("id", id);
|
||||
row.put("title", title);
|
||||
row.put("description", description);
|
||||
return row;
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,19 @@ public class LoginHandler {
|
||||
private final ImsApiService imsApiService;
|
||||
private final StudentMenuHandler studentMenuHandler;
|
||||
|
||||
private final FacultyMenuHandler facultyMenuHandler;
|
||||
|
||||
@Autowired
|
||||
public LoginHandler(WhatsAppMessageService messageService,
|
||||
SessionService sessionService,
|
||||
ImsApiService imsApiService,
|
||||
StudentMenuHandler studentMenuHandler) {
|
||||
StudentMenuHandler studentMenuHandler,
|
||||
FacultyMenuHandler facultyMenuHandler) {
|
||||
this.messageService = messageService;
|
||||
this.sessionService = sessionService;
|
||||
this.imsApiService = imsApiService;
|
||||
this.studentMenuHandler = studentMenuHandler;
|
||||
this.facultyMenuHandler = facultyMenuHandler;
|
||||
}
|
||||
|
||||
public void startLoginFlow(String phoneNumber) {
|
||||
@@ -42,18 +46,22 @@ public class LoginHandler {
|
||||
|
||||
messageService.sendMessage(phoneNumber, "Authenticating with IMS... ⏳");
|
||||
|
||||
String imsSessionToken = imsApiService.authenticate(imsId, passcode);
|
||||
ImsApiService.AuthResult result = imsApiService.authenticate(imsId, passcode);
|
||||
|
||||
if (imsSessionToken != null) {
|
||||
if (result != null && result.token != null) {
|
||||
// Authentication successful
|
||||
sessionService.authenticateUser(phoneNumber, imsId, passcode, imsSessionToken);
|
||||
sessionService.authenticateUser(phoneNumber, imsId, passcode, result);
|
||||
sessionService.clearTempLoginId(phoneNumber);
|
||||
sessionService.resetState(phoneNumber); // Reset the flow state
|
||||
|
||||
messageService.sendMessage(phoneNumber, "✅ Login Successful!");
|
||||
|
||||
// Show the main menu
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
if (result.isFaculty) {
|
||||
facultyMenuHandler.sendMainMenu(phoneNumber);
|
||||
} else {
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
}
|
||||
} else {
|
||||
// Authentication failed
|
||||
sessionService.clearTempLoginId(phoneNumber);
|
||||
|
||||
Reference in New Issue
Block a user