Getting started with Amazon pinpoint – Part 2

Reading Time: 3 minutes

In the last article, we have discussed amazon pinpoint, benefits, services, channels, use cases, etc. You may go through the Getting started with Amazon pinpoint (Part-1). So In this article, we will discuss how we can implement a voice message sending API using amazon pinpoint with scala. We will be using Akka HTTP. It is an Akka-based HTTP library for building RESTful services. You can learn more about Akka HTTP from here.

Project Setup

We’ll be using sbt version 1.5.2 and Scala version 2.12.12. Let’s start by adding the required dependencies. We need to add AWS SDK to use amazon pinpoint in our project. Since Akka HTTP depends on actors and streams. We’ll need to add those libraries as well.

val AkkaVersion = "2.6.8"
val AkkaHttpVersion = "10.2.4"
val awsSDK = "2.17.61"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,
  "software.amazon.awssdk" % "aws-sdk-java" % awsSDK
)

Now let’s create a package under “src/main/scala” name “org.knoldus” and create a scala object named “HttpServer”. Add the following dependencies to create a server with Akka HTTP.

Creating the server

we will need a port and host to bind the server. So add the following variable

val host = "localhost"
val port = 7000

Akka HTTP uses Akka actors and streams for handling concurrent requests. So we have to add the dependencies for this as well.

implicit val system = ActorSystem("Amazon-Pinpoint-Server")
implicit val materializer : ActorMaterializer.type = ActorMaterializer
import system.dispatcher

Creating Voice Message Service

Let’s create a class named Pinpoint.scala and create the following methods.

  • makeCall – This method will send a voice message to the recipient number.
  • createPinpointClient – This will create an pinpoint client to send a voice mesage.
  • createMessageType – This will create an object of SSMLMessageType.
  • createContent – This will create an voiceMessageContent.
  • createVoiceMessageRequest – This will create an message request which can be sent to pinpoint.
  • sendVoiceMessageRequest – This will send a request to amazon pinpoint client to send a voice message.
  def makeCall(body: String, toNumber: String): Future[Response] = {

    val languageCode: String = "en-US"
    val fromNumber = "1234567890"
    val client: PinpointSmsVoiceClient = createPinpointClient
    val ssmlTypeBody = s"<speak>$body</speak>"


    try {
      val ssmlMessageType: SSMLMessageType = createMessageType(
        languageCode,
        ssmlTypeBody
      )
      val content: VoiceMessageContent = createContent(ssmlMessageType)
      val voiceMessageRequest: SendVoiceMessageRequest = createVoiceMessageRequest(
        toNumber,
        fromNumber,
        content
      )

      sendVoiceMessageRequest(client, voiceMessageRequest)
      Future(Successful)
    }
    catch {
      case e: BadRequestException =>
        Future(Failed)
      case ex: PinpointException =>
        Future(Failed)
      case exception: Exception =>
        Future(Failed)
    }
    finally {
      client.close()
    }

  }


  def createPinpointClient: PinpointSmsVoiceClient = {
    val list: util.List[String] = new util.ArrayList[String]()
    list.add("application/json")
    val values: util.Map[String, util.List[String]] = new util.HashMap[String, util.List[String]]()
    values.put("Content-Type", list)
    val clientConfig: ClientOverrideConfiguration = ClientOverrideConfiguration.builder()
      .headers(values)
      .build()

    PinpointSmsVoiceClient.builder()
      .overrideConfiguration(clientConfig)
      .region(Region.US_EAST_1)
      .build()
  }

  protected def createMessageType(
                                   languageCode: String,
                                   ssmlMessage: String
                                 ): SSMLMessageType = {
    SSMLMessageType.builder()
      .languageCode(languageCode)
      .text(ssmlMessage)
      .build()
  }

  protected def createContent(ssmlMessageType: SSMLMessageType): VoiceMessageContent = {
    VoiceMessageContent.builder()
      .ssmlMessage(ssmlMessageType)
      .build()
  }

  protected def createVoiceMessageRequest(
                                           destinationNumber: String,
                                           originationNumber: String,
                                           content: VoiceMessageContent
                                         ): SendVoiceMessageRequest = {
    SendVoiceMessageRequest.builder()
      .destinationPhoneNumber(destinationNumber)
      .originationPhoneNumber(originationNumber)
      .content(content)
      .build()
  }

  def sendVoiceMessageRequest(
                               client: PinpointSmsVoiceClient,
                               voiceMessageRequest: SendVoiceMessageRequest
                             ): SendVoiceMessageResponse = {
    client.sendVoiceMessage(voiceMessageRequest)
  }

Above we can see that makecall method takes two parameters body and toNumber. This method either returns Successful or Failure and in finally block it will close the pinpoint client.

Defining Routes

Route specifies the URI endpoints REST server exposing. Create another class named Routes.scala and add the following route below the dependencies.

  val provider = new Pinpoint

  val route = {
    pathPrefix("api" / "send"){
      get{
        (path(LongNumber) | parameter(Symbol("id").as[Long])){ id =>
          complete(
            provider.makeCall("Hello from amazon Pinpoint.",id.toString).map{
              case Successful =>
                (StatusCodes.OK,"Voice message has been send successfully.")
              case Failed =>
                (StatusCodes.BadRequest,"unable to send voice message.")
            }
          )
        }
      } ~
        pathEndOrSingleSlash{
          complete(StatusCodes.BadRequest, s"Route Not found")
        }

    } ~
    pathEndOrSingleSlash{
      complete(StatusCodes.BadRequest, s"Route Not found")
    }
  }

Above we can see we have created an object of Pinpoint class and created routes with the following properties.

  • “/api/send/ContactNumber” is the directive (ContactNumber refers to real mobile number with country code without + sign)
  • get is the method
  • complete() will returns a Successful message with OK status or failure message with BadRequest status.

Bind the Server

Now we need to create a server and bind to this route, address, and port. We need to make an object of route class.

val route = new Routes

val binding = Http().newServerAt(host,port).bindFlow(route.route)

Now to check if our server is running or not. We will add logging to the console

binding.onComplete{
    case Success(value) =>
      println(s"Server is listening on http://$host:${port}")
    case Failure(exception) =>
      println(s"Failure : $exception")
      system.terminate()
  }

Now we have to run the HttpServer object. Just right-click and hit Run “HttpServer”. You can see the run is up and running. You can use postman to send voice message.

Conclusion

In this article we have seen how can we create an API for sending voice message using amazon pinpoint voice channel. You can also check out the code here.

If you find this article interesting, please check out our other articles.

knoldus

Written by 

Asbin Bhadra is a Software consultant at Knoldus Inc. Knoldus does niche Reactive and Big Data product development on Scala, Spark, and Functional Java. He is recognized as a good team player, a dedicated and responsible professional, and a technology enthusiast. His current passions include utilizing the power of Scala, Akka, and Play to make Reactive systems. He has also contributed to various Akka, Play, and Scala-based templates.

Leave a Reply