added various fields inside the dropdown
This commit is contained in:
@@ -155,8 +155,25 @@ public class WebhookController {
|
||||
}
|
||||
|
||||
private void handleInteractiveListSelection(String phoneNumber, String selectedListId, UserState currentState) {
|
||||
if (currentState == UserState.MAIN_MENU) {
|
||||
if (selectedListId.startsWith("student_")) {
|
||||
// They clicked an option from the main menu
|
||||
studentMenuHandler.handleMenuSelection(phoneNumber, selectedListId);
|
||||
} else if (selectedListId.startsWith("sem_")) {
|
||||
// They clicked an option from the semester selection list
|
||||
if (currentState == UserState.AWAITING_SEMESTER_SELECTION) {
|
||||
studentMenuHandler.handleSemesterSelection(phoneNumber, selectedListId);
|
||||
} else {
|
||||
messageService.sendMessage(phoneNumber, "This menu selection is no longer active.");
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
}
|
||||
} else if (selectedListId.startsWith("receipt_")) {
|
||||
// They clicked a receipt download option
|
||||
if (currentState == UserState.AWAITING_RECEIPT_SELECTION) {
|
||||
studentMenuHandler.handleReceiptSelection(phoneNumber, selectedListId);
|
||||
} else {
|
||||
messageService.sendMessage(phoneNumber, "This menu selection is no longer active.");
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
}
|
||||
} else {
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
}
|
||||
|
||||
@@ -36,41 +36,23 @@ public class StudentMenuHandler {
|
||||
public void sendMainMenu(String phoneNumber) {
|
||||
sessionService.setUserState(phoneNumber, UserState.MAIN_MENU);
|
||||
|
||||
List<Map<String, String>> rows = new ArrayList<>();
|
||||
rows.add(createRow("student_attendance", "Attendance", "Overall % and Subject-wise"));
|
||||
rows.add(createRow("student_timetable", "Timetable", "Today's classes and Weekly"));
|
||||
rows.add(createRow("student_internal_marks", "Internal marks", "CAT 1, CAT 2, Overall"));
|
||||
rows.add(createRow("student_semester_results", "Semester results", "Latest result and CGPA"));
|
||||
rows.add(createRow("student_fee_details", "Fee details & receipt", "Fee paid, Pending, Due date"));
|
||||
rows.add(createRow("student_academic_calendar", "Academic calendar", "Holidays and Important dates"));
|
||||
rows.add(createRow("student_exam_schedule", "Exam schedule", "Upcoming exams and Hall details"));
|
||||
rows.add(createRow("student_circulars", "Circulars & Notices", "Department circulars"));
|
||||
rows.add(createRow("student_raise_grievance", "Raise a grievance \u2B50", "Submit an issue ticket"));
|
||||
rows.add(createRow("student_contact_department", "Contact department", "Faculty mentor, HOD, etc."));
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
String studentName = "Student";
|
||||
if (token != null) {
|
||||
studentName = sessionService.getStudentName(phoneNumber, token);
|
||||
}
|
||||
|
||||
Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
"RIT Digital Assistant",
|
||||
"Welcome to the RITz Student Portal! Please select an option from the menu below.",
|
||||
"Welcome " + studentName + "! Please select an option from the menu below.",
|
||||
"Student Menu",
|
||||
rows
|
||||
getStudentMenuRows()
|
||||
);
|
||||
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
|
||||
// Send a separate Logout button since the list is maxed out at 10 items
|
||||
List<Map<String, String>> logoutBtn = new ArrayList<>();
|
||||
Map<String, String> btn = new HashMap<>();
|
||||
btn.put("id", "student_logout");
|
||||
btn.put("title", "Logout");
|
||||
logoutBtn.add(btn);
|
||||
|
||||
Map<String, Object> logoutMessage = WhatsAppMessageBuilder.buildButtonMessage(
|
||||
"Need to switch accounts?",
|
||||
logoutBtn
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, logoutMessage);
|
||||
}
|
||||
|
||||
|
||||
public void handleMenuSelection(String phoneNumber, String selectedOptionId) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
@@ -81,23 +63,67 @@ public class StudentMenuHandler {
|
||||
switch (selectedOptionId) {
|
||||
case "student_attendance":
|
||||
String rawAttendance = imsApiService.getAttendance(token);
|
||||
messageService.sendMessage(phoneNumber, MessageFormatter.formatAttendance(rawAttendance));
|
||||
String rawLeaves = imsApiService.getLeaves(token);
|
||||
sendResponseWithMenu(phoneNumber, MessageFormatter.formatAttendanceAndLeaves(rawAttendance, rawLeaves));
|
||||
break;
|
||||
|
||||
case "student_timetable":
|
||||
String rawTimetable = imsApiService.getTimetable(token);
|
||||
messageService.sendMessage(phoneNumber, MessageFormatter.formatTimetable(rawTimetable));
|
||||
sendResponseWithMenu(phoneNumber, MessageFormatter.formatTimetable(rawTimetable));
|
||||
break;
|
||||
|
||||
case "student_internal_marks":
|
||||
String rawCatMarks = imsApiService.getCatMarks(token);
|
||||
String rawAssignmentMarks = imsApiService.getAssignmentMarks(token);
|
||||
sendResponseWithMenu(phoneNumber, MessageFormatter.formatInternalMarks(rawCatMarks, rawAssignmentMarks));
|
||||
break;
|
||||
|
||||
case "student_semester_results":
|
||||
// Defaulting to semester 1 for now
|
||||
String rawResults = imsApiService.getResults(token, 1);
|
||||
messageService.sendMessage(phoneNumber, MessageFormatter.formatResults(rawResults, 1));
|
||||
sendSemesterSelection(phoneNumber, token);
|
||||
break;
|
||||
|
||||
case "student_fee_details":
|
||||
String rawFees = imsApiService.getFees(token);
|
||||
String rawExamFees = imsApiService.getExamFees(token);
|
||||
String bodyText = MessageFormatter.formatFeesAndExamFees(rawFees, rawExamFees);
|
||||
|
||||
List<Map<String, String>> receiptRows = MessageFormatter.extractReceiptRows(rawExamFees);
|
||||
|
||||
if (!receiptRows.isEmpty()) {
|
||||
// Max 10 items in a list. Limit to 10 if necessary.
|
||||
if (receiptRows.size() > 10) receiptRows = receiptRows.subList(0, 10);
|
||||
|
||||
sessionService.setUserState(phoneNumber, UserState.AWAITING_RECEIPT_SELECTION);
|
||||
|
||||
// Meta API interactive list body limit is 1024 characters
|
||||
if (bodyText.length() > 1024) bodyText = bodyText.substring(0, 1021) + "...";
|
||||
|
||||
Map<String, Object> listMessage = WhatsAppMessageBuilder.buildListMessage(
|
||||
"Receipts", bodyText, "Download Receipt", receiptRows
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, listMessage);
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, bodyText);
|
||||
}
|
||||
break;
|
||||
|
||||
case "student_raise_grievance":
|
||||
grievanceHandler.startGrievanceFlow(phoneNumber);
|
||||
break;
|
||||
|
||||
case "student_help_menu":
|
||||
sendHelpMenu(phoneNumber);
|
||||
break;
|
||||
|
||||
case "student_logout":
|
||||
sessionService.logoutUser(phoneNumber);
|
||||
messageService.sendMessage(phoneNumber, "You have been logged out successfully. Type 'Hi' to log in again.");
|
||||
break;
|
||||
|
||||
case "student_contact_department":
|
||||
messageService.sendMessage(phoneNumber, "Contact Department is currently under development. Please check back later!");
|
||||
sendMainMenu(phoneNumber);
|
||||
break;
|
||||
|
||||
default:
|
||||
messageService.sendMessage(phoneNumber, "You selected an option that is currently under development. Please check back later!");
|
||||
@@ -107,6 +133,134 @@ public class StudentMenuHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSemesterSelection(String phoneNumber, String token) {
|
||||
sessionService.setUserState(phoneNumber, UserState.AWAITING_SEMESTER_SELECTION);
|
||||
|
||||
List<Map<String, String>> rows = new ArrayList<>();
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
rows.add(createRow("sem_" + i, "Semester " + i, ""));
|
||||
}
|
||||
|
||||
String rawDashboard = imsApiService.getDashboard(token);
|
||||
String bodyText = MessageFormatter.formatDashboardCgpa(rawDashboard);
|
||||
|
||||
Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
"Semester Results",
|
||||
bodyText,
|
||||
"Select Semester",
|
||||
rows
|
||||
);
|
||||
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
}
|
||||
|
||||
public void handleSemesterSelection(String phoneNumber, String selectedOptionId) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int semester = Integer.parseInt(selectedOptionId.replace("sem_", ""));
|
||||
byte[] pdfBytes = imsApiService.getResultsPdf(token, semester);
|
||||
|
||||
if (pdfBytes != null) {
|
||||
String fileName = "Semester_" + semester + "_Results.pdf";
|
||||
String mediaId = messageService.uploadMedia(pdfBytes, fileName, "application/pdf");
|
||||
|
||||
if (mediaId != null) {
|
||||
messageService.sendDocumentMessage(phoneNumber, mediaId, "Here are your Semester " + semester + " results!", fileName);
|
||||
// Send menu afterwards
|
||||
sendResponseWithMenu(phoneNumber, "Select an option from the menu below.");
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "Failed to upload the results document to WhatsApp.");
|
||||
}
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "Failed to download your results. They may not be published yet.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendResponseWithMenu(phoneNumber, "Invalid semester selected.");
|
||||
}
|
||||
|
||||
sessionService.resetState(phoneNumber); // Reset state after fetching results
|
||||
}
|
||||
|
||||
public void handleReceiptSelection(String phoneNumber, String selectedOptionId) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String historyId = selectedOptionId.replace("receipt_", "");
|
||||
byte[] pdfBytes = imsApiService.getExamFeeReceiptPdf(token, historyId);
|
||||
|
||||
if (pdfBytes != null) {
|
||||
String fileName = "Receipt_" + historyId + ".pdf";
|
||||
String mediaId = messageService.uploadMedia(pdfBytes, fileName, "application/pdf");
|
||||
|
||||
if (mediaId != null) {
|
||||
messageService.sendDocumentMessage(phoneNumber, mediaId, "Here is your Exam Fee Receipt!", fileName);
|
||||
sendResponseWithMenu(phoneNumber, "Select an option from the menu below.");
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "Failed to upload the receipt document to WhatsApp.");
|
||||
}
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "Failed to download your receipt. It may not be available.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendResponseWithMenu(phoneNumber, "Invalid receipt selected.");
|
||||
}
|
||||
|
||||
sessionService.resetState(phoneNumber); // Reset state after fetching receipt
|
||||
}
|
||||
|
||||
private void sendResponseWithMenu(String phoneNumber, String bodyText) {
|
||||
// Meta API interactive list body limit is 1024 characters
|
||||
if (bodyText.length() > 1024) {
|
||||
bodyText = bodyText.substring(0, 1021) + "...";
|
||||
}
|
||||
|
||||
Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
null,
|
||||
bodyText,
|
||||
"Student Menu",
|
||||
getStudentMenuRows()
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
}
|
||||
|
||||
public void sendHelpMenu(String phoneNumber) {
|
||||
List<Map<String, String>> rows = new ArrayList<>();
|
||||
rows.add(createRow("student_contact_department", "Contact department", "Faculty mentor, HOD, etc."));
|
||||
rows.add(createRow("student_logout", "Logout", "Sign out of your account"));
|
||||
|
||||
Map<String, Object> interactiveList = WhatsAppMessageBuilder.buildListMessage(
|
||||
"Help & Settings",
|
||||
"Please select an option below:",
|
||||
"Help Menu",
|
||||
rows
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, interactiveList);
|
||||
}
|
||||
|
||||
private List<Map<String, String>> getStudentMenuRows() {
|
||||
List<Map<String, String>> rows = new ArrayList<>();
|
||||
rows.add(createRow("student_attendance", "Attendance", "Overall % and Subject-wise"));
|
||||
rows.add(createRow("student_timetable", "Timetable", "Today's classes and Weekly"));
|
||||
rows.add(createRow("student_internal_marks", "Internal marks", "CAT 1, CAT 2, Overall"));
|
||||
rows.add(createRow("student_semester_results", "Semester results", "Latest result and CGPA"));
|
||||
rows.add(createRow("student_fee_details", "Fee details & receipt", "Fee paid, Pending, Due date"));
|
||||
rows.add(createRow("student_academic_calendar", "Academic calendar", "Holidays and Important dates"));
|
||||
rows.add(createRow("student_exam_schedule", "Exam schedule", "Upcoming exams and Hall details"));
|
||||
rows.add(createRow("student_circulars", "Circulars & Notices", "Department circulars"));
|
||||
rows.add(createRow("student_raise_grievance", "Raise a grievance \u2B50", "Submit an issue ticket"));
|
||||
rows.add(createRow("student_help_menu", "Help & Settings", "Logout, Contact dept, etc."));
|
||||
return rows;
|
||||
}
|
||||
|
||||
private Map<String, String> createRow(String id, String title, String description) {
|
||||
Map<String, String> row = new HashMap<>();
|
||||
row.put("id", id);
|
||||
|
||||
@@ -4,6 +4,8 @@ public enum UserState {
|
||||
MAIN_MENU,
|
||||
AWAITING_LOGIN_ID,
|
||||
AWAITING_LOGIN_PASSCODE,
|
||||
AWAITING_SEMESTER_SELECTION,
|
||||
AWAITING_RECEIPT_SELECTION,
|
||||
AWAITING_GRIEVANCE_CATEGORY,
|
||||
AWAITING_GRIEVANCE_DESC
|
||||
}
|
||||
|
||||
@@ -69,7 +69,9 @@ public class ImsApiService {
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, request, String.class);
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
return response.getBody();
|
||||
String rawJson = response.getBody();
|
||||
System.out.println("API Response for " + endpoint + ": " + rawJson);
|
||||
return rawJson;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to fetch from IMS API (" + endpoint + "): " + e.getMessage());
|
||||
@@ -89,6 +91,66 @@ public class ImsApiService {
|
||||
return fetchFromIms("/api/student/results?semester=" + semester, token);
|
||||
}
|
||||
|
||||
public byte[] getResultsPdf(String token, int semester) {
|
||||
String url = BASE_URL + "/api/student/results/download?semester=" + semester;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, request, byte[].class);
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
return response.getBody();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to download PDF: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDashboard(String token) {
|
||||
return fetchFromIms("/api/student/dashboard", token);
|
||||
}
|
||||
|
||||
public String getCatMarks(String token) {
|
||||
return fetchFromIms("/api/student/cat-marks", token);
|
||||
}
|
||||
|
||||
public String getAssignmentMarks(String token) {
|
||||
return fetchFromIms("/api/student/assignment-marks", token);
|
||||
}
|
||||
|
||||
public String getLeaves(String token) {
|
||||
return fetchFromIms("/api/student/leaves", token);
|
||||
}
|
||||
|
||||
public String getFees(String token) {
|
||||
return fetchFromIms("/api/student/fees", token);
|
||||
}
|
||||
|
||||
public String getExamFees(String token) {
|
||||
return fetchFromIms("/api/student/exam-fee", token);
|
||||
}
|
||||
|
||||
public byte[] getExamFeeReceiptPdf(String token, String historyId) {
|
||||
String url = BASE_URL + "/api/student/exam-fee/receipt?history_id=" + historyId;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, request, byte[].class);
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
return response.getBody();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to download Exam Fee receipt PDF: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void logout(String token) {
|
||||
String url = BASE_URL + "/api/auth/logout";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -102,4 +164,26 @@ public class ImsApiService {
|
||||
System.err.println("Failed to logout from IMS API: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getStudentName(String token) {
|
||||
String rawJson = fetchFromIms("/api/student/profile", token);
|
||||
if (rawJson == null) return "Student";
|
||||
|
||||
System.out.println("Profile JSON: " + rawJson);
|
||||
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(rawJson);
|
||||
// Search common fields for the name
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.has("name")) return data.get("name").asText();
|
||||
if (data.has("student_name")) return data.get("student_name").asText();
|
||||
}
|
||||
if (root.has("name")) return root.get("name").asText();
|
||||
if (root.has("student_name")) return root.get("student_name").asText();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to parse profile JSON: " + e.getMessage());
|
||||
}
|
||||
return "Student";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class SessionService {
|
||||
// In-memory cache for API session tokens
|
||||
private final Map<String, String> sessionTokens = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> temporaryLoginIds = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> studentNames = new ConcurrentHashMap<>();
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final EncryptionUtils encryptionUtils;
|
||||
@@ -90,9 +91,19 @@ public class SessionService {
|
||||
imsApiService.logout(token);
|
||||
}
|
||||
userRepository.deleteById(phoneNumber);
|
||||
studentNames.remove(phoneNumber);
|
||||
resetState(phoneNumber);
|
||||
}
|
||||
|
||||
public String getStudentName(String phoneNumber, String token) {
|
||||
if (studentNames.containsKey(phoneNumber)) {
|
||||
return studentNames.get(phoneNumber);
|
||||
}
|
||||
String name = imsApiService.getStudentName(token);
|
||||
studentNames.put(phoneNumber, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setTempLoginId(String phoneNumber, String tempId) {
|
||||
temporaryLoginIds.put(phoneNumber, tempId);
|
||||
}
|
||||
|
||||
@@ -50,19 +50,69 @@ public class WhatsAppMessageService {
|
||||
}
|
||||
|
||||
private void executePost(Map<String, Object> payload) {
|
||||
String url = "https://graph.facebook.com/v19.0/" + phoneNumberId + "/messages";
|
||||
String cleanPhoneId = phoneNumberId != null ? phoneNumberId.trim() : "";
|
||||
String cleanToken = apiToken != null ? apiToken.trim() : "";
|
||||
|
||||
String url = "https://graph.facebook.com/v19.0/" + cleanPhoneId + "/messages";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(apiToken);
|
||||
headers.setBearerAuth(cleanToken);
|
||||
|
||||
HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
|
||||
System.out.println("Message sent successfully. Response: " + response.getBody());
|
||||
System.out.println("Message successfully delivered to WhatsApp.");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to send message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String uploadMedia(byte[] fileData, String fileName, String mimeType) {
|
||||
String cleanPhoneId = phoneNumberId != null ? phoneNumberId.trim() : "";
|
||||
String cleanToken = apiToken != null ? apiToken.trim() : "";
|
||||
String url = "https://graph.facebook.com/v19.0/" + cleanPhoneId + "/media";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
headers.setBearerAuth(cleanToken);
|
||||
|
||||
org.springframework.util.MultiValueMap<String, Object> body = new org.springframework.util.LinkedMultiValueMap<>();
|
||||
body.add("messaging_product", "whatsapp");
|
||||
body.add("file", new org.springframework.core.io.ByteArrayResource(fileData) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return fileName;
|
||||
}
|
||||
});
|
||||
body.add("type", mimeType);
|
||||
|
||||
HttpEntity<org.springframework.util.MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
|
||||
com.fasterxml.jackson.databind.JsonNode root = new com.fasterxml.jackson.databind.ObjectMapper().readTree(response.getBody());
|
||||
return root.get("id").asText();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Media upload failed: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDocumentMessage(String toPhoneNumber, String mediaId, String caption, String filename) {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("messaging_product", "whatsapp");
|
||||
payload.put("to", toPhoneNumber);
|
||||
payload.put("type", "document");
|
||||
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("id", mediaId);
|
||||
if (caption != null && !caption.isEmpty()) doc.put("caption", caption);
|
||||
if (filename != null && !filename.isEmpty()) doc.put("filename", filename);
|
||||
|
||||
payload.put("document", doc);
|
||||
|
||||
executePost(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,39 +7,92 @@ public class MessageFormatter {
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public static String formatAttendance(String rawJson) {
|
||||
if (rawJson == null) return "❌ Failed to retrieve attendance data.";
|
||||
public static String formatAttendanceAndLeaves(String rawAttendance, String rawLeaves) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawJson);
|
||||
// Assuming the JSON has an array of subjects or an overall percentage
|
||||
// Since we don't have the exact schema, this is a best-effort template.
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\ud83d\udcca *Your Attendance Report*\n\n");
|
||||
|
||||
if (root.has("overall_percentage")) {
|
||||
sb.append("*Overall Attendance:* ").append(root.get("overall_percentage").asText()).append("% \ud83d\udfe2\n\n");
|
||||
} else {
|
||||
sb.append("*Overall Attendance:* Data unavailable\n\n");
|
||||
}
|
||||
|
||||
sb.append("*Subject-wise Breakdown:*\n");
|
||||
if (root.has("data") && root.get("data").isArray()) {
|
||||
for (JsonNode subject : root.get("data")) {
|
||||
String name = subject.has("course_name") ? subject.get("course_name").asText() : "Unknown";
|
||||
String percentage = subject.has("percentage") ? subject.get("percentage").asText() : "0";
|
||||
sb.append("- ").append(name).append(": ").append(percentage).append("%\n");
|
||||
// --- Parse Attendance ---
|
||||
sb.append("\ud83d\udcca *Your Attendance Report*\n\n");
|
||||
if (rawAttendance == null) {
|
||||
sb.append("❌ Failed to retrieve attendance data.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawAttendance);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data") && root.get("data").isObject() && root.get("data").has("subjects") && root.get("data").get("subjects").isArray()) {
|
||||
JsonNode subjects = root.get("data").get("subjects");
|
||||
int totalConducted = 0;
|
||||
int totalPresent = 0;
|
||||
|
||||
StringBuilder subjectsBuilder = new StringBuilder();
|
||||
for (JsonNode subject : subjects) {
|
||||
String name = subject.has("name") ? subject.get("name").asText() : "Unknown";
|
||||
int percentage = subject.has("percentage") ? subject.get("percentage").asInt() : 0;
|
||||
|
||||
if (subject.has("conducted") && subject.has("present")) {
|
||||
totalConducted += subject.get("conducted").asInt();
|
||||
totalPresent += subject.get("present").asInt();
|
||||
}
|
||||
|
||||
String emoji = percentage >= 75 ? "\ud83d\udfe2" : (percentage >= 60 ? "\ud83d\udfe1" : "\ud83d\udd34");
|
||||
subjectsBuilder.append("- *").append(name).append("*: ").append(percentage).append("% ").append(emoji).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
|
||||
if (totalConducted > 0) {
|
||||
int overallPercentage = (totalPresent * 100) / totalConducted;
|
||||
String overallEmoji = overallPercentage >= 75 ? "\ud83d\udfe2" : (overallPercentage >= 60 ? "\ud83d\udfe1" : "\ud83d\udd34");
|
||||
sb.append("*Overall Attendance:* ").append(overallPercentage).append("% ").append(overallEmoji).append("\n\n");
|
||||
}
|
||||
|
||||
sb.append("*Subject-wise Breakdown:*\n");
|
||||
sb.append(subjectsBuilder);
|
||||
}
|
||||
} else {
|
||||
sb.append("- Details not available in this format.\n");
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW ATTENDANCE JSON:*\n").append(rawAttendance).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting attendance data.\nRAW JSON: ").append(rawAttendance).append("\n");
|
||||
}
|
||||
|
||||
sb.append("\n_Keep up the good work!_");
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
return "⚠️ Error formatting attendance data.";
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
|
||||
// --- Parse Leaves/OD ---
|
||||
sb.append("🏖️ *Leave / OD History*\n");
|
||||
if (rawLeaves == null) {
|
||||
sb.append("❌ Failed to retrieve leaves data.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawLeaves);
|
||||
boolean foundData = false;
|
||||
|
||||
// Assuming typical array of objects for leaves
|
||||
if (root.has("data") && root.get("data").isArray() && root.get("data").size() > 0) {
|
||||
for (JsonNode leave : root.get("data")) {
|
||||
String date = leave.has("date") ? leave.get("date").asText() :
|
||||
(leave.has("from_date") ? leave.get("from_date").asText() : "Unknown");
|
||||
String type = leave.has("type") ? leave.get("type").asText() :
|
||||
(leave.has("leave_type") ? leave.get("leave_type").asText() : "Leave");
|
||||
String status = leave.has("status") ? leave.get("status").asText() : "Pending";
|
||||
|
||||
sb.append("- *").append(date).append("*: ").append(type).append(" (").append(status).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW LEAVES JSON:*\n").append(rawLeaves).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting leaves data.\nRAW JSON: ").append(rawLeaves).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatTimetable(String rawJson) {
|
||||
@@ -50,21 +103,63 @@ public class MessageFormatter {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\ud83d\uddd3\ufe0f *Today's Classes*\n\n");
|
||||
|
||||
if (root.has("data") && root.get("data").isArray()) {
|
||||
for (JsonNode classNode : root.get("data")) {
|
||||
String time = classNode.has("time") ? classNode.get("time").asText() : "TBA";
|
||||
String course = classNode.has("course_name") ? classNode.get("course_name").asText() : "Unknown";
|
||||
String room = classNode.has("room") ? classNode.get("room").asText() : "Unknown";
|
||||
boolean validFormat = false;
|
||||
boolean hasClassesToday = false;
|
||||
|
||||
if (root.has("data") && root.get("data").isObject() && root.get("data").has("schedule")) {
|
||||
validFormat = true;
|
||||
JsonNode schedule = root.get("data").get("schedule");
|
||||
|
||||
String today = java.time.LocalDate.now().getDayOfWeek().toString().toLowerCase();
|
||||
|
||||
if (schedule.has(today)) {
|
||||
JsonNode todaySchedule = schedule.get(today);
|
||||
|
||||
sb.append("*").append(time).append(":* ").append(course).append(" (Room ").append(room).append(")\n");
|
||||
if (todaySchedule.isObject() && todaySchedule.size() > 0) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = todaySchedule.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
String timeOrPeriod = field.getKey();
|
||||
JsonNode details = field.getValue();
|
||||
|
||||
if (details.isObject()) {
|
||||
String course = details.has("course_name") ? details.get("course_name").asText() :
|
||||
(details.has("name") ? details.get("name").asText() : "Unknown");
|
||||
String room = details.has("room") ? details.get("room").asText() : "";
|
||||
|
||||
sb.append("*").append(timeOrPeriod).append(":* ").append(course);
|
||||
if (!room.isEmpty()) sb.append(" (Room ").append(room).append(")");
|
||||
sb.append("\n");
|
||||
} else {
|
||||
sb.append("*").append(timeOrPeriod).append(":* ").append(details.asText()).append("\n");
|
||||
}
|
||||
hasClassesToday = true;
|
||||
}
|
||||
} else if (todaySchedule.isArray() && todaySchedule.size() > 0) {
|
||||
for (JsonNode details : todaySchedule) {
|
||||
String time = details.has("time") ? details.get("time").asText() : "TBA";
|
||||
String course = details.has("course_name") ? details.get("course_name").asText() : "Unknown";
|
||||
String room = details.has("room") ? details.get("room").asText() : "";
|
||||
|
||||
sb.append("*").append(time).append(":* ").append(course);
|
||||
if (!room.isEmpty()) sb.append(" (Room ").append(room).append(")");
|
||||
sb.append("\n");
|
||||
hasClassesToday = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append("No classes scheduled for today.\n");
|
||||
}
|
||||
|
||||
if (validFormat && !hasClassesToday) {
|
||||
sb.append("No classes scheduled for today.\n");
|
||||
} else if (!validFormat) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n\n*DEBUG RAW JSON:*\n").append(rawJson);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
return "⚠️ Error formatting timetable data.";
|
||||
return "⚠️ Error formatting timetable data.\nRAW JSON: " + rawJson;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,11 +172,12 @@ public class MessageFormatter {
|
||||
sb.append("\ud83c\udf93 *Semester ").append(semester).append(" Results*\n\n");
|
||||
|
||||
boolean passedAll = true;
|
||||
boolean hasData = false;
|
||||
|
||||
if (root.has("data") && root.get("data").isArray()) {
|
||||
if (root.has("data") && root.get("data").isArray() && root.get("data").size() > 0) {
|
||||
hasData = true;
|
||||
for (JsonNode courseNode : root.get("data")) {
|
||||
String name = courseNode.has("course_name") ? courseNode.get("course_name").asText() : "Unknown";
|
||||
String total = courseNode.has("total_mark") ? courseNode.get("total_mark").asText() : "0";
|
||||
String grade = courseNode.has("grade") ? courseNode.get("grade").asText() : "N/A";
|
||||
String result = courseNode.has("result") ? courseNode.get("result").asText() : "N/A";
|
||||
|
||||
@@ -89,10 +185,12 @@ public class MessageFormatter {
|
||||
passedAll = false;
|
||||
}
|
||||
|
||||
sb.append("*").append(name).append(":* ").append(total).append("/100 (Grade: ").append(grade).append(")\n");
|
||||
sb.append("*").append(name).append(":* Grade ").append(grade).append("\n");
|
||||
}
|
||||
} else {
|
||||
sb.append("No results found for this semester.\n");
|
||||
}
|
||||
|
||||
if (!hasData) {
|
||||
return "\ud83c\udf93 *Semester " + semester + " Results*\n\nNil or not yet updated.";
|
||||
}
|
||||
|
||||
sb.append("\n*Result:* ").append(passedAll ? "PASS \ud83c\udf89" : "Arrears \u26a0\ufe0f");
|
||||
@@ -101,4 +199,248 @@ public class MessageFormatter {
|
||||
return "⚠️ Error formatting results data.";
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatDashboardCgpa(String rawJson) {
|
||||
String defaultMsg = "Please select the semester to download your detailed PDF results:";
|
||||
if (rawJson == null) return defaultMsg;
|
||||
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawJson);
|
||||
if (root.has("data") && root.get("data").isObject()) {
|
||||
JsonNode data = root.get("data");
|
||||
String cgpa = data.has("cgpa") ? data.get("cgpa").asText() : "N/A";
|
||||
String arrears = data.has("arrears") ? data.get("arrears").asText() : "0";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\ud83c\udf93 *Your Current CGPA:* ").append(cgpa).append("\n");
|
||||
if (!"0".equals(arrears) && !"N/A".equals(arrears)) {
|
||||
sb.append("\ud83d\udd34 *Active Arrears:* ").append(arrears).append("\n");
|
||||
}
|
||||
sb.append("\n").append(defaultMsg);
|
||||
return sb.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore parse errors and return default message
|
||||
}
|
||||
return defaultMsg;
|
||||
}
|
||||
|
||||
public static String formatInternalMarks(String rawCatJson, String rawAssignmentJson) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\ud83d\udcc8 *Internal Marks*\n\n");
|
||||
|
||||
// --- Parse CAT Marks ---
|
||||
sb.append("*CAT MARKS:*\n");
|
||||
if (rawCatJson == null) {
|
||||
sb.append("❌ Failed to retrieve CAT marks.\n\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawCatJson);
|
||||
boolean foundData = false;
|
||||
|
||||
JsonNode targetArray = null;
|
||||
if (root.has("data")) {
|
||||
if (root.get("data").isArray()) {
|
||||
targetArray = root.get("data");
|
||||
} else if (root.get("data").isObject() && root.get("data").has("subjects") && root.get("data").get("subjects").isArray()) {
|
||||
targetArray = root.get("data").get("subjects");
|
||||
}
|
||||
}
|
||||
|
||||
if (targetArray != null && targetArray.size() > 0) {
|
||||
for (JsonNode subject : targetArray) {
|
||||
String name = subject.has("name") ? subject.get("name").asText() :
|
||||
(subject.has("course_name") ? subject.get("course_name").asText() : "Unknown");
|
||||
|
||||
String marks = "";
|
||||
if (subject.has("total") && !subject.get("total").asText().isEmpty()) {
|
||||
marks = subject.get("total").asText();
|
||||
if (subject.has("weightage") && !subject.get("weightage").asText().isEmpty()) {
|
||||
marks += " (" + subject.get("weightage").asText() + " wt)";
|
||||
}
|
||||
} else if (subject.has("marks")) {
|
||||
marks = subject.get("marks").asText();
|
||||
} else {
|
||||
marks = "N/A";
|
||||
}
|
||||
|
||||
sb.append("- *").append(name).append("*: ").append(marks).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW CAT JSON:*\n").append(rawCatJson).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting CAT marks.\nRAW JSON: ").append(rawCatJson).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
|
||||
// --- Parse Assignment Marks ---
|
||||
sb.append("*ASSIGNMENT MARKS:*\n");
|
||||
if (rawAssignmentJson == null) {
|
||||
sb.append("❌ Failed to retrieve Assignment marks.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawAssignmentJson);
|
||||
boolean foundData = false;
|
||||
|
||||
JsonNode targetArray = null;
|
||||
if (root.has("data")) {
|
||||
if (root.get("data").isArray()) {
|
||||
targetArray = root.get("data");
|
||||
} else if (root.get("data").isObject() && root.get("data").has("subjects") && root.get("data").get("subjects").isArray()) {
|
||||
targetArray = root.get("data").get("subjects");
|
||||
}
|
||||
}
|
||||
|
||||
if (targetArray != null && targetArray.size() > 0) {
|
||||
for (JsonNode subject : targetArray) {
|
||||
String name = subject.has("name") ? subject.get("name").asText() :
|
||||
(subject.has("course_name") ? subject.get("course_name").asText() : "Unknown");
|
||||
|
||||
String marks = "";
|
||||
if (subject.has("total") && !subject.get("total").asText().isEmpty()) {
|
||||
marks = subject.get("total").asText();
|
||||
if (subject.has("weightage") && !subject.get("weightage").asText().isEmpty()) {
|
||||
marks += " (" + subject.get("weightage").asText() + " wt)";
|
||||
}
|
||||
} else if (subject.has("marks")) {
|
||||
marks = subject.get("marks").asText();
|
||||
} else {
|
||||
marks = "N/A";
|
||||
}
|
||||
|
||||
sb.append("- *").append(name).append("*: ").append(marks).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW ASSIGNMENT JSON:*\n").append(rawAssignmentJson).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting Assignment marks.\nRAW JSON: ").append(rawAssignmentJson).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatFeesAndExamFees(String rawFees, String rawExamFees) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// --- Parse Academic Fees ---
|
||||
sb.append("\ud83d\udcb0 *Academic Dues / Fees*\n\n");
|
||||
if (rawFees == null) {
|
||||
sb.append("❌ Failed to retrieve academic fee details.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawFees);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.isArray() && data.size() > 0) {
|
||||
for (JsonNode fee : data) {
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : "Fee");
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "0";
|
||||
String status = fee.has("status") ? fee.get("status").asText() : "N/A";
|
||||
|
||||
sb.append("- *").append(type).append("*: ₹").append(amount).append(" (").append(status).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
} else if (data.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = data.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
sb.append("- *").append(field.getKey().replace("_", " ")).append("*: ").append(field.getValue().asText()).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW FEES JSON:*\n").append(rawFees).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting academic fee data.\nRAW JSON: ").append(rawFees).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
|
||||
// --- Parse Exam Fees ---
|
||||
sb.append("📝 *Exam Fees & Payment History*\n");
|
||||
if (rawExamFees == null) {
|
||||
sb.append("❌ Failed to retrieve exam fee details.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.isArray() && data.size() > 0) {
|
||||
for (JsonNode fee : data) {
|
||||
String semester = fee.has("semester") ? fee.get("semester").asText() : "Unknown Sem";
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "0";
|
||||
String status = fee.has("status") ? fee.get("status").asText() : "N/A";
|
||||
String date = fee.has("date") ? " on " + fee.get("date").asText() : "";
|
||||
|
||||
sb.append("- *Sem ").append(semester).append("*: ₹").append(amount).append(" (").append(status).append(date).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
} else if (data.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = data.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
sb.append("- *").append(field.getKey().replace("_", " ")).append("*: ").append(field.getValue().asText()).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW EXAM FEES JSON:*\n").append(rawExamFees).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting exam fee data.\nRAW JSON: ").append(rawExamFees).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static java.util.List<java.util.Map<String, String>> extractReceiptRows(String rawExamFees) {
|
||||
java.util.List<java.util.Map<String, String>> rows = new java.util.ArrayList<>();
|
||||
if (rawExamFees == null) return rows;
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
if (root.has("data") && root.get("data").isArray()) {
|
||||
for (JsonNode fee : root.get("data")) {
|
||||
if (fee.has("history_id") || fee.has("id")) {
|
||||
String id = fee.has("history_id") ? fee.get("history_id").asText() : fee.get("id").asText();
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "";
|
||||
String date = fee.has("date") ? fee.get("date").asText() : "";
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
row.put("id", "receipt_" + id);
|
||||
row.put("title", "Receipt " + id);
|
||||
row.put("description", "₹" + amount + (date.isEmpty() ? "" : " on " + date));
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user