diff --git a/src/main/java/com/example/whatsappautomation/WhatsAppAutomationApplication.java b/src/main/java/com/example/whatsappautomation/WhatsAppAutomationApplication.java index 3f9a9d5..6546218 100644 --- a/src/main/java/com/example/whatsappautomation/WhatsAppAutomationApplication.java +++ b/src/main/java/com/example/whatsappautomation/WhatsAppAutomationApplication.java @@ -2,8 +2,10 @@ package com.example.whatsappautomation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class WhatsAppAutomationApplication { public static void main(String[] args) { diff --git a/src/main/java/com/example/whatsappautomation/entity/UserState.java b/src/main/java/com/example/whatsappautomation/entity/UserState.java index 8532990..2ae4bb4 100644 --- a/src/main/java/com/example/whatsappautomation/entity/UserState.java +++ b/src/main/java/com/example/whatsappautomation/entity/UserState.java @@ -11,6 +11,7 @@ public class UserState { @Id private String phoneNumber; + private String phoneNumberId; private String patientId; private String state; private String category; @@ -22,6 +23,7 @@ public class UserState { private String bookingTime; private String languagePreference = "en"; private int menuPage = 0; + private Long lastActiveTime = System.currentTimeMillis(); public UserState() {} @@ -41,6 +43,19 @@ public class UserState { this.bookingDate = null; this.bookingTime = null; this.menuPage = 0; + this.lastActiveTime = System.currentTimeMillis(); + } + + public void expire() { + this.state = "EXPIRED"; + this.category = null; + this.subCategory = null; + this.selectedItem = null; + this.price = null; + this.address = null; + this.bookingDate = null; + this.bookingTime = null; + this.menuPage = 0; } public String getPhoneNumber() { @@ -141,4 +156,20 @@ public class UserState { public void setMenuPage(int menuPage) { this.menuPage = menuPage; } + + public Long getLastActiveTime() { + return lastActiveTime; + } + + public void setLastActiveTime(Long lastActiveTime) { + this.lastActiveTime = lastActiveTime; + } + + public String getPhoneNumberId() { + return phoneNumberId; + } + + public void setPhoneNumberId(String phoneNumberId) { + this.phoneNumberId = phoneNumberId; + } } diff --git a/src/main/java/com/example/whatsappautomation/repository/UserStateRepository.java b/src/main/java/com/example/whatsappautomation/repository/UserStateRepository.java index 77ce3d4..4cce3bb 100644 --- a/src/main/java/com/example/whatsappautomation/repository/UserStateRepository.java +++ b/src/main/java/com/example/whatsappautomation/repository/UserStateRepository.java @@ -4,6 +4,9 @@ import com.example.whatsappautomation.entity.UserState; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface UserStateRepository extends JpaRepository { + List findByStateNotAndLastActiveTimeBefore(String state, Long cutoffTime); } diff --git a/src/main/java/com/example/whatsappautomation/service/ChatbotService.java b/src/main/java/com/example/whatsappautomation/service/ChatbotService.java index 6fc4cb6..c7ef598 100644 --- a/src/main/java/com/example/whatsappautomation/service/ChatbotService.java +++ b/src/main/java/com/example/whatsappautomation/service/ChatbotService.java @@ -35,6 +35,9 @@ public class ChatbotService { @Value("${whatsapp.access-token}") private String accessToken; + @Value("${conversation.inactivity.timeout:120}") + private long inactivityTimeoutSeconds; + private final RestTemplate restTemplate = new RestTemplate(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -55,13 +58,21 @@ public class ChatbotService { currentFrom.set(from); try { UserState userState = userStateRepository.findById(from).orElse(new UserState(from)); + userState.setPhoneNumberId(phoneNumberId); + + String lowerInput = messageText.trim().toLowerCase(); // Global Reset: Handling basic greetings - String lowerInput = messageText.trim().toLowerCase(); if (lowerInput.equals("hi") || lowerInput.equals("hello") || lowerInput.equals("hey") || lowerInput.equals("0")) { userState.reset(); + } else if ("EXPIRED".equals(userState.getState())) { + // If expired and not a greeting, ignore or return without processing + return; } + // Refresh activity timer + userState.setLastActiveTime(System.currentTimeMillis()); + String response = handleState(userState, messageText.trim()); // If not in MAIN_MENU or LANGUAGE_SELECTION, append restart hint @@ -1178,4 +1189,24 @@ public class ChatbotService { sendInteractiveList(bodyText, buttonText, sectionTitle, ids, titles); } } + + @org.springframework.scheduling.annotation.Scheduled(fixedDelay = 30000) // Runs every 30 seconds + public void pollInactiveSessions() { + long cutoffTime = System.currentTimeMillis() - (inactivityTimeoutSeconds * 1000); + List inactiveUsers = userStateRepository.findByStateNotAndLastActiveTimeBefore("EXPIRED", cutoffTime); + + for (UserState user : inactiveUsers) { + user.expire(); + userStateRepository.save(user); + + // Send fallback message + String fallbackText = "en".equals(user.getLanguagePreference()) + ? "It looks like your previous conversation has ended.\n👋 To start a new conversation, please type Hi or Hello." + : "உங்கள் முந்தைய உரையாடல் முடிவடைந்துவிட்டது.\n👋 புதிய உரையாடலைத் தொடங்க Hi அல்லது Hello என டைப் செய்யவும்."; + + if (user.getPhoneNumberId() != null) { + sendMessage(user.getPhoneNumberId(), user.getPhoneNumber(), fallbackText); + } + } + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b37975a..db84a95 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,7 +1,7 @@ spring.application.name=whatsapp-automation server.port=8085 -whatsapp.access-token=EAAOZCONMSXCkBSHaYF1PqnnZAVjcmLqoOOJZCcbQGVWfWxILu9n0hQZBnUVGJG8WZBnLiZASM7etogv1xGj2PZCCmQPhFYyAKrhagFeYoi9fUZCfTryKonhStH3OU3RnHuZCxIWhnVIkIKe4byuOwqOv47SmM815ZCHUZA33fEhxPe9Onr1lg4tOnDeJMHoRYyDtcpWPbpc1eDbZBcy3vXwLslaBwx94xovhSKPpkDNgttTWsNBMnhbT7rKmnXEZAqDwWSg5FhITKePQS9HQ5troiyrXb +whatsapp.access-token=EAAOZCONMSXCkBSL3CC4fKfY9X4ZAV8VzgQbiVx3qc05mdTSPgyy32jxSVVRDA5bGN3tcNmBmU5fWrqeRYYZCZApsobi4lZCWBrWBXJjEDQrxdTvZCdDZCjv7GZBgidEqZBboKbi5ZA9Yi864jE8mYmZAtjitliIKJ1nDsHpJpY0tZB2kpaZAKnnfHf9RcZBixr8owGaGHUvok4LO1SVNNZBJ4ZAr7JrZC5DY3vi402sGPOnY9n8QXGzIRKrcmFij74xAWKhybZAgvHIZBAIVsuicybPiDfVy8ZBN whatsapp.verify-token=my_whatsapp_verify_token_123 spring.datasource.url=jdbc:postgresql://localhost:5432/rmc_bot @@ -15,3 +15,6 @@ spring.jpa.show-sql=true spring.messages.encoding=UTF-8 + +# Conversation timeout in seconds +conversation.inactivity.timeout=120