diff --git a/build.gradle b/build.gradle index 5d77fa3..c7c6a1f 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { } group = 'com.flexcodelabs' -version = '0.0.61' +version = '0.0.62' description = 'Flextuma App' java { diff --git a/src/main/java/com/flexcodelabs/flextuma/core/enums/SmsLogStatus.java b/src/main/java/com/flexcodelabs/flextuma/core/enums/SmsLogStatus.java index 1bc2806..8cf8cd9 100644 --- a/src/main/java/com/flexcodelabs/flextuma/core/enums/SmsLogStatus.java +++ b/src/main/java/com/flexcodelabs/flextuma/core/enums/SmsLogStatus.java @@ -5,5 +5,6 @@ public enum SmsLogStatus { PROCESSING, SENT, FAILED, - DELIVERED + DELIVERED, + READ } diff --git a/src/main/java/com/flexcodelabs/flextuma/modules/dashboard/services/DashboardService.java b/src/main/java/com/flexcodelabs/flextuma/modules/dashboard/services/DashboardService.java index a91e30b..c7edbff 100644 --- a/src/main/java/com/flexcodelabs/flextuma/modules/dashboard/services/DashboardService.java +++ b/src/main/java/com/flexcodelabs/flextuma/modules/dashboard/services/DashboardService.java @@ -37,7 +37,7 @@ @RequiredArgsConstructor public class DashboardService { - private static final EnumSet SUCCESS_STATUSES = EnumSet.of(SmsLogStatus.SENT, SmsLogStatus.DELIVERED); + private static final EnumSet SUCCESS_STATUSES = EnumSet.of(SmsLogStatus.SENT, SmsLogStatus.DELIVERED, SmsLogStatus.READ); private static final EnumSet ACTIVE_CAMPAIGN_STATUSES = EnumSet.of( SmsCampaignStatus.SCHEDULED, SmsCampaignStatus.PROCESSING); diff --git a/src/main/java/com/flexcodelabs/flextuma/modules/whatsapp/controllers/WhatsAppWebhookController.java b/src/main/java/com/flexcodelabs/flextuma/modules/whatsapp/controllers/WhatsAppWebhookController.java index 3f37849..d6d141d 100644 --- a/src/main/java/com/flexcodelabs/flextuma/modules/whatsapp/controllers/WhatsAppWebhookController.java +++ b/src/main/java/com/flexcodelabs/flextuma/modules/whatsapp/controllers/WhatsAppWebhookController.java @@ -134,15 +134,34 @@ private void updateDeliveryStatus(Map payload) { } } + // sent < delivered < read: Meta's status callbacks can arrive out of order (or duplicated), + // and without this a late "delivered" retry could visibly regress an already-READ message's + // blue ticks back to gray. FAILED isn't in this progression -- it's a terminal outcome, not + // a step past it. + private static final List DELIVERY_PROGRESSION = List.of(SmsLogStatus.SENT, SmsLogStatus.DELIVERED, SmsLogStatus.READ); + @SuppressWarnings("unchecked") private void applyStatus(SmsLog logEntry, String status, Map raw) { - if ("delivered".equalsIgnoreCase(status) || "read".equalsIgnoreCase(status)) logEntry.setStatus(SmsLogStatus.DELIVERED); - else if ("failed".equalsIgnoreCase(status)) { logEntry.setStatus(SmsLogStatus.FAILED); logEntry.setError(extractErrorMessage(raw)); } - else if ("sent".equalsIgnoreCase(status)) logEntry.setStatus(SmsLogStatus.SENT); + SmsLogStatus next; + if ("read".equalsIgnoreCase(status)) next = SmsLogStatus.READ; + else if ("delivered".equalsIgnoreCase(status)) next = SmsLogStatus.DELIVERED; + else if ("failed".equalsIgnoreCase(status)) next = SmsLogStatus.FAILED; + else if ("sent".equalsIgnoreCase(status)) next = SmsLogStatus.SENT; else return; + + if (next != SmsLogStatus.FAILED && isDeliveryRegression(logEntry.getStatus(), next)) return; + + logEntry.setStatus(next); + if (next == SmsLogStatus.FAILED) logEntry.setError(extractErrorMessage(raw)); smsLogRepository.save(logEntry); } + private boolean isDeliveryRegression(SmsLogStatus current, SmsLogStatus next) { + int currentIndex = DELIVERY_PROGRESSION.indexOf(current); + int nextIndex = DELIVERY_PROGRESSION.indexOf(next); + return currentIndex >= 0 && nextIndex >= 0 && nextIndex < currentIndex; + } + private String extractErrorMessage(Map raw) { Object errors = raw.get("errors"); if (!(errors instanceof List list) || list.isEmpty() || !(list.get(0) instanceof Map first)) return null;