diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/TelegramConfig.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/TelegramConfig.java new file mode 100644 index 00000000000..42b5597bffb --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/TelegramConfig.java @@ -0,0 +1,25 @@ +package io.mosip.kernel.emailnotification.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class TelegramConfig { + + private final String botToken = "YOUR_TELEGRAM_BOT_TOKEN"; + private final String chatId = "YOUR_CHAT_ID"; + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + public String getBotToken() { + return botToken; + } + + public String getChatId() { + return chatId; + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WebConfig.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WebConfig.java new file mode 100644 index 00000000000..a4dbe00176b --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WebConfig.java @@ -0,0 +1,23 @@ +package io.mosip.kernel.emailnotification.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class WebConfig { + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedOriginPattern("*"); // Allow all origins temporarily for debugging + config.addAllowedMethod("*"); // Allow all HTTP methods + config.addAllowedHeader("*"); // Allow all headers + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WhatsAppConfig.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WhatsAppConfig.java new file mode 100644 index 00000000000..4b579be8711 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/WhatsAppConfig.java @@ -0,0 +1,14 @@ +package io.mosip.kernel.emailnotification.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class WhatsAppConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MessageController.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MessageController.java new file mode 100644 index 00000000000..19bdadca281 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MessageController.java @@ -0,0 +1,234 @@ +package io.mosip.kernel.emailnotification.controller; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import io.mosip.kernel.emailnotification.model.BulkMessage; +import io.mosip.kernel.emailnotification.model.BulkMessageRequest; +import io.mosip.kernel.emailnotification.model.MessageRequest; +import io.mosip.kernel.emailnotification.model.MessageTemplate; +import io.mosip.kernel.emailnotification.model.TemplateRequest; +import io.mosip.kernel.emailnotification.service.impl.CsvService; +import io.mosip.kernel.emailnotification.service.impl.MessageService; +import io.mosip.kernel.emailnotification.service.impl.TemplateService; + +@RestController +@RequestMapping("/api/messages") +@CrossOrigin(origins = "http://localhost:3000") +public class MessageController { + + private final MessageService messageService; + private final TemplateService templateService; + private final CsvService csvService; + + public MessageController(MessageService messageService, TemplateService templateService, CsvService csvService) { + this.messageService = messageService; + this.templateService = templateService; + this.csvService = csvService; + } + + @PostMapping + public ResponseEntity scheduleMessage(@RequestBody Map request) { + try { + // System.out.println("🔍 Received message request:"); + // System.out.println("📱 Phone: " + request.get("phoneNumber")); + // System.out.println("📱 Telegram: " + request.get("telegramUserId")); + // System.out.println("💬 Text: " + request.get("text")); + // System.out.println("⏰ Delivery Time (raw): " + request.get("deliveryTime")); + // System.out.println("🌍 Current Time: " + java.time.LocalDateTime.now()); + + // Parse delivery time (supports ISO with 'Z'/offset or local pattern) + String deliveryTimeStr = (String) request.get("deliveryTime"); + java.time.LocalDateTime deliveryTime = parseToLocalDateTime(deliveryTimeStr); + + // System.out.println("⏰ Parsed Delivery Time: " + deliveryTime); + + // Create MessageRequest object + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setPhoneNumber((String) request.get("phoneNumber")); + messageRequest.setTelegramUserId((String) request.get("telegramUserId")); + messageRequest.setText((String) request.get("text")); + messageRequest.setDeliveryTime(deliveryTime); + + messageService.scheduleMessage(messageRequest); + return ResponseEntity.ok("Message scheduled successfully"); + } catch (Exception e) { + System.err.println("❌ Error parsing message request: " + e.getMessage()); + return ResponseEntity.badRequest().body("Failed to parse message request: " + e.getMessage()); + } + } + + @PostMapping("/send") + public ResponseEntity sendMessage(@RequestBody MessageRequest messageRequest) { + try { + messageService.sendMessage(messageRequest); + return ResponseEntity.ok("Message sent successfully"); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to send message: " + e.getMessage()); + } + } + + @PostMapping("/sendToAll") + public void sendMessageToAllPlatforms(@RequestParam String phoneNumber, @RequestParam String telegramUserId, @RequestBody String message) { + messageService.sendMessageToAllPlatforms(phoneNumber, telegramUserId, message); + } + + // Template endpoints + @GetMapping("/templates") + public ResponseEntity> getAllTemplates() { + return ResponseEntity.ok(templateService.getAllTemplates()); + } + + @GetMapping("/templates/category/{category}") + public ResponseEntity> getTemplatesByCategory(@PathVariable String category) { + return ResponseEntity.ok(templateService.getTemplatesByCategory(category)); + } + + @GetMapping("/templates/{id}") + public ResponseEntity getTemplateById(@PathVariable String id) { + MessageTemplate template = templateService.getTemplateById(id); + if (template == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(template); + } + + @GetMapping("/templates/categories") + public ResponseEntity> getCategories() { + return ResponseEntity.ok(templateService.getCategories()); + } + + @PostMapping("/templates/process") + public ResponseEntity processTemplate(@RequestBody TemplateRequest templateRequest) { + try { + String processedMessage = templateService.processTemplate( + templateRequest.getTemplateId(), + templateRequest.getVariables() + ); + return ResponseEntity.ok(processedMessage); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + @PostMapping("/templates/schedule") + public ResponseEntity scheduleTemplateMessage(@RequestBody Map request) { + try { + String templateId = (String) request.get("templateId"); + Map variables = (Map) request.get("variables"); + String phoneNumber = (String) request.get("phoneNumber"); + String telegramUserId = (String) request.get("telegramUserId"); + String scheduledTime = (String) request.get("scheduledTime"); + + String processedMessage = templateService.processTemplate(templateId, variables); + + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setPhoneNumber(phoneNumber); + messageRequest.setTelegramUserId(telegramUserId); + messageRequest.setText(processedMessage); + messageRequest.setDeliveryTime(parseToLocalDateTime(scheduledTime)); + + messageService.scheduleMessage(messageRequest); + return ResponseEntity.ok("Template message scheduled successfully"); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Failed to schedule template message: " + e.getMessage()); + } + } + + private java.time.LocalDateTime parseToLocalDateTime(String input) { + if (input == null || input.trim().isEmpty()) { + throw new IllegalArgumentException("deliveryTime is required"); + } + + String trimmed = input.trim(); + + // Try ISO instant/offset first (e.g., 2025-09-29T06:08:00Z or with +05:30) + try { + java.time.OffsetDateTime odt = java.time.OffsetDateTime.parse(trimmed); + return odt.atZoneSameInstant(java.time.ZoneId.systemDefault()).toLocalDateTime(); + } catch (Exception ignore) { + // fallthrough + } + + // Try Instant.parse for strict Z timestamps + try { + java.time.Instant instant = java.time.Instant.parse(trimmed); + return java.time.LocalDateTime.ofInstant(instant, java.time.ZoneId.systemDefault()); + } catch (Exception ignore) { + // fallthrough + } + + // Try LocalDateTime ISO without zone + try { + return java.time.LocalDateTime.parse(trimmed); + } catch (Exception ignore) { + // fallthrough + } + + // Try CSV-style formatter to align with bulk flow + try { + java.time.format.DateTimeFormatter fmt = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return java.time.LocalDateTime.parse(trimmed, fmt); + } catch (Exception e) { + throw new IllegalArgumentException("Unsupported date-time format: " + input); + } + } + + // CSV Upload endpoints + @PostMapping("/csv/upload") + public ResponseEntity uploadCsv(@RequestParam("file") MultipartFile file) { + try { + BulkMessageRequest bulkRequest = csvService.parseCsv(file); + int scheduledCount = 0; + + for (BulkMessage bulkMessage : bulkRequest.getMessages()) { + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setPhoneNumber(bulkMessage.getPhoneNumber()); + messageRequest.setTelegramUserId(bulkMessage.getTelegramUserId()); + messageRequest.setText(bulkMessage.getMessage()); + messageRequest.setDeliveryTime(bulkMessage.getScheduledTime()); + + messageService.scheduleMessage(messageRequest); + scheduledCount++; + } + + return ResponseEntity.ok("CSV uploaded successfully. " + scheduledCount + " messages scheduled."); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Failed to process CSV: " + e.getMessage()); + } + } + + @PostMapping("/csv/schedule") + public ResponseEntity scheduleBulkMessages(@RequestBody BulkMessageRequest bulkRequest) { + try { + int scheduledCount = 0; + + for (BulkMessage bulkMessage : bulkRequest.getMessages()) { + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setPhoneNumber(bulkMessage.getPhoneNumber()); + messageRequest.setTelegramUserId(bulkMessage.getTelegramUserId()); + messageRequest.setText(bulkMessage.getMessage()); + messageRequest.setDeliveryTime(bulkMessage.getScheduledTime()); + + messageService.scheduleMessage(messageRequest); + scheduledCount++; + } + + return ResponseEntity.ok("Bulk messages scheduled successfully. " + scheduledCount + " messages scheduled."); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Failed to schedule bulk messages: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MyController.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MyController.java new file mode 100644 index 00000000000..dfc37212e85 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/MyController.java @@ -0,0 +1,13 @@ +package io.mosip.kernel.emailnotification.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController("/hello") +public class MyController { + + @GetMapping("/greet") + public String hello() { + return "Hello World"; + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessage.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessage.java new file mode 100644 index 00000000000..e667c6386bb --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessage.java @@ -0,0 +1,61 @@ +package io.mosip.kernel.emailnotification.model; + +import java.time.LocalDateTime; + +public class BulkMessage { + private String phoneNumber; // For WhatsApp and Viber + private String telegramUserId; // For Telegram + private String message; + private LocalDateTime scheduledTime; + private String platforms; // comma-separated: "telegram,whatsapp,viber" + + public BulkMessage() {} + + public BulkMessage(String phoneNumber, String telegramUserId, String message, LocalDateTime scheduledTime, String platforms) { + this.phoneNumber = phoneNumber; + this.telegramUserId = telegramUserId; + this.message = message; + this.scheduledTime = scheduledTime; + this.platforms = platforms; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getTelegramUserId() { + return telegramUserId; + } + + public void setTelegramUserId(String telegramUserId) { + this.telegramUserId = telegramUserId; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public LocalDateTime getScheduledTime() { + return scheduledTime; + } + + public void setScheduledTime(LocalDateTime scheduledTime) { + this.scheduledTime = scheduledTime; + } + + public String getPlatforms() { + return platforms; + } + + public void setPlatforms(String platforms) { + this.platforms = platforms; + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessageRequest.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessageRequest.java new file mode 100644 index 00000000000..5ea702f207b --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/BulkMessageRequest.java @@ -0,0 +1,21 @@ +package io.mosip.kernel.emailnotification.model; + +import java.util.List; + +public class BulkMessageRequest { + private List messages; + + public BulkMessageRequest() {} + + public BulkMessageRequest(List messages) { + this.messages = messages; + } + + public List getMessages() { + return messages; + } + + public void setMessages(List messages) { + this.messages = messages; + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageRequest.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageRequest.java new file mode 100644 index 00000000000..b2de3a3c528 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageRequest.java @@ -0,0 +1,43 @@ +package io.mosip.kernel.emailnotification.model; + +import java.time.LocalDateTime; + +public class MessageRequest { + private String text; + private LocalDateTime deliveryTime; + private String phoneNumber; // For WhatsApp and Viber + private String telegramUserId; // For Telegram + + // Getters and Setters + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public LocalDateTime getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(LocalDateTime deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getTelegramUserId() { + return telegramUserId; + } + + public void setTelegramUserId(String telegramUserId) { + this.telegramUserId = telegramUserId; + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageTemplate.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageTemplate.java new file mode 100644 index 00000000000..be94ba70716 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/MessageTemplate.java @@ -0,0 +1,72 @@ +package io.mosip.kernel.emailnotification.model; + +import java.util.List; + +public class MessageTemplate { + private String id; + private String name; + private String description; + private String content; + private List variables; + private String category; + + public MessageTemplate() {} + + public MessageTemplate(String id, String name, String description, String content, List variables, String category) { + this.id = id; + this.name = name; + this.description = description; + this.content = content; + this.variables = variables; + this.category = category; + } + + // Getters and Setters + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public List getVariables() { + return variables; + } + + public void setVariables(List variables) { + this.variables = variables; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/TemplateRequest.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/TemplateRequest.java new file mode 100644 index 00000000000..445cbc28504 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/model/TemplateRequest.java @@ -0,0 +1,32 @@ +package io.mosip.kernel.emailnotification.model; + +import java.util.Map; + +public class TemplateRequest { + private String templateId; + private Map variables; + + public TemplateRequest() {} + + public TemplateRequest(String templateId, Map variables) { + this.templateId = templateId; + this.variables = variables; + } + + // Getters and Setters + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/CsvService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/CsvService.java new file mode 100644 index 00000000000..b050616871c --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/CsvService.java @@ -0,0 +1,56 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import io.mosip.kernel.emailnotification.model.BulkMessage; +import io.mosip.kernel.emailnotification.model.BulkMessageRequest; + +@Service +public class CsvService { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public BulkMessageRequest parseCsv(MultipartFile file) throws IOException { + List messages = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) { + String line; + boolean isFirstLine = true; + + while ((line = reader.readLine()) != null) { + // Skip header row + if (isFirstLine) { + isFirstLine = false; + continue; + } + + String[] columns = line.split(","); + if (columns.length >= 5) { + try { + String phoneNumber = columns[0].trim(); + String telegramUserId = columns[1].trim(); + String message = columns[2].trim(); + LocalDateTime scheduledTime = LocalDateTime.parse(columns[3].trim(), DATE_TIME_FORMATTER); + String platforms = columns[4].trim(); + + BulkMessage bulkMessage = new BulkMessage(phoneNumber, telegramUserId, message, scheduledTime, platforms); + messages.add(bulkMessage); + } catch (Exception e) { + System.err.println("Error parsing CSV line: " + line + " - " + e.getMessage()); + } + } + } + } + + return new BulkMessageRequest(messages); + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/MessageService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/MessageService.java new file mode 100644 index 00000000000..7acce93a777 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/MessageService.java @@ -0,0 +1,212 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.concurrent.PriorityBlockingQueue; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import io.mosip.kernel.emailnotification.model.MessageRequest; + +import jakarta.annotation.PostConstruct; + +@Service +public class MessageService { + + @Value("${telegram.bot.token}") + private String botToken; + + @Value("${green.api.instance.id}") + private String instanceId; + + @Value("${green.api.token}") + private String apiToken; + + private final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + + private final PriorityBlockingQueue messageQueue = + new PriorityBlockingQueue<>(11, (MessageRequest a, MessageRequest b) -> a.getDeliveryTime().compareTo(b.getDeliveryTime())); + + @Autowired + private TelegramService telegramService; + + @Autowired + private WhatsAppService whatsAppService; + + @Autowired + private ViberService viberService; + + @PostConstruct + public void init() { + taskScheduler.initialize(); + taskScheduler.scheduleAtFixedRate(this::processMessages, Duration.ofSeconds(1)); + } + + public void scheduleMessage(MessageRequest messageRequest) { + System.out.println("📝 Scheduling message for: " + messageRequest.getPhoneNumber() + " (Telegram: " + messageRequest.getTelegramUserId() + ")"); + System.out.println("📅 Scheduled time: " + messageRequest.getDeliveryTime()); + System.out.println("⏰ Current time: " + LocalDateTime.now()); + System.out.println("⏳ Time until delivery: " + java.time.Duration.between(LocalDateTime.now(), messageRequest.getDeliveryTime()).toMinutes() + " minutes"); + messageQueue.add(messageRequest); + } + + private void processMessages() { + LocalDateTime now = LocalDateTime.now(); + if (!messageQueue.isEmpty()) { + System.out.println("🔍 Checking " + messageQueue.size() + " scheduled messages at " + now); + } + while (!messageQueue.isEmpty()) { + MessageRequest message = messageQueue.peek(); + System.out.println("🔍 Checking message scheduled for: " + message.getDeliveryTime() + " (current: " + now + ")"); + + // Convert the scheduled time to local timezone for comparison + LocalDateTime scheduledTime = message.getDeliveryTime(); + // System.out.println("⏰ Scheduled time: " + scheduledTime); + // System.out.println("⏰ Current time: " + now); + // System.out.println("⏰ Is scheduled time before now? " + scheduledTime.isBefore(now)); + // System.out.println("⏰ Is scheduled time equal to now? " + scheduledTime.isEqual(now)); + + if (scheduledTime.isBefore(now) || scheduledTime.isEqual(now)) { + message = messageQueue.poll(); + System.out.println("📅 Processing scheduled message for: " + message.getPhoneNumber() + " (Telegram: " + message.getTelegramUserId() + ") at " + now); + sendMessageToAllPlatforms(message.getPhoneNumber(), message.getTelegramUserId(), message.getText()); + } else { + // Message is scheduled for future, break the loop + System.out.println("⏳ Message scheduled for future, waiting..."); + break; + } + } + } + + public void sendMessage(MessageRequest message) { + // Use the new sendMessageToAllPlatforms method + sendMessageToAllPlatforms(message.getPhoneNumber(), message.getTelegramUserId(), message.getText()); + } + + public void sendWhatsAppMessage(String recipient, String message) { + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", "application/json"); + + // Format phone number with @c.us suffix if not already present + String chatId = recipient; + if (!chatId.contains("@c.us")) { + chatId = recipient + "@c.us"; + } + + // Create Green API payload + java.util.Map payload = new java.util.HashMap<>(); + payload.put("chatId", chatId); + payload.put("message", message); + + HttpEntity> request = new HttpEntity<>(payload, headers); + + String url = String.format("https://%s.api.greenapi.com/waInstance%s/sendMessage/%s", + instanceId, instanceId, apiToken); + + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("Failed to send WhatsApp message: " + response.getBody()); + } + } + + public void sendTelegramMessage(String recipient, String message) { + String url = String.format("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s", botToken, recipient, message); + try { + java.net.URI uri = java.net.URI.create(url); + java.net.http.HttpClient client = java.net.http.HttpClient.newHttpClient(); + java.net.http.HttpRequest request = java.net.http.HttpRequest.newBuilder(uri).GET().build(); + client.send(request, java.net.http.HttpResponse.BodyHandlers.ofString()); + } catch (IllegalArgumentException e) { + System.err.println("Invalid URL: " + e.getMessage()); + } catch (java.io.IOException | InterruptedException e) { + System.err.println("Error sending message: " + e.getMessage()); + } + } + + // public void sendWatiMessage(String recipient, String message) { + // RestTemplate restTemplate = new RestTemplate(); + + // HttpHeaders headers = new HttpHeaders(); + // headers.set("Authorization", "Bearer " + watiApiToken); + // headers.set("Content-Type", "application/json"); + + // String payload = "{" + + // "\"phone\":\"" + recipient + "\"," + + // "\"message\":\"" + message + "\"}"; + + // HttpEntity request = new HttpEntity<>(payload, headers); + + // ResponseEntity response = restTemplate.postForEntity(watiApiUrl, request, String.class); + // if (!response.getStatusCode().is2xxSuccessful()) { + // throw new RuntimeException("Failed to send WATI message: " + response.getBody()); + // } + // } + + public void sendMessageToAllPlatforms(String phoneNumber, String telegramUserId, String message) { + boolean telegramSuccess = false; + boolean whatsappSuccess = false; + boolean viberSuccess = false; + + // Try to send to Telegram (use Telegram user ID) + if (telegramUserId != null && !telegramUserId.trim().isEmpty()) { + try { + telegramService.sendTelegramMessage(telegramUserId, message); + telegramSuccess = true; + System.out.println("✅ Telegram message sent successfully"); + } catch (Exception e) { + System.err.println("❌ Telegram Error: " + e.getMessage()); + } + } else { + System.out.println("⚠️ Telegram user ID not provided, skipping Telegram"); + } + + // Try to send to WhatsApp (use phone number) + if (phoneNumber != null && !phoneNumber.trim().isEmpty()) { + try { + whatsAppService.sendWhatsAppMessage(phoneNumber, message); + whatsappSuccess = true; + System.out.println("✅ WhatsApp message sent successfully"); + } catch (Exception e) { + System.err.println("❌ WhatsApp Error: " + e.getMessage()); + } + } else { + System.out.println("⚠️ Phone number not provided, skipping WhatsApp"); + } + + // Try to send to Viber (use phone number) + if (phoneNumber != null && !phoneNumber.trim().isEmpty()) { + try { + viberService.sendViberMessage(phoneNumber, message); + viberSuccess = true; + System.out.println("✅ Viber message sent successfully"); + } catch (Exception e) { + System.err.println("❌ Viber Error: " + e.getMessage()); + } + } else { + System.out.println("⚠️ Phone number not provided, skipping Viber"); + } + + // Log overall status + int successCount = (telegramSuccess ? 1 : 0) + (whatsappSuccess ? 1 : 0) + (viberSuccess ? 1 : 0); + + if (successCount == 3) { + System.out.println("🎉 All messages sent successfully to all three platforms (Telegram, WhatsApp, Viber)"); + } else if (successCount > 0) { + System.out.println("⚠️ Partial success: " + + (telegramSuccess ? "Telegram ✓" : "Telegram ✗") + ", " + + (whatsappSuccess ? "WhatsApp ✓" : "WhatsApp ✗") + ", " + + (viberSuccess ? "Viber ✓" : "Viber ✗") + " (" + successCount + "/3 platforms)"); + } else { + System.err.println("💥 Failed to send messages to all platforms"); + } + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TelegramService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TelegramService.java new file mode 100644 index 00000000000..5146505c852 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TelegramService.java @@ -0,0 +1,42 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class TelegramService { + + @Value("${telegram.bot.token}") + private String botToken; + + public void sendTelegramMessage(String chatId, String message) { + try { + // URL encode the message to handle special characters + String encodedMessage = URLEncoder.encode(message, StandardCharsets.UTF_8); + String url = String.format("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s", botToken, chatId, encodedMessage); + + URI uri = URI.create(url); + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + System.out.println("Telegram API Response: " + response.body()); + + // Check if the response indicates an error + if (response.body().contains("\"ok\":false")) { + System.err.println("Telegram API Error: " + response.body()); + } + } catch (IllegalArgumentException e) { + System.err.println("Invalid URL: " + e.getMessage()); + } catch (java.io.IOException | InterruptedException e) { + System.err.println("Error sending Telegram message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TemplateService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TemplateService.java new file mode 100644 index 00000000000..a25b7b2d975 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/TemplateService.java @@ -0,0 +1,151 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.stereotype.Service; + +import io.mosip.kernel.emailnotification.model.MessageTemplate; + +@Service +public class TemplateService { + + private final Map templates = new HashMap<>(); + + public TemplateService() { + initializeTemplates(); + } + + private void initializeTemplates() { + // Welcome Template + templates.put("welcome", new MessageTemplate( + "welcome", + "Welcome Message", + "A friendly welcome message for new users", + "Hello {{name}}! Welcome to MOSIP registration portal. We're excited to have you on board!", + Arrays.asList("name"), + "greeting" + )); + + templates.put("certificate_expired", new MessageTemplate( + "certificate_expired", + "Certificate Expired", + "Certificate Expired", + "Hi {{name}}, your certificate has expired. Please visit our site {{site_url}} to renew it.", + Arrays.asList("name", "site_url"), + "certificate_expired" + )); + + + // Payment Due + templates.put("payment", new MessageTemplate( + "payment", + "Payment Due Notice", + "Notification about payment due", + "Dear {{name}}, your payment of {{amount}} is due on {{due_date}}. Please make the payment to avoid any late fees.", + Arrays.asList("name", "amount", "due_date"), + "payment" + )); + + // certificate Confirmation + templates.put("certificate_confirmation", new MessageTemplate( + "certificate_confirmation", + "Certificate Confirmation", + "Confirmation for existing certificates", + "Thank you {{name}}! Your certificate #{{certificate_id}} has been authenticated from our end.", + Arrays.asList("name", "certificate_id"), + "confirmation" + )); + + // Password Reset + templates.put("password_reset", new MessageTemplate( + "password_reset", + "Password Reset", + "Password reset instructions", + "Hi {{name}}, you requested a password reset. Click here to reset: {{reset_link}}. This link expires in {{expiration_time}}.", + Arrays.asList("name", "reset_link", "expiration_time"), + "security" + )); + + // Event Invitation + templates.put("event", new MessageTemplate( + "event", + "Event Expiry", + "Notification about event expiry", + "Hi {{name}}, your {{event_name}} event expires on {{expiry_date}}. Renew now to continue our valued relationship!", + Arrays.asList("name", "event_name", "expiry_date"), + "event" + )); + + // certificate Expiry + templates.put("certificate", new MessageTemplate( + "certificate", + "Certificate Expiry", + "Notification about certificate expiry", + "Hi {{name}}, your {{certificate_name}} certificate expires on {{expiry_date}}. Renew now for seamless experience!", + Arrays.asList("name", "certificate_name", "expiry_date"), + "certificate" + )); + + // Support Ticket + templates.put("support", new MessageTemplate( + "support", + "Support Ticket Update", + "Update on support tickets", + "Hello {{name}}, your support ticket #{{ticket_id}} has been {{status}}. {{additional_info}}", + Arrays.asList("name", "ticket_id", "status", "additional_info"), + "support" + )); + } + + public List getAllTemplates() { + return new ArrayList<>(templates.values()); + } + + public List getTemplatesByCategory(String category) { + return templates.values().stream() + .filter(template -> template.getCategory().equals(category)) + .toList(); + } + + public MessageTemplate getTemplateById(String id) { + return templates.get(id); + } + + public String processTemplate(String templateId, Map variables) { + MessageTemplate template = getTemplateById(templateId); + if (template == null) { + throw new IllegalArgumentException("Template not found: " + templateId); + } + + String content = template.getContent(); + + // Replace all variables in the template + for (Map.Entry entry : variables.entrySet()) { + String placeholder = "{{" + entry.getKey() + "}}"; + content = content.replace(placeholder, entry.getValue()); + } + + // Check if any variables are still not replaced + Pattern pattern = Pattern.compile("\\{\\{([^}]+)\\}\\}"); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + throw new IllegalArgumentException("Missing variables: " + matcher.group(1)); + } + + return content; + } + + public List getCategories() { + return templates.values().stream() + .map(MessageTemplate::getCategory) + .distinct() + .sorted() + .toList(); + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/ViberService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/ViberService.java new file mode 100644 index 00000000000..06b812b5f69 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/ViberService.java @@ -0,0 +1,73 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class ViberService { + + @Value("${viber.api.url}") + private String viberApiUrl; + + @Value("${viber.api.token}") + private String viberApiToken; + + @Value("${viber.sender.name}") + private String senderName; + + public void sendViberMessage(String recipient, String message) { + // Check if Viber API is properly configured + if (viberApiToken.equals("YOUR_VIBER_API_TOKEN") || viberApiUrl.contains("")) { + System.err.println("Viber API not properly configured. Please update application.properties with valid credentials."); + return; + } + + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "App " + viberApiToken); + headers.set("Content-Type", "application/json"); + headers.set("Accept", "application/json"); + + // Create Viber API payload + Map destination = new HashMap<>(); + destination.put("to", recipient); + + Map content = new HashMap<>(); + content.put("text", message); + content.put("type", "TEXT"); + + Map messageObj = new HashMap<>(); + messageObj.put("sender", senderName); + messageObj.put("destinations", new Object[]{destination}); + messageObj.put("content", content); + + Map payload = new HashMap<>(); + payload.put("messages", new Object[]{messageObj}); + + HttpEntity> request = new HttpEntity<>(payload, headers); + + try { + ResponseEntity response = restTemplate.postForEntity(viberApiUrl, request, String.class); + System.out.println("Viber API Response: " + response.getBody()); + + if (!response.getStatusCode().is2xxSuccessful()) { + System.err.println("Viber API Error: " + response.getStatusCode() + " - " + response.getBody()); + throw new RuntimeException("Failed to send Viber message: " + response.getBody()); + } + } catch (org.springframework.web.client.RestClientException e) { + System.err.println("Error while sending Viber message: " + e.getMessage()); + throw e; + } catch (Exception e) { + System.err.println("Unexpected error while sending Viber message: " + e.getMessage()); + throw new RuntimeException("Failed to send Viber message", e); + } + } +} diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/WhatsAppService.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/WhatsAppService.java new file mode 100644 index 00000000000..97314871412 --- /dev/null +++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/WhatsAppService.java @@ -0,0 +1,66 @@ +package io.mosip.kernel.emailnotification.service.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class WhatsAppService { + + @Value("${green.api.instance.id}") + private String instanceId; + + @Value("${green.api.token}") + private String apiToken; + + public void sendWhatsAppMessage(String recipient, String message) { + // Check if Green API is properly configured + if (apiToken.equals("YOUR_GREEN_API_TOKEN") || instanceId.equals("YOUR_INSTANCE_ID")) { + System.err.println("Green API not properly configured. Please update application.properties with valid credentials."); + return; + } + + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", "application/json"); + + // Format phone number with @c.us suffix if not already present + String chatId = recipient; + if (!chatId.contains("@c.us")) { + chatId = recipient + "@c.us"; + } + + // Create Green API payload + Map payload = new HashMap<>(); + payload.put("chatId", chatId); + payload.put("message", message); + + HttpEntity> request = new HttpEntity<>(payload, headers); + + try { + String url = String.format("https://7105.api.greenapi.com/waInstance%s/sendMessage/%s", + instanceId, apiToken); + + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + System.out.println("Green API Response: " + response.getBody()); + + if (!response.getStatusCode().is2xxSuccessful()) { + System.err.println("Green API Error: " + response.getStatusCode() + " - " + response.getBody()); + throw new RuntimeException("Failed to send WhatsApp message: " + response.getBody()); + } + // } catch (org.springframework.web.client.RestClientException | org.springframework.web.client.HttpClientErrorException e) { + // System.err.println("Error while sending WhatsApp message: " + e.getMessage()); + // throw e; + } catch (Exception e) { + System.err.println("Unexpected error while sending WhatsApp message: " + e.getMessage()); + throw new RuntimeException("Failed to send WhatsApp message", e); + } + } +} \ No newline at end of file diff --git a/kernel/kernel-notification-service/src/main/resources/application-local.properties b/kernel/kernel-notification-service/src/main/resources/application-local.properties index 9cc5fe81cb6..758d71b2355 100644 --- a/kernel/kernel-notification-service/src/main/resources/application-local.properties +++ b/kernel/kernel-notification-service/src/main/resources/application-local.properties @@ -46,4 +46,19 @@ mosip.kernel.sms.proxy-sms=false mosip.kernel.email.proxy-email=false mosip.role.kernel.postemailsend=ZONAL_ADMIN,PRE_REGISTRATION_ADMIN,AUTH,ID_AUTHENTICATION,RESIDENT,REGISTRATION_ADMIN,REGISTRATION_OFFICER,REGISTRATION_PROCESSOR,REGISTRATION_SUPERVISOR,INDIVIDUAL -mosip.role.kernel.postsmssend=PRE_REGISTRATION_ADMIN,AUTH,ID_AUTHENTICATION,RESIDENT,REGISTRATION_ADMIN,REGISTRATION_OFFICER,REGISTRATION_PROCESSOR,REGISTRATION_SUPERVISOR,INDIVIDUAL \ No newline at end of file +mosip.role.kernel.postsmssend=PRE_REGISTRATION_ADMIN,AUTH,ID_AUTHENTICATION,RESIDENT,REGISTRATION_ADMIN,REGISTRATION_OFFICER,REGISTRATION_PROCESSOR,REGISTRATION_SUPERVISOR,INDIVIDUAL + +server.port=8080 +telegram.bot.token=1234567 + +spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false +spring.main.allow-bean-definition-overriding=true + +# Green API Configuration for WhatsApp +green.api.instance.id=123456789abcdef +green.api.token=123456789 + +# Viber API Configuration (Infobip) +viber.api.url=https://e5zl2r.api.infobip.com/viber/2/messages +viber.api.token=123456789 +viber.sender.name=IBSelfServe \ No newline at end of file