Skip to content

Commit

Permalink
Campaign manager added to quickstart
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander.Mair1 committed Nov 4, 2024
1 parent fa37347 commit 081f4c0
Show file tree
Hide file tree
Showing 14 changed files with 593 additions and 0 deletions.
3 changes: 3 additions & 0 deletions tutorials/sms/sinch-campaign-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sinch Campaign Manager Application

This directory contains the completed application for the [Java SMS campaign manager tutorial](https://developers.sinch.com/docs/sms/tutorials/sms/tutorials/java-sdk/campaign-manager)
57 changes: 57 additions & 0 deletions tutorials/sms/sinch-campaign-manager/campaign/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sinch</groupId>
<artifactId>campaign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>campaign</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.2.5</version>
</dependency>

<dependency>
<groupId>com.sinch.sdk</groupId>
<artifactId>sinch-sdk-java</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.sinch.campaign;

import com.sinch.sdk.domains.sms.SMSService;
import com.sinch.sdk.domains.sms.models.Group;
import com.sinch.sdk.domains.sms.models.InboundText;
import com.sinch.sdk.domains.sms.models.requests.GroupUpdateRequestParameters;
import com.sinch.sdk.domains.sms.models.requests.SendSmsBatchTextRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AutoSubscribeService {

private static final Logger LOGGER = Logger.getLogger(AutoSubscribeService.class.getName());

static final String SUBSCRIBE_ACTION = "SUBSCRIBE";
static final String STOP_ACTION = "STOP";

private final SMSService smsService;
public final Group group;

@Autowired
public AutoSubscribeService(SMSService smsService, GroupManager groupManager) {
this.smsService = smsService;
this.group = groupManager.getGroup();
}

public void processInboundEvent(InboundText event) {

LOGGER.info("Received event:" + event);

var from = event.getFrom();
var to = event.getTo();
var action = event.getBody().trim();

var membersList = getMembersList(group);
var isMemberInGroup = isMemberInGroup(membersList, from);

String response =
switch (action) {
case SUBSCRIBE_ACTION -> subscribe(group, isMemberInGroup, to, from);
case STOP_ACTION -> unsubscribe(group, isMemberInGroup, to, from);
default ->
"Thanks for your interest. If you want to receive SMS notificatins from this group, text \"SUBSCRIBE\" to +%s"
.formatted(to);
};

sendResponse(to, from, response);
}

private Collection<String> getMembersList(Group group) {
return smsService.groups().listMembers(group.getId());
}

private boolean isMemberInGroup(Collection<String> membersList, String member) {
return membersList.contains(member);
}

private String subscribe(
Group group, boolean isMemberInGroup, String groupPhoneNumber, String member) {

if (isMemberInGroup) {
return "You already subscribed to '%s'. Text \"STOP\" to +%s to leave this group."
.formatted(group.getName(), groupPhoneNumber);
}

var request =
GroupUpdateRequestParameters.builder()
.setAdd(Collections.singletonList(member))
.build();

smsService.groups().update(group.getId(), request);
return "Congratulations! You are now subscribed to '%s'. Text \"STOP\" to +%s to leave this group."
.formatted(group.getName(), groupPhoneNumber);
}

private String unsubscribe(
Group group, boolean isMemberInGroup, String groupPhoneNumber, String member) {

if (!isMemberInGroup) {
return "You did not subscribed to '%s'. Text \"SUBSCRIBE\" to +%s to join this group."
.formatted(group.getName(), groupPhoneNumber);
}

var request =
GroupUpdateRequestParameters.builder()
.setRemove(Collections.singletonList(member))
.build();

smsService.groups().update(group.getId(), request);
return "We're sorry to see you go. You can always rejoin '%s' by texting \"SUBSCRIBE\" to +%s."
.formatted(group.getName(), groupPhoneNumber);
}

private void sendResponse(String from, String to, String response) {

var request =
SendSmsBatchTextRequest.builder()
.setTo(Collections.singletonList(to))
.setBody(response)
.setFrom(from)
.build();

smsService.batches().send(request);

LOGGER.info("Replied: '%s'".formatted(response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.sinch.campaign;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CampaignApplication {

public static void main(String[] args) {
SpringApplication.run(CampaignApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.sinch.campaign;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import com.sinch.sdk.domains.sms.SMSService;
import com.sinch.sdk.domains.sms.models.InboundText;
import com.sinch.sdk.domains.sms.models.requests.SendSmsBatchTextRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import java.util.logging.Logger;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;

@Controller
public class CampaignController {
private final SMSService smsService;
private final AutoSubscribeService service;
@Value("${sinch_details.from_number}")
String from_number;

private static final Logger LOGGER = Logger.getLogger(CampaignController.class.getName());

public CampaignController(SMSService smsService, AutoSubscribeService service) {

this.smsService = smsService;
this.service = service;

}

@PostMapping(value = "/subscribe", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> smsDeliveryEvent(@RequestBody String body) {

// decode the request payload
var event = smsService.webHooks().parse(body);

// let business layer process the request
if (event instanceof InboundText e) {
service.processInboundEvent(e);
} else {
throw new IllegalStateException("Unexpected value: " + event);
}
return new ResponseEntity<String>("OK", HttpStatus.NO_CONTENT);
}

@GetMapping("/")
public String index() {
return "redirect:/campaign";
}

@GetMapping("/campaign")
public String campaign() {
return "message_composer";
}

private void send_campaign(String message, Instant sendAt) {

smsService.batches().send(SendSmsBatchTextRequest.builder()
.setTo(smsService.groups().listMembers(service.group.getId()))
.setBody(message)
.setFrom(from_number).setSendAt(sendAt)
.build());

}

@GetMapping("/success")
public String getSuccess(HttpServletRequest request) {
return "success";
}

@PostMapping("/campaign")
public RedirectView submitPost(
@RequestParam("message") String message,
@RequestParam("campaign_date") LocalDate date,
@RequestParam("campaign_time") String time,
HttpServletRequest request,
RedirectAttributes redirectAttributes) {
LocalDateTime campaignDateTime = LocalDateTime.of(date, LocalTime.parse(time));

ZonedDateTime zonedCampaignDateTime = campaignDateTime.atZone(ZoneId.systemDefault());
ZonedDateTime utcDateTime = zonedCampaignDateTime.withZoneSameInstant(ZoneId.of("UTC"));

Instant sendAt = utcDateTime.toInstant();

send_campaign(message, sendAt);

redirectAttributes.addFlashAttribute("campaign_datetime", campaignDateTime);
return new RedirectView("/success", true);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sinch.campaign;

import com.sinch.sdk.SinchClient;
import com.sinch.sdk.domains.sms.SMSService;
import com.sinch.sdk.models.Configuration;
import com.sinch.sdk.models.SMSRegion;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Config {

@Value("${sinch_details.project-id}")
String projectId;

@Value("${sinch_details.key-id}")
String keyId;

@Value("${sinch_details.key-secret}")
String keySecret;

@Bean
public SMSService smsService() {

var configuration =
Configuration.builder()
.setProjectId(projectId)
.setKeyId(keyId)
.setKeySecret(keySecret)
.setSmsRegion(SMSRegion.EU)
.build();

return new SinchClient(configuration).sms();
}
}
Loading

0 comments on commit 081f4c0

Please sign in to comment.