feat: init development branch with permanent access token and menu handlers
This commit is contained in:
2
.env
2
.env
@@ -1,3 +1,3 @@
|
|||||||
DB_URL=jdbc:postgresql://localhost:5432/whatsapp_bot
|
DB_URL=jdbc:postgresql://localhost:5432/IMS%20data
|
||||||
DB_USERNAME=whatsapp_user
|
DB_USERNAME=whatsapp_user
|
||||||
DB_PASSWORD=RITHosting123
|
DB_PASSWORD=RITHosting123
|
||||||
|
|||||||
@@ -27,18 +27,21 @@ public class WebhookController {
|
|||||||
private final GrievanceHandler grievanceHandler;
|
private final GrievanceHandler grievanceHandler;
|
||||||
private final LoginHandler loginHandler;
|
private final LoginHandler loginHandler;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
private final com.rit.whatsapp.handler.FacultyMenuHandler facultyMenuHandler;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public WebhookController(WhatsAppMessageService messageService,
|
public WebhookController(WhatsAppMessageService messageService,
|
||||||
SessionService sessionService,
|
SessionService sessionService,
|
||||||
StudentMenuHandler studentMenuHandler,
|
StudentMenuHandler studentMenuHandler,
|
||||||
GrievanceHandler grievanceHandler,
|
GrievanceHandler grievanceHandler,
|
||||||
LoginHandler loginHandler) {
|
LoginHandler loginHandler,
|
||||||
|
com.rit.whatsapp.handler.FacultyMenuHandler facultyMenuHandler) {
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.sessionService = sessionService;
|
this.sessionService = sessionService;
|
||||||
this.studentMenuHandler = studentMenuHandler;
|
this.studentMenuHandler = studentMenuHandler;
|
||||||
this.grievanceHandler = grievanceHandler;
|
this.grievanceHandler = grievanceHandler;
|
||||||
this.loginHandler = loginHandler;
|
this.loginHandler = loginHandler;
|
||||||
|
this.facultyMenuHandler = facultyMenuHandler;
|
||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,13 +91,24 @@ public class WebhookController {
|
|||||||
if ("text".equals(messageType) && messageNode.get("text").get("body").asText().trim().equalsIgnoreCase("reset")) {
|
if ("text".equals(messageType) && messageNode.get("text").get("body").asText().trim().equalsIgnoreCase("reset")) {
|
||||||
sessionService.resetState(fromPhoneNumber);
|
sessionService.resetState(fromPhoneNumber);
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
|
if (sessionService.isFaculty(fromPhoneNumber)) {
|
||||||
|
facultyMenuHandler.sendMainMenu(fromPhoneNumber);
|
||||||
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(fromPhoneNumber);
|
studentMenuHandler.sendMainMenu(fromPhoneNumber);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loginHandler.startLoginFlow(fromPhoneNumber);
|
loginHandler.startLoginFlow(fromPhoneNumber);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok("EVENT_RECEIVED");
|
return ResponseEntity.ok("EVENT_RECEIVED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global erase command
|
||||||
|
if ("text".equals(messageType) && messageNode.get("text").get("body").asText().trim().equalsIgnoreCase("erase")) {
|
||||||
|
sessionService.deleteUser(fromPhoneNumber);
|
||||||
|
messageService.sendMessage(fromPhoneNumber, "Your IMS credentials have been deleted from our records. Type 'Hi' to start again.");
|
||||||
|
return ResponseEntity.ok("EVENT_RECEIVED");
|
||||||
|
}
|
||||||
|
|
||||||
// Route unauthenticated users strictly to Login Flow
|
// Route unauthenticated users strictly to Login Flow
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
if ("text".equals(messageType)) {
|
if ("text".equals(messageType)) {
|
||||||
@@ -116,9 +130,13 @@ public class WebhookController {
|
|||||||
String mediaId = imageNode.get("id").asText();
|
String mediaId = imageNode.get("id").asText();
|
||||||
String mimeType = imageNode.has("mime_type") ? imageNode.get("mime_type").asText() : "image/jpeg";
|
String mimeType = imageNode.has("mime_type") ? imageNode.get("mime_type").asText() : "image/jpeg";
|
||||||
grievanceHandler.handleImageInput(fromPhoneNumber, mediaId, mimeType);
|
grievanceHandler.handleImageInput(fromPhoneNumber, mediaId, mimeType);
|
||||||
|
} else {
|
||||||
|
if (sessionService.isFaculty(fromPhoneNumber)) {
|
||||||
|
facultyMenuHandler.sendMainMenu(fromPhoneNumber);
|
||||||
} else {
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(fromPhoneNumber);
|
studentMenuHandler.sendMainMenu(fromPhoneNumber);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if ("interactive".equals(messageType)) {
|
} else if ("interactive".equals(messageType)) {
|
||||||
JsonNode interactiveNode = messageNode.get("interactive");
|
JsonNode interactiveNode = messageNode.get("interactive");
|
||||||
String interactiveType = interactiveNode.get("type").asText();
|
String interactiveType = interactiveNode.get("type").asText();
|
||||||
@@ -170,9 +188,16 @@ public class WebhookController {
|
|||||||
messageService.sendMessage(phoneNumber, "Please send a photo or type 'skip' / click 'Skip Image' button.");
|
messageService.sendMessage(phoneNumber, "Please send a photo or type 'skip' / click 'Skip Image' button.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case AWAITING_ABSENTEES:
|
||||||
|
facultyMenuHandler.handleAbsenteesInput(phoneNumber, messageBody);
|
||||||
|
break;
|
||||||
case MAIN_MENU:
|
case MAIN_MENU:
|
||||||
default:
|
default:
|
||||||
|
if (sessionService.isFaculty(phoneNumber)) {
|
||||||
|
facultyMenuHandler.sendMainMenu(phoneNumber);
|
||||||
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,16 +213,27 @@ public class WebhookController {
|
|||||||
grievanceHandler.handleDeleteSelection(phoneNumber, selectedListId);
|
grievanceHandler.handleDeleteSelection(phoneNumber, selectedListId);
|
||||||
} else if (selectedListId.startsWith("student_")) {
|
} else if (selectedListId.startsWith("student_")) {
|
||||||
studentMenuHandler.handleMenuSelection(phoneNumber, selectedListId);
|
studentMenuHandler.handleMenuSelection(phoneNumber, selectedListId);
|
||||||
|
} else if (selectedListId.startsWith("faculty_")) {
|
||||||
|
facultyMenuHandler.handleMenuSelection(phoneNumber, selectedListId);
|
||||||
} else if (selectedListId.startsWith("sem_")) {
|
} else if (selectedListId.startsWith("sem_")) {
|
||||||
studentMenuHandler.handleSemesterSelection(phoneNumber, selectedListId);
|
studentMenuHandler.handleSemesterSelection(phoneNumber, selectedListId);
|
||||||
} else if (selectedListId.startsWith("receipt_") || selectedListId.startsWith("exam_") || selectedListId.startsWith("acad_") || selectedListId.startsWith("cons_")) {
|
} else if (selectedListId.startsWith("receipt_") || selectedListId.startsWith("exam_") || selectedListId.startsWith("acad_") || selectedListId.startsWith("cons_")) {
|
||||||
studentMenuHandler.handleReceiptSelection(phoneNumber, selectedListId);
|
studentMenuHandler.handleReceiptSelection(phoneNumber, selectedListId);
|
||||||
|
} else {
|
||||||
|
if (sessionService.isFaculty(phoneNumber)) {
|
||||||
|
facultyMenuHandler.sendMainMenu(phoneNumber);
|
||||||
} else {
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleInteractiveButtonSelection(String phoneNumber, String selectedButtonId, UserState currentState) {
|
private void handleInteractiveButtonSelection(String phoneNumber, String selectedButtonId, UserState currentState) {
|
||||||
|
if (selectedButtonId.startsWith("faculty_")) {
|
||||||
|
facultyMenuHandler.handleButtonSelection(phoneNumber, selectedButtonId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ("student_logout".equals(selectedButtonId)) {
|
if ("student_logout".equals(selectedButtonId)) {
|
||||||
sessionService.logoutUser(phoneNumber);
|
sessionService.logoutUser(phoneNumber);
|
||||||
messageService.sendMessage(phoneNumber, "You have been logged out successfully. Type 'Hi' to log in again.");
|
messageService.sendMessage(phoneNumber, "You have been logged out successfully. Type 'Hi' to log in again.");
|
||||||
@@ -246,8 +282,12 @@ public class WebhookController {
|
|||||||
|
|
||||||
if ("grv_skip_image".equals(selectedButtonId)) {
|
if ("grv_skip_image".equals(selectedButtonId)) {
|
||||||
grievanceHandler.handleSkipImage(phoneNumber);
|
grievanceHandler.handleSkipImage(phoneNumber);
|
||||||
|
} else {
|
||||||
|
if (sessionService.isFaculty(phoneNumber)) {
|
||||||
|
facultyMenuHandler.sendMainMenu(phoneNumber);
|
||||||
} else {
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 ImsApiService imsApiService;
|
||||||
private final StudentMenuHandler studentMenuHandler;
|
private final StudentMenuHandler studentMenuHandler;
|
||||||
|
|
||||||
|
private final FacultyMenuHandler facultyMenuHandler;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LoginHandler(WhatsAppMessageService messageService,
|
public LoginHandler(WhatsAppMessageService messageService,
|
||||||
SessionService sessionService,
|
SessionService sessionService,
|
||||||
ImsApiService imsApiService,
|
ImsApiService imsApiService,
|
||||||
StudentMenuHandler studentMenuHandler) {
|
StudentMenuHandler studentMenuHandler,
|
||||||
|
FacultyMenuHandler facultyMenuHandler) {
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.sessionService = sessionService;
|
this.sessionService = sessionService;
|
||||||
this.imsApiService = imsApiService;
|
this.imsApiService = imsApiService;
|
||||||
this.studentMenuHandler = studentMenuHandler;
|
this.studentMenuHandler = studentMenuHandler;
|
||||||
|
this.facultyMenuHandler = facultyMenuHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startLoginFlow(String phoneNumber) {
|
public void startLoginFlow(String phoneNumber) {
|
||||||
@@ -42,18 +46,22 @@ public class LoginHandler {
|
|||||||
|
|
||||||
messageService.sendMessage(phoneNumber, "Authenticating with IMS... ⏳");
|
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
|
// Authentication successful
|
||||||
sessionService.authenticateUser(phoneNumber, imsId, passcode, imsSessionToken);
|
sessionService.authenticateUser(phoneNumber, imsId, passcode, result);
|
||||||
sessionService.clearTempLoginId(phoneNumber);
|
sessionService.clearTempLoginId(phoneNumber);
|
||||||
sessionService.resetState(phoneNumber); // Reset the flow state
|
sessionService.resetState(phoneNumber); // Reset the flow state
|
||||||
|
|
||||||
messageService.sendMessage(phoneNumber, "✅ Login Successful!");
|
messageService.sendMessage(phoneNumber, "✅ Login Successful!");
|
||||||
|
|
||||||
// Show the main menu
|
// Show the main menu
|
||||||
|
if (result.isFaculty) {
|
||||||
|
facultyMenuHandler.sendMainMenu(phoneNumber);
|
||||||
|
} else {
|
||||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Authentication failed
|
// Authentication failed
|
||||||
sessionService.clearTempLoginId(phoneNumber);
|
sessionService.clearTempLoginId(phoneNumber);
|
||||||
|
|||||||
@@ -1,24 +1,38 @@
|
|||||||
package com.rit.whatsapp.model;
|
package com.rit.whatsapp.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "users")
|
@Table(name = "students")
|
||||||
public class UserEntity {
|
public class UserEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
private String phoneNumber;
|
@Column(name = "roll_no")
|
||||||
|
|
||||||
private String imsId;
|
private String imsId;
|
||||||
|
|
||||||
|
@Column(name = "mobile_no")
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
@Column(name = "encrypted_passcode")
|
||||||
private String encryptedPasscode;
|
private String encryptedPasscode;
|
||||||
|
|
||||||
|
@Column(name = "is_faculty", columnDefinition = "boolean default false")
|
||||||
|
private Boolean isFaculty = false;
|
||||||
|
|
||||||
public UserEntity() {
|
public UserEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserEntity(String phoneNumber, String imsId, String encryptedPasscode) {
|
public UserEntity(String imsId, String phoneNumber, String encryptedPasscode) {
|
||||||
|
this.imsId = imsId;
|
||||||
|
this.phoneNumber = phoneNumber;
|
||||||
|
this.encryptedPasscode = encryptedPasscode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep secondary constructor for backward compatibility
|
||||||
|
public UserEntity(String phoneNumber, String imsId, String encryptedPasscode, boolean legacyOrder) {
|
||||||
this.phoneNumber = phoneNumber;
|
this.phoneNumber = phoneNumber;
|
||||||
this.imsId = imsId;
|
this.imsId = imsId;
|
||||||
this.encryptedPasscode = encryptedPasscode;
|
this.encryptedPasscode = encryptedPasscode;
|
||||||
@@ -47,4 +61,12 @@ public class UserEntity {
|
|||||||
public void setEncryptedPasscode(String encryptedPasscode) {
|
public void setEncryptedPasscode(String encryptedPasscode) {
|
||||||
this.encryptedPasscode = encryptedPasscode;
|
this.encryptedPasscode = encryptedPasscode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFaculty() {
|
||||||
|
return isFaculty != null && isFaculty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFaculty(Boolean faculty) {
|
||||||
|
isFaculty = faculty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ public enum UserState {
|
|||||||
AWAITING_GRIEVANCE_DESC,
|
AWAITING_GRIEVANCE_DESC,
|
||||||
AWAITING_GRIEVANCE_IMAGE,
|
AWAITING_GRIEVANCE_IMAGE,
|
||||||
AWAITING_GRIEVANCE_STATUS_SELECTION,
|
AWAITING_GRIEVANCE_STATUS_SELECTION,
|
||||||
AWAITING_GRIEVANCE_DELETE_SELECTION
|
AWAITING_GRIEVANCE_DELETE_SELECTION,
|
||||||
|
AWAITING_ABSENTEES
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package com.rit.whatsapp.repository;
|
|||||||
import com.rit.whatsapp.model.UserEntity;
|
import com.rit.whatsapp.model.UserEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserRepository extends JpaRepository<UserEntity, String> {
|
public interface UserRepository extends JpaRepository<UserEntity, String> {
|
||||||
|
Optional<UserEntity> findByPhoneNumber(String phoneNumber);
|
||||||
|
boolean existsByPhoneNumber(String phoneNumber);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,22 @@ public class ImsApiService {
|
|||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AuthResult {
|
||||||
|
public final String token;
|
||||||
|
public final boolean isFaculty;
|
||||||
|
public AuthResult(String token, boolean isFaculty) {
|
||||||
|
this.token = token;
|
||||||
|
this.isFaculty = isFaculty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates the user against the real IMS backend.
|
* Authenticates the user against the real IMS backend.
|
||||||
* @param imsId The user ID
|
* @param imsId The user ID
|
||||||
* @param passcode The passcode
|
* @param passcode The passcode
|
||||||
* @return The API session token if successful, null otherwise.
|
* @return AuthResult if successful, null otherwise.
|
||||||
*/
|
*/
|
||||||
public String authenticate(String imsId, String passcode) {
|
public AuthResult authenticate(String imsId, String passcode) {
|
||||||
String url = BASE_URL + "/api/auth/login";
|
String url = BASE_URL + "/api/auth/login";
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
@@ -48,7 +57,12 @@ public class ImsApiService {
|
|||||||
JsonNode rootNode = objectMapper.readTree(response.getBody());
|
JsonNode rootNode = objectMapper.readTree(response.getBody());
|
||||||
if (rootNode.has("success") && rootNode.get("success").asBoolean()) {
|
if (rootNode.has("success") && rootNode.get("success").asBoolean()) {
|
||||||
if (rootNode.has("session")) {
|
if (rootNode.has("session")) {
|
||||||
return rootNode.get("session").asText();
|
String token = rootNode.get("session").asText();
|
||||||
|
boolean isFaculty = false;
|
||||||
|
if (rootNode.has("role")) {
|
||||||
|
isFaculty = "staff".equals(rootNode.get("role").asText());
|
||||||
|
}
|
||||||
|
return new AuthResult(token, isFaculty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +73,7 @@ public class ImsApiService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fetchFromIms(String endpoint, String token) {
|
public String fetchFromIms(String endpoint, String token) {
|
||||||
String url = BASE_URL + endpoint;
|
String url = BASE_URL + endpoint;
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setBearerAuth(token);
|
headers.setBearerAuth(token);
|
||||||
@@ -250,4 +264,65 @@ public class ImsApiService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFacultyProfile(String token) {
|
||||||
|
return fetchFromIms("/api/faculty/profile", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFacultyAttendanceSubjects(String token) {
|
||||||
|
return fetchFromIms("/api/faculty/attendance-subjects", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFacultyName(String token) {
|
||||||
|
String rawJson = getFacultyProfile(token);
|
||||||
|
if (rawJson == null) return "Faculty";
|
||||||
|
try {
|
||||||
|
JsonNode root = objectMapper.readTree(rawJson);
|
||||||
|
if (root.has("data")) {
|
||||||
|
JsonNode data = root.get("data");
|
||||||
|
if (data.has("name")) return data.get("name").asText();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to parse faculty profile JSON: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return "Faculty";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStudentListForAttendance(String token, Map<String, Object> params) {
|
||||||
|
String url = BASE_URL + "/api/faculty/attendance/list";
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setBearerAuth(token);
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
HttpEntity<Map<String, Object>> request = new HttpEntity<>(params, headers);
|
||||||
|
try {
|
||||||
|
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
|
||||||
|
return response.getBody();
|
||||||
|
} catch (org.springframework.web.client.HttpStatusCodeException e) {
|
||||||
|
System.err.println("Failed to fetch student list (HTTP error): " + e.getResponseBodyAsString());
|
||||||
|
return e.getResponseBodyAsString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to fetch student list: " + e.getMessage());
|
||||||
|
return "{\"success\":false,\"message\":\"" + e.getMessage() + "\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String submitAttendance(String token, Map<String, Object> postData) {
|
||||||
|
String url = BASE_URL + "/api/faculty/attendance/take_periods";
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setBearerAuth(token);
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
HttpEntity<Map<String, Object>> request = new HttpEntity<>(postData, headers);
|
||||||
|
try {
|
||||||
|
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
|
||||||
|
return response.getBody();
|
||||||
|
} catch (org.springframework.web.client.HttpStatusCodeException e) {
|
||||||
|
System.err.println("Failed to submit attendance (HTTP error): " + e.getResponseBodyAsString());
|
||||||
|
return e.getResponseBodyAsString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to submit attendance: " + e.getMessage());
|
||||||
|
return "{\"success\":false,\"message\":\"" + e.getMessage() + "\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.rit.whatsapp.util.EncryptionUtils;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@@ -48,7 +49,7 @@ public class SessionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSavedCredentials(String phoneNumber) {
|
public boolean hasSavedCredentials(String phoneNumber) {
|
||||||
return userRepository.existsById(phoneNumber);
|
return userRepository.existsByPhoneNumber(phoneNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetState(String phoneNumber) {
|
public void resetState(String phoneNumber) {
|
||||||
@@ -61,15 +62,22 @@ public class SessionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check database for persistent session
|
// Check database for persistent session
|
||||||
Optional<UserEntity> userOpt = userRepository.findById(phoneNumber);
|
Optional<UserEntity> userOpt = userRepository.findByPhoneNumber(phoneNumber);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
UserEntity user = userOpt.get();
|
UserEntity user = userOpt.get();
|
||||||
|
if (user.getEncryptedPasscode() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String decryptedPasscode = encryptionUtils.decrypt(user.getEncryptedPasscode());
|
String decryptedPasscode = encryptionUtils.decrypt(user.getEncryptedPasscode());
|
||||||
|
|
||||||
// Silent background login
|
// Silent background login
|
||||||
String token = imsApiService.authenticate(user.getImsId(), decryptedPasscode);
|
ImsApiService.AuthResult result = imsApiService.authenticate(user.getImsId(), decryptedPasscode);
|
||||||
if (token != null) {
|
if (result != null) {
|
||||||
sessionTokens.put(phoneNumber, token);
|
sessionTokens.put(phoneNumber, result.token);
|
||||||
|
if (user.isFaculty() != result.isFaculty) {
|
||||||
|
user.setFaculty(result.isFaculty);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,12 +85,21 @@ public class SessionService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void authenticateUser(String phoneNumber, String imsId, String passcode, String token) {
|
public void authenticateUser(String phoneNumber, String imsId, String passcode, ImsApiService.AuthResult result) {
|
||||||
sessionTokens.put(phoneNumber, token);
|
sessionTokens.put(phoneNumber, result.token);
|
||||||
|
|
||||||
// Persist to database
|
// Persist to database (students table)
|
||||||
|
Optional<UserEntity> userOpt = userRepository.findById(imsId);
|
||||||
|
UserEntity user;
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
user = userOpt.get();
|
||||||
|
user.setPhoneNumber(phoneNumber);
|
||||||
|
user.setEncryptedPasscode(encryptionUtils.encrypt(passcode));
|
||||||
|
} else {
|
||||||
String encryptedPasscode = encryptionUtils.encrypt(passcode);
|
String encryptedPasscode = encryptionUtils.encrypt(passcode);
|
||||||
UserEntity user = new UserEntity(phoneNumber, imsId, encryptedPasscode);
|
user = new UserEntity(imsId, phoneNumber, encryptedPasscode);
|
||||||
|
}
|
||||||
|
user.setFaculty(result.isFaculty);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +116,12 @@ public class SessionService {
|
|||||||
if (token != null) {
|
if (token != null) {
|
||||||
imsApiService.logout(token);
|
imsApiService.logout(token);
|
||||||
}
|
}
|
||||||
userRepository.deleteById(phoneNumber);
|
Optional<UserEntity> userOpt = userRepository.findByPhoneNumber(phoneNumber);
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
UserEntity user = userOpt.get();
|
||||||
|
user.setEncryptedPasscode(null);
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
studentNames.remove(phoneNumber);
|
studentNames.remove(phoneNumber);
|
||||||
resetState(phoneNumber);
|
resetState(phoneNumber);
|
||||||
}
|
}
|
||||||
@@ -108,11 +130,53 @@ public class SessionService {
|
|||||||
if (studentNames.containsKey(phoneNumber)) {
|
if (studentNames.containsKey(phoneNumber)) {
|
||||||
return studentNames.get(phoneNumber);
|
return studentNames.get(phoneNumber);
|
||||||
}
|
}
|
||||||
String name = imsApiService.getStudentName(token);
|
String name;
|
||||||
|
if (isFaculty(phoneNumber)) {
|
||||||
|
name = imsApiService.getFacultyName(token);
|
||||||
|
} else {
|
||||||
|
name = imsApiService.getStudentName(token);
|
||||||
|
}
|
||||||
studentNames.put(phoneNumber, name);
|
studentNames.put(phoneNumber, name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Map<String, List<Map<String, Object>>> attendanceSubjectParams = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, Map<String, Object>> activeAttendanceParams = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, List<com.rit.whatsapp.util.MessageFormatter.StudentAttendanceInfo>> activeStudentLists = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void storeAttendanceSubjectParams(String phoneNumber, List<Map<String, Object>> paramsList) {
|
||||||
|
attendanceSubjectParams.put(phoneNumber, paramsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAttendanceSubjectParams(String phoneNumber, int index) {
|
||||||
|
List<Map<String, Object>> paramsList = attendanceSubjectParams.get(phoneNumber);
|
||||||
|
if (paramsList != null && index >= 0 && index < paramsList.size()) {
|
||||||
|
return paramsList.get(index);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveAttendanceParams(String phoneNumber, Map<String, Object> params) {
|
||||||
|
activeAttendanceParams.put(phoneNumber, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getActiveAttendanceParams(String phoneNumber) {
|
||||||
|
return activeAttendanceParams.get(phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearActiveAttendanceParams(String phoneNumber) {
|
||||||
|
activeAttendanceParams.remove(phoneNumber);
|
||||||
|
activeStudentLists.remove(phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void storeActiveStudentList(String phoneNumber, List<com.rit.whatsapp.util.MessageFormatter.StudentAttendanceInfo> list) {
|
||||||
|
activeStudentLists.put(phoneNumber, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.rit.whatsapp.util.MessageFormatter.StudentAttendanceInfo> getActiveStudentList(String phoneNumber) {
|
||||||
|
return activeStudentLists.get(phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
private final Map<String, Map<String, String>> receiptIdMappings = new ConcurrentHashMap<>();
|
private final Map<String, Map<String, String>> receiptIdMappings = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public void storeReceiptIdMappings(String phoneNumber, Map<String, String> mappings) {
|
public void storeReceiptIdMappings(String phoneNumber, Map<String, String> mappings) {
|
||||||
@@ -158,10 +222,26 @@ public class SessionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getImsId(String phoneNumber) {
|
public String getImsId(String phoneNumber) {
|
||||||
Optional<UserEntity> userOpt = userRepository.findById(phoneNumber);
|
Optional<UserEntity> userOpt = userRepository.findByPhoneNumber(phoneNumber);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
return userOpt.get().getImsId();
|
return userOpt.get().getImsId();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFaculty(String phoneNumber) {
|
||||||
|
Optional<UserEntity> userOpt = userRepository.findByPhoneNumber(phoneNumber);
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
return userOpt.get().isFaculty();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUser(String phoneNumber) {
|
||||||
|
logoutUser(phoneNumber);
|
||||||
|
Optional<UserEntity> userOpt = userRepository.findByPhoneNumber(phoneNumber);
|
||||||
|
if (userOpt.isPresent()) {
|
||||||
|
userRepository.delete(userOpt.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1244,4 +1244,310 @@ public class MessageFormatter {
|
|||||||
if (text.length() <= length) return text;
|
if (text.length() <= length) return text;
|
||||||
return text.substring(0, length);
|
return text.substring(0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatFacultyTimetable(String rawJson) {
|
||||||
|
if (rawJson == null) return "❌ Failed to retrieve timetable data.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode root = mapper.readTree(rawJson);
|
||||||
|
if (!root.has("data") || !root.get("data").isObject() || !root.get("data").has("schedule")) {
|
||||||
|
return "❌ Timetable details not available in this format.";
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode schedule = root.get("data").get("schedule");
|
||||||
|
String day = java.time.LocalDate.now().getDayOfWeek().name().toLowerCase();
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("📅 *Today's Classes (Faculty)*\n");
|
||||||
|
sb.append("━━━━━━━━━━━━━━\n\n");
|
||||||
|
|
||||||
|
if (schedule.has(day)) {
|
||||||
|
JsonNode daySchedule = schedule.get(day);
|
||||||
|
|
||||||
|
int maxPeriod = 7;
|
||||||
|
java.util.Iterator<String> fieldNames = daySchedule.fieldNames();
|
||||||
|
while (fieldNames.hasNext()) {
|
||||||
|
String fName = fieldNames.next();
|
||||||
|
try {
|
||||||
|
int p = Integer.parseInt(fName);
|
||||||
|
if (p > maxPeriod) maxPeriod = p;
|
||||||
|
} catch (NumberFormatException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int period = 1; period <= maxPeriod; period++) {
|
||||||
|
String periodKey = String.valueOf(period);
|
||||||
|
sb.append(getNumberEmoji(period)).append(" ");
|
||||||
|
|
||||||
|
if (daySchedule.has(periodKey)) {
|
||||||
|
JsonNode periodNode = daySchedule.get(periodKey);
|
||||||
|
java.util.List<String> entries = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
if (periodNode.isArray()) {
|
||||||
|
for (JsonNode node : periodNode) {
|
||||||
|
String sName = node.has("subjectName") ? node.get("subjectName").asText() : "";
|
||||||
|
String sCode = node.has("subjectCode") ? node.get("subjectCode").asText() : "";
|
||||||
|
String cName = node.has("className") ? node.get("className").asText() : "";
|
||||||
|
entries.add("*" + sName + "*\n `" + sCode + "` - " + cName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entries.isEmpty()) {
|
||||||
|
sb.append(String.join(" / ", entries)).append("\n\n");
|
||||||
|
} else {
|
||||||
|
sb.append("*Free Period*\n\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append("*Free Period*\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append("No classes scheduled for today.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("━━━━━━━━━━━━━━");
|
||||||
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error formatting faculty timetable: " + e.getMessage());
|
||||||
|
return "⚠️ Error formatting timetable data.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatFacultySubjects(String rawJson) {
|
||||||
|
if (rawJson == null) return "❌ Failed to retrieve subjects data.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode root = mapper.readTree(rawJson);
|
||||||
|
if (!root.has("data") || !root.get("data").isObject() || !root.get("data").has("subjects")) {
|
||||||
|
return "❌ Subjects details not available in this format.";
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode subjects = root.get("data").get("subjects");
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("📚 *Your Subjects*\n");
|
||||||
|
sb.append("━━━━━━━━━━━━━━\n\n");
|
||||||
|
|
||||||
|
if (subjects.isArray() && subjects.size() > 0) {
|
||||||
|
for (JsonNode node : subjects) {
|
||||||
|
String sName = node.has("subjectName") ? node.get("subjectName").asText() : "";
|
||||||
|
String sCode = node.has("subjectCode") ? node.get("subjectCode").asText() : "";
|
||||||
|
String cName = node.has("className") ? node.get("className").asText() : "";
|
||||||
|
|
||||||
|
sb.append("🔹 *").append(sName).append("*\n");
|
||||||
|
sb.append(" Code: `").append(sCode).append("`\n");
|
||||||
|
sb.append(" Class: ").append(cName).append("\n\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append("No subjects assigned currently.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("━━━━━━━━━━━━━━");
|
||||||
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error formatting faculty subjects: " + e.getMessage());
|
||||||
|
return "⚠️ Error formatting subjects data.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatFacultyAttendanceSubjects(String rawJson) {
|
||||||
|
if (rawJson == null) return "❌ Failed to retrieve attendance subjects data.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode root = mapper.readTree(rawJson);
|
||||||
|
if (!root.has("data") || !root.get("data").isObject()) {
|
||||||
|
return "❌ Attendance details not available in this format.";
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode data = root.get("data");
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("📝 *Attendance Pending*\n");
|
||||||
|
sb.append("━━━━━━━━━━━━━━\n\n");
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
if (data.has("regularSubjects")) {
|
||||||
|
JsonNode regular = data.get("regularSubjects");
|
||||||
|
if (regular.isArray() && regular.size() > 0) {
|
||||||
|
sb.append("*Regular Subjects:*\n");
|
||||||
|
for (JsonNode node : regular) {
|
||||||
|
String sName = node.has("subject") ? node.get("subject").asText() : "";
|
||||||
|
String pName = node.has("allotedPeriod") ? node.get("allotedPeriod").asText() : "";
|
||||||
|
String cName = node.has("className") ? node.get("className").asText() : "";
|
||||||
|
|
||||||
|
sb.append("🔹 Period ").append(pName).append(": *").append(sName).append("*\n");
|
||||||
|
sb.append(" Class: ").append(cName).append("\n\n");
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
sb.append("No attendance pending to mark today.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("━━━━━━━━━━━━━━");
|
||||||
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error formatting faculty attendance subjects: " + e.getMessage());
|
||||||
|
return "⚠️ Error formatting attendance subjects data.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StudentAttendanceInfo {
|
||||||
|
public String id;
|
||||||
|
public String regNo;
|
||||||
|
public String name;
|
||||||
|
public StudentAttendanceInfo(String id, String regNo, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.regNo = regNo;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static java.util.List<StudentAttendanceInfo> parseStudentList(String html) {
|
||||||
|
java.util.List<StudentAttendanceInfo> list = new java.util.ArrayList<>();
|
||||||
|
String cleanHtml = html.replaceAll("\\s+", " ");
|
||||||
|
java.util.regex.Matcher rowMatcher = java.util.regex.Pattern.compile("<tr[^>]*>(.*?)</tr>").matcher(cleanHtml);
|
||||||
|
int serial = 1;
|
||||||
|
while (rowMatcher.find()) {
|
||||||
|
String rowContent = rowMatcher.group(1);
|
||||||
|
java.util.regex.Matcher idMatcher = java.util.regex.Pattern.compile("<input[^>]+value=\"(\\d+)\"[^>]*name=\"student_id\\[\\]\"").matcher(rowContent);
|
||||||
|
if (!idMatcher.find()) {
|
||||||
|
idMatcher = java.util.regex.Pattern.compile("<input[^>]+name=\"student_id\\[\\]\"[^>]+value=\"(\\d+)\"").matcher(rowContent);
|
||||||
|
}
|
||||||
|
if (idMatcher.find()) {
|
||||||
|
String id = idMatcher.group(1);
|
||||||
|
java.util.List<String> cells = new java.util.ArrayList<>();
|
||||||
|
java.util.regex.Matcher cellMatcher = java.util.regex.Pattern.compile("<td[^>]*>(.*?)</td>").matcher(rowContent);
|
||||||
|
while (cellMatcher.find()) {
|
||||||
|
cells.add(cellMatcher.group(1).replaceAll("<[^>]*>", "").trim());
|
||||||
|
}
|
||||||
|
String regNo = cells.size() > 1 ? cells.get(1) : "";
|
||||||
|
String name = cells.size() > 2 ? cells.get(2) : "";
|
||||||
|
if (regNo.isEmpty()) {
|
||||||
|
regNo = "Roll " + serial;
|
||||||
|
}
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = "Student " + serial;
|
||||||
|
}
|
||||||
|
list.add(new StudentAttendanceInfo(id, regNo, name));
|
||||||
|
serial++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static java.util.List<StudentAttendanceInfo> parseStudents(String rawJsonOrHtml) {
|
||||||
|
java.util.List<StudentAttendanceInfo> list = new java.util.ArrayList<>();
|
||||||
|
if (rawJsonOrHtml == null) return list;
|
||||||
|
try {
|
||||||
|
JsonNode root = mapper.readTree(rawJsonOrHtml);
|
||||||
|
JsonNode dataNode = root.has("data") ? root.get("data") : root;
|
||||||
|
|
||||||
|
if (dataNode.isArray()) {
|
||||||
|
for (JsonNode student : dataNode) {
|
||||||
|
String id = student.has("id") ? student.get("id").asText() : "";
|
||||||
|
String name = student.has("name") ? student.get("name").asText() :
|
||||||
|
(student.has("student_name") ? student.get("student_name").asText() : "");
|
||||||
|
String regNo = student.has("roll_no") ? student.get("roll_no").asText() :
|
||||||
|
(student.has("register_number") ? student.get("register_number").asText() :
|
||||||
|
(student.has("reg_no") ? student.get("reg_no").asText() : ""));
|
||||||
|
if (!id.isEmpty()) {
|
||||||
|
list.add(new StudentAttendanceInfo(id, regNo, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} else if (dataNode.isTextual()) {
|
||||||
|
return parseStudentList(dataNode.asText());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return parseStudentList(rawJsonOrHtml);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatFacultyWeeklyTimetable(String rawJson) {
|
||||||
|
if (rawJson == null) return "❌ Failed to retrieve weekly timetable data.";
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode root = mapper.readTree(rawJson);
|
||||||
|
if (!root.has("data") || !root.get("data").isObject() || !root.get("data").has("schedule")) {
|
||||||
|
return "❌ Weekly timetable details not available in the response.";
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode schedule = root.get("data").get("schedule");
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("📅 *Weekly Timetable (Faculty)*\n\n");
|
||||||
|
|
||||||
|
String[] days = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||||
|
String[] dayLabels = {"MON", "TUE", "WED", "THU", "FRI", "SAT"};
|
||||||
|
boolean hasAnyClasses = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < days.length; i++) {
|
||||||
|
String day = days[i];
|
||||||
|
String label = dayLabels[i];
|
||||||
|
|
||||||
|
if (schedule.has(day)) {
|
||||||
|
JsonNode daySchedule = schedule.get(day);
|
||||||
|
|
||||||
|
StringBuilder daySb = new StringBuilder();
|
||||||
|
daySb.append("📅 *").append(label).append("*\n");
|
||||||
|
boolean dayHasClasses = false;
|
||||||
|
|
||||||
|
int maxPeriod = 7;
|
||||||
|
java.util.Iterator<String> fieldNames = daySchedule.fieldNames();
|
||||||
|
while (fieldNames.hasNext()) {
|
||||||
|
try {
|
||||||
|
int p = Integer.parseInt(fieldNames.next());
|
||||||
|
if (p > maxPeriod) {
|
||||||
|
maxPeriod = p;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int period = 1; period <= maxPeriod; period++) {
|
||||||
|
String periodKey = String.valueOf(period);
|
||||||
|
|
||||||
|
if (daySchedule.has(periodKey)) {
|
||||||
|
JsonNode details = daySchedule.get(periodKey);
|
||||||
|
java.util.Set<String> subjects = new java.util.LinkedHashSet<>();
|
||||||
|
|
||||||
|
if (details.isArray()) {
|
||||||
|
for (JsonNode entry : details) {
|
||||||
|
String name = entry.has("subjectName") ? entry.get("subjectName").asText() : "";
|
||||||
|
String code = entry.has("subjectCode") ? entry.get("subjectCode").asText() : "";
|
||||||
|
String cName = entry.has("className") ? entry.get("className").asText() : "";
|
||||||
|
if (!name.trim().isEmpty()) {
|
||||||
|
String entryStr = name.trim() + " (" + code.trim() + ")";
|
||||||
|
if (!cName.isEmpty()) {
|
||||||
|
entryStr += " - " + cName.trim();
|
||||||
|
}
|
||||||
|
subjects.add(entryStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subjects.isEmpty()) {
|
||||||
|
daySb.append(" ").append(getNumberEmoji(period)).append(" ").append(String.join(" / ", subjects)).append("\n");
|
||||||
|
dayHasClasses = true;
|
||||||
|
hasAnyClasses = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dayHasClasses) {
|
||||||
|
sb.append(daySb).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAnyClasses) {
|
||||||
|
return "📅 *Weekly Timetable*\n\nNo classes scheduled for this week.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString().trim();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "⚠️ Error formatting weekly timetable data.";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ whatsapp.verify-token=rit_bot_secret_token_123
|
|||||||
|
|
||||||
# WhatsApp Graph API Token (You get this from the Meta developer console)
|
# WhatsApp Graph API Token (You get this from the Meta developer console)
|
||||||
# WhatsApp Phone Number ID (You get this from the Meta developer console)
|
# WhatsApp Phone Number ID (You get this from the Meta developer console)
|
||||||
whatsapp.phone-number-id=1236658909534007
|
whatsapp.phone-number-id=1239246362613929
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL Database Configuration
|
# PostgreSQL Database Configuration
|
||||||
spring.datasource.url=${DB_URL}
|
spring.datasource.url=${DB_URL}
|
||||||
@@ -18,4 +19,4 @@ spring.jpa.hibernate.ddl-auto=update
|
|||||||
|
|
||||||
# Encryption Secret for Passcodes (Must be 16, 24, or 32 bytes)
|
# Encryption Secret for Passcodes (Must be 16, 24, or 32 bytes)
|
||||||
bot.encryption.secret=12345678901234567890123456789012
|
bot.encryption.secret=12345678901234567890123456789012
|
||||||
whatsapp.api-token=EAAc3Wjql00oBSdZAyOjO81it8Tnpiqa1mmJq9c3WFzwXmYvjZC1RaXLlJDBe14zrCQ7DA2l5pzAV5y8sinZCBmTzlp9SUXvrcMMzmMDJ7rN58KHBqP6MAIkxcyQZCpewbFqB3mp4xwUHJ89GRZB88rsLpvJFMZABWNbfsaTZAQ0ZCabRcAvL0iJ528UGJJnv98y9F9EMerZCi3KZCGYfvIZBuYBM8tL2XWmesQir8Dlj2ZCjBmtuDLGOEQ59lHZBq1XrEbhbzlWOzH6EdUZBOajYDHn8rcZBdGZC
|
whatsapp.api-token=EAAc3Wjql00oBSTlrhFqV7uer16ZAEy5B0c4GXZAForXZCTGp8rUcpyL5yAbQvOFjC2T0ZAgUmTFSMDT00SGbaaQVQLlYqZBOmkd91tqVCSSTGJZCCzBB2pPEHr8ckxTZCSe0srBZAGl7IHX8ucODcwuzQm8TZBy2RrnGG4gAUZB9a0HHO4uFYZAZCyUZAv3i65GVRfainyAZDZD
|
||||||
|
|||||||
Reference in New Issue
Block a user