Skip to content
This repository has been archived by the owner on Nov 6, 2024. It is now read-only.

Commit

Permalink
Feat: Added quick reply buttons for attendance
Browse files Browse the repository at this point in the history
Issue: #47
  • Loading branch information
AnshGupta01 committed Oct 1, 2023
1 parent 600ccbc commit fc99b63
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/states/render-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,35 @@ export const renderAmizoneMenu = () => ({
},
});

export const renderReplyMessage = () => ({
type: "button",
header: {
type: "text",
text: "Attendance",
},
body: {
text: "Quick Attendance Checkout",
},
action: {
buttons: [
{
type: "reply",
reply: {
id: "yesterday_attendance",
title: "Yesterday's", // Check schedule -> 28
},
},
{
type: "reply",
reply: {
id: "today_attendance",
title: "Today's",
},
},
],
},
});

export const renderClassScheduleDateList = () => {
const dates = new Array(5);
for (let i = 0; i < 5; i += 1) {
Expand Down
97 changes: 96 additions & 1 deletion src/states/state-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BotHandlerContext, User, states } from "./states.js";
import { BotHandlerContext, states, User } from "./states.js";
import {
renderAmizoneMenu,
renderReplyMessage,
renderAttendance,
renderCourses,
renderSchedule,
Expand All @@ -14,6 +15,15 @@ import {
} from "./render-messages.js";
import { firstNonEmpty, newAmizoneClient } from "../utils.js";

// === Utilities ===
const OFFSET_IST = 330;
const MINUTE_TO_MS = 60_000;
const DAY_TO_MINUTE = 24 * 60;
const currentTzOffset = new Date().getTimezoneOffset();

const dateToIST = (date: Date): Date =>
new Date(date.getTime() + (OFFSET_IST - currentTzOffset) * MINUTE_TO_MS);

const validateAmizoneCredentials = async (
username: string,
password: string
Expand Down Expand Up @@ -79,6 +89,7 @@ export const handleExpectPassword = async (
if (credentialsAreValid) {
updatedUser.amizoneCredentials.password = password;
updatedUser.state = states.LOGGED_IN;
await ctx.bot.sendInteractiveMessage(payload.sender, renderReplyMessage());
await ctx.bot.sendInteractiveMessage(payload.sender, renderAmizoneMenu());
return updatedUser;
}
Expand Down Expand Up @@ -191,6 +202,10 @@ export const handleLoggedIn = async (ctx: BotHandlerContext): Promise<User> => {
updatedUser.state = states.NEW_USER;
await ctx.bot.sendMessage(payload.sender, "Logged Out!");
return updatedUser;
} else if (payload.interactive.title === "Yesterday's" || payload.interactive.title === "Today's") {
// Handle attendance button click
await handleReplyAttendanceButton(ctx);
return updatedUser;
}

const messageHandler = amizoneMenuHandlersMap.get(inputMessage);
Expand All @@ -201,6 +216,7 @@ export const handleLoggedIn = async (ctx: BotHandlerContext): Promise<User> => {
payload.sender,
"Invalid option selected. Try again?"
);
await ctx.bot.sendInteractiveMessage(payload.sender, renderReplyMessage());
await ctx.bot.sendInteractiveMessage(payload.sender, renderAmizoneMenu());
return updatedUser;
}
Expand All @@ -211,12 +227,14 @@ export const handleLoggedIn = async (ctx: BotHandlerContext): Promise<User> => {
payload.sender,
"Unsuccessful. Either Amizone is down or you need to login again (hint: menu has a _logout_ option)"
);
await ctx.bot.sendInteractiveMessage(payload.sender, renderReplyMessage());
await ctx.bot.sendInteractiveMessage(payload.sender, renderAmizoneMenu());
return updatedUser;
}

if (typeof message === "string") {
await ctx.bot.sendMessage(payload.sender, message);
await ctx.bot.sendInteractiveMessage(payload.sender, renderReplyMessage());
await ctx.bot.sendInteractiveMessage(payload.sender, renderAmizoneMenu());
}

Expand All @@ -228,6 +246,67 @@ export const handleLoggedIn = async (ctx: BotHandlerContext): Promise<User> => {
return updatedUser;
};

export const handleReplyAttendanceButton = async (ctx: BotHandlerContext): Promise<User> => {
const { payload } = ctx;
const updatedUser = structuredClone(ctx.user);

// Check if the user clicked either "Today's Attendance" or "Yesterday's Attendance"
if (
payload.interactive.title === "Today's" ||
payload.interactive.title === "Yesterday's"
) {
let selectedDate;

if (payload.interactive.title === "Today's") {
selectedDate = new Date(dateToIST(new Date()).getTime());
} else {
selectedDate = new Date(
dateToIST(new Date()).getTime() - 1 * DAY_TO_MINUTE * MINUTE_TO_MS
);
}

try {
// Fetch attendance data for the selected date using your Amizone API client
const [year, month, day] = [
selectedDate.getFullYear(),
selectedDate.getMonth() + 1,
selectedDate.getDate(),
];
const attendance = await newAmizoneClient(
ctx.user.amizoneCredentials
).amizoneServiceGetClassSchedule(year, month, day);

// Send the attendance data as a message to the user
if (
attendance.data.classes !== undefined &&
attendance.data.classes.length > 0
) {
await ctx.bot.sendMessage(
payload.sender,
renderSchedule(attendance.data)
);
} else {
await ctx.bot.sendMessage(
payload.sender,
"no schedule available."
);
}
await ctx.bot.sendInteractiveMessage(
payload.sender,
renderReplyMessage()
);
await ctx.bot.sendInteractiveMessage(
payload.sender,
renderAmizoneMenu()
);
} catch (error) {
// Handle errors (e.g., API request error)
console.error("Error fetching attendance for the selected date:", error);
}
}
return updatedUser;
};

export const handleScheduleDateInput = async (ctx: BotHandlerContext) => {
const { payload: whatsappPayload } = ctx;
const dateInput = firstNonEmpty(
Expand Down Expand Up @@ -273,6 +352,10 @@ export const handleScheduleDateInput = async (ctx: BotHandlerContext) => {
"no schedule available."
);
}
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderReplyMessage()
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderAmizoneMenu()
Expand All @@ -295,6 +378,10 @@ export const handleFacultyFeedbackRating = async (ctx: BotHandlerContext) => {

if (message.toLowerCase().trim() === "cancel") {
await ctx.bot.sendMessage(whatsappPayload.sender, "Cancelled.");
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderReplyMessage()
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderAmizoneMenu()
Expand Down Expand Up @@ -345,6 +432,10 @@ export const handleFacultyFeedbackRating = async (ctx: BotHandlerContext) => {
whatsappPayload.sender,
"No feedback to fill at the moment"
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderReplyMessage()
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderAmizoneMenu()
Expand All @@ -357,6 +448,10 @@ export const handleFacultyFeedbackRating = async (ctx: BotHandlerContext) => {
// @ts-ignore
renderFacultyFeedbackConfirmation(feedback.data.filledFor)
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderReplyMessage()
);
await ctx.bot.sendInteractiveMessage(
whatsappPayload.sender,
renderAmizoneMenu()
Expand Down

0 comments on commit fc99b63

Please sign in to comment.