Commit 32bbd3b8 authored by Andreas Muttscheller's avatar Andreas Muttscheller

Implement own SlackApiClient

parent d4a3519e
......@@ -30,9 +30,6 @@ lazy val root = (project in file("."))
"com.amazonaws" % "aws-lambda-java-events" % "2.2.5",
"com.amazonaws" % "aws-lambda-java-core" % "1.2.0",
// Slack
"com.github.slack-scala-client" %% "slack-scala-client" % "0.2.5",
// ElasicSearch
"com.sksamuel.elastic4s" % "elastic4s-core_2.12" % "6.3.8",
"com.sksamuel.elastic4s" %% "elastic4s-http" % "6.3.8",
......
......@@ -2,32 +2,18 @@ package de.codecentric.amuttsch.bahndelayinfo.aws.lambda
import java.net.URLDecoder
import akka.actor.ActorSystem
import com.amazonaws.services.dynamodbv2.document.{DynamoDB, Table}
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClientBuilder}
import com.amazonaws.services.lambda.AWSLambdaClientBuilder
import com.amazonaws.services.lambda.model.{InvocationType, InvokeRequest}
import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.events.{APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent}
import com.typesafe.config.{Config, ConfigFactory}
import com.typesafe.scalalogging.Logger
import de.codecentric.amuttsch.bahndelayinfo.aws
import org.json4s._
import org.json4s.native.Serialization.write
import slack.api.BlockingSlackApiClient
class APISlackBotEventHandler {
implicit val logger: Logger = Logger(classOf[APISlackBotEventHandlerWorker])
implicit val jsonFormats: DefaultFormats.type = DefaultFormats
implicit val system: ActorSystem = ActorSystem("slack")
val ddbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.standard.build
val ddb: DynamoDB = new DynamoDB(ddbClient)
val tableSlackUsers: Table = ddb.getTable("SlackUsers")
val apiConf: Config = ConfigFactory.load("api")
val token: String = apiConf.getString("slack.token")
val slackApiClient: BlockingSlackApiClient = BlockingSlackApiClient(token)
def handleRequest(event: APIGatewayProxyRequestEvent, context: Context): APIGatewayProxyResponseEvent = {
val lambdaClient = AWSLambdaClientBuilder.defaultClient
......
package de.codecentric.amuttsch.bahndelayinfo.aws.lambda
import akka.actor.ActorSystem
import com.amazonaws.services.dynamodbv2.document.{DynamoDB, Index, Item, Table}
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClientBuilder}
import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent
import com.softwaremill.sttp._
import com.typesafe.config.{Config, ConfigFactory}
import com.typesafe.scalalogging.Logger
import de.codecentric.amuttsch.bahndelayinfo.slackbot.{SearchStation, _}
......@@ -14,7 +12,6 @@ import de.codecentric.amuttsch.bahndelayinfo.utils.JsonSerializers
import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization.write
import slack.api.BlockingSlackApiClient
import scala.collection.JavaConverters._
import scala.collection.mutable
......@@ -25,7 +22,6 @@ class APISlackBotEventHandlerWorker {
implicit val jsonFormats: Formats = DefaultFormats +
JsonSerializers.LocalTimeSerializer +
JsonSerializers.LocalDateTimeSerializer
implicit val system: ActorSystem = ActorSystem("slack")
val ddbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.standard.build
val ddb: DynamoDB = new DynamoDB(ddbClient)
......@@ -36,7 +32,7 @@ class APISlackBotEventHandlerWorker {
val apiConf: Config = ConfigFactory.load("api")
val token: String = apiConf.getString("slack.token")
implicit val slackApiClient: BlockingSlackApiClient = BlockingSlackApiClient(token)
implicit val ownApiClient: SlackClient = SlackClient(token)
var seenEvents: mutable.Queue[String] = mutable.Queue[String]()
......@@ -139,10 +135,12 @@ class APISlackBotEventHandlerWorker {
implicit val slackUser: SlackUser = getOrCreateUser(message)
commands(cmd).execute(params)
case tail =>
slackApiClient.postChatMessage(
message.channel,
s"Unknown command: ${tail.mkString("_")}",
asUser = Some(true))
ownApiClient.postMessage(
SlackApiMessage(
message.channel,
s"Unknown command: ${tail.mkString("_")}"
)
)
}
new APIGatewayProxyResponseEvent()
......@@ -167,10 +165,11 @@ class APISlackBotEventHandlerWorker {
| Author: <@UEJN43CP9> Support: <#CFP0G0X0U> Code: https://gitlab.codecentric.de/andreas.muttscheller/BahnDelayInformation/
|
""".stripMargin
slackApiClient.postChatMessage(
message.channel,
helpText,
asUser = Some(true)
ownApiClient.postMessage(
SlackApiMessage(
message.channel,
helpText
)
)
}
......
package de.codecentric.amuttsch.bahndelayinfo.aws.lambda
import akka.actor.ActorSystem
import com.amazonaws.services.dynamodbv2.document.{DynamoDB, Table}
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClientBuilder}
import com.amazonaws.services.lambda.runtime.Context
......@@ -18,7 +17,6 @@ import scala.collection.JavaConverters._
class APIStationQuery {
implicit val logger: Logger = Logger(classOf[APISlackBotEventHandlerWorker])
implicit val jsonFormats: DefaultFormats.type = DefaultFormats
implicit val system: ActorSystem = ActorSystem("slack")
import com.sksamuel.elastic4s.http.ElasticDsl._
val ddbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.standard.build
......@@ -95,10 +93,8 @@ class APIStationQuery {
object APIStationQuery extends App {
val asq = new APIStationQuery()
val request = new APIGatewayProxyRequestEvent()
.withQueryStringParamters(Map("q" -> "Darmstadt").asJava)
.withQueryStringParameters(Map("q" -> "Darmstadt").asJava)
val resp = asq.handleRequest(request, null)
println(s"$resp")
asq.system.terminate()
}
\ No newline at end of file
......@@ -2,7 +2,6 @@ package de.codecentric.amuttsch.bahndelayinfo.aws.lambda
import java.time.LocalTime
import akka.actor.ActorSystem
import com.amazonaws.services.dynamodbv2.document.{DynamoDB, Item, Table}
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClientBuilder}
import com.amazonaws.services.lambda.runtime.Context
......@@ -11,12 +10,11 @@ import com.typesafe.config.{Config, ConfigFactory}
import com.typesafe.scalalogging.Logger
import de.codecentric.amuttsch.bahndelayinfo.aws.sns.SNSNewDelayInformation
import de.codecentric.amuttsch.bahndelayinfo.fetcher.TimetableInformation
import de.codecentric.amuttsch.bahndelayinfo.slackbot.SlackUser
import de.codecentric.amuttsch.bahndelayinfo.slackbot.{SlackClient, SlackUser}
import de.codecentric.amuttsch.bahndelayinfo.utils.JsonSerializers
import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization.write
import slack.api.BlockingSlackApiClient
import de.codecentric.amuttsch.bahndelayinfo.utils.TraversableImplicits._
import scala.util.{Success, Try}
......@@ -29,7 +27,7 @@ class SNSChangeSlackReporter {
val apiConf: Config = ConfigFactory.load("api")
val token: String = apiConf.getString("slack.token")
val slackApiClient: BlockingSlackApiClient = BlockingSlackApiClient(token)
val slackApiClient: SlackClient = SlackClient(token)
val ddbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.standard.build
val ddb: DynamoDB = new DynamoDB(ddbClient)
......@@ -46,7 +44,6 @@ class SNSChangeSlackReporter {
}
def parseDelayInformation(newDelayInformation: SNSNewDelayInformation): Unit = {
implicit val system: ActorSystem = ActorSystem("slack")
var slackRecipients = Set.empty[SlackUser]
val slackUserItems = tableSlackUsers.scan()
......@@ -80,11 +77,11 @@ class SNSChangeSlackReporter {
(d.from.isDefined && d.until.isDefined &&
d.from.get.isBefore(LocalTime.from(tti.plannedTime)) && d.until.get.isAfter(LocalTime.from(tti.plannedTime))) }
.foreach { case (tti, sr, _) =>
val resp = slackApiClient.postChatMessage(
slackApiClient.postMessage(
sr.channel,
s"`${tti.toString}`"
)
logger.info(s"Sending messages to channel ${sr.channel} for station ${tti.station}: $resp")
logger.info(s"Sending messages to channel ${sr.channel} for station ${tti.station}")
val suJson = tableSlackUsers.getItem("id", sr.user).toJSON
val newSlackUser = parse(suJson).extract[SlackUser]
......@@ -97,8 +94,6 @@ class SNSChangeSlackReporter {
tableSlackUsers.putItem(item)
}
}
system.terminate()
}
}
......
package de.codecentric.amuttsch.bahndelayinfo.slackbot
case class SlackApiMessage(
channel: String,
text: String,
attachments: Option[Seq[SlackAttachment]] = None
)
case class SlackAttachment (
callback_id: Option[String] = None,
color: Option[String] = None,
text: Option[String] = None,
actions: Option[Seq[SlackActionField]] = None,
)
case class SlackActionField(name: String,
text: String, `type`: String,
style: Option[String] = None,
value: Option[String] = None)
package de.codecentric.amuttsch.bahndelayinfo.slackbot
import com.softwaremill.sttp._
import org.json4s.native.Serialization.write
import org.json4s.{DefaultFormats, Formats}
class SlackClient(token: String) {
private implicit val backend: SttpBackend[Id, Nothing] = HttpURLConnectionBackend()
private implicit val jsonFormats: Formats = DefaultFormats
val slackApiBaseUri = "https://slack.com/api/"
def postMessage(
channel: String,
text: String,
attachments: Option[Seq[SlackAttachment]] = None
): Unit = {
postMessage(
SlackApiMessage(
channel,
text,
attachments
)
)
}
def postMessage(
message: SlackApiMessage
): Unit = {
sttp
.post(uri"${slackApiBaseUri}chat.postMessage")
.headers(Map(
"Content-type" -> "application/json",
"Authorization" -> s"Bearer $token"
))
.body(write(message))
.send()
}
}
object SlackClient {
def apply(token: String): SlackClient = new SlackClient(token)
}
\ No newline at end of file
......@@ -3,7 +3,6 @@ package de.codecentric.amuttsch.bahndelayinfo.slackbot
import java.time.format.DateTimeFormatter
import java.time.{LocalDate, LocalTime, ZoneId}
import akka.actor.ActorSystem
import com.amazonaws.services.dynamodbv2.document.{DynamoDB, Index, Item, Table}
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClientBuilder}
import com.amazonaws.services.lambda.AWSLambdaClientBuilder
......@@ -21,8 +20,6 @@ import de.codecentric.amuttsch.bahndelayinfo.utils.JsonSerializers
import org.json4s.native.JsonMethods.parse
import org.json4s.native.Serialization.write
import org.json4s.{DefaultFormats, Formats}
import slack.api.BlockingSlackApiClient
import slack.models.{ActionField, Attachment}
import scala.annotation.tailrec
import scala.concurrent.duration.{Duration, MINUTES}
......@@ -45,8 +42,7 @@ sealed trait SlackCommand {
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit
def printHelp: String = {
......@@ -127,8 +123,7 @@ case object SearchStation extends SlackCommand with StationFinder {
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val station = params.mkString(" ")
......@@ -136,10 +131,9 @@ case object SearchStation extends SlackCommand with StationFinder {
case Right(s) => List(s)
case Left(s) => s
}
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Stations found:\n" + stations.map(_.name).mkString("\n"),
asUser = Some(true)
"Stations found:\n" + stations.map(_.name).mkString("\n")
)
}
}
......@@ -160,8 +154,7 @@ case object AddStation extends SlackCommand with StationFinder {
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val station = params.mkString(" ")
getEitherStationOrStations(station) match {
......@@ -173,16 +166,14 @@ case object AddStation extends SlackCommand with StationFinder {
rq.setInvocationType(InvocationType.Event)
lambdaClient.invoke(rq)
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
s"Added station ${s.name}!",
asUser = Some(true)
s"Added station ${s.name}!"
)
case Left(s) =>
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Please specify station:\n" + s.map(_.name).mkString("\n"),
asUser = Some(true)
"Please specify station:\n" + s.map(_.name).mkString("\n")
)
}
}
......@@ -208,22 +199,21 @@ case object SubscribeDelay extends SlackCommand with StationFinder with Paramete
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val paramsMap = parseCommand(params)
if (!paramsMap.isDefinedAt("-s")) {
slackApiClient.postChatMessage(slackUser.channel,s"Station not defined!", asUser = Some(true))
slackApiClient.postMessage(slackUser.channel,s"Station not defined!")
return
}
if (!paramsMap.isDefinedAt("-t")) {
slackApiClient.postChatMessage(slackUser.channel,s"Train not defined!", asUser = Some(true))
slackApiClient.postMessage(slackUser.channel,s"Train not defined!")
return
}
val invalidParams = paramsMap.keys.filter(!_.startsWith("-"))
if (invalidParams.nonEmpty) {
slackApiClient.postChatMessage(slackUser.channel,s"Invalid parameters found: ${invalidParams.mkString(" ")} ", asUser = Some(true))
slackApiClient.postMessage(slackUser.channel,s"Invalid parameters found: ${invalidParams.mkString(" ")} ")
return
}
......@@ -238,16 +228,15 @@ case object SubscribeDelay extends SlackCommand with StationFinder with Paramete
Try(train.r) match {
case Success(_) =>
case Failure(_) =>
slackApiClient.postChatMessage(slackUser.channel, "Invalid regex for train", asUser = Some(true))
slackApiClient.postMessage(slackUser.channel, "Invalid regex for train")
return
}
val matchedStation = getEitherStationOrStations(station) match {
case Left(stations) =>
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Could not find station, did you mean:\n" + stations.map(_.name).mkString("\n"),
asUser = Some(true)
"Could not find station, did you mean:\n" + stations.map(_.name).mkString("\n")
)
return
case Right(s) => s
......@@ -255,10 +244,9 @@ case object SubscribeDelay extends SlackCommand with StationFinder with Paramete
val directionStation = if (direction.isEmpty) "" else getEitherStationOrStations(direction) match {
case Left(stations) =>
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Could not find direction station, did you mean:\n" + stations.map(_.name).mkString("\n"),
asUser = Some(true)
"Could not find direction station, did you mean:\n" + stations.map(_.name).mkString("\n")
)
return
case Right(s) => s.name
......@@ -290,10 +278,10 @@ case object SubscribeDelay extends SlackCommand with StationFinder with Paramete
s":train2: ${d.trainRegex} :station: $s :clock1: $from - $until :arrow_right: $direction"
}.mkString("\n")
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Success! Your active delay subscriptions: \n" + delayInformation,
asUser = Some(true))
)
}
}
......@@ -309,8 +297,7 @@ case object ListActiveStations extends SlackCommand with StationFinder with Para
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val stations = aws.dynamoDBScanToScala(tablePlannedTimetables.scan())
.map { i =>
......@@ -318,10 +305,9 @@ case object ListActiveStations extends SlackCommand with StationFinder with Para
parse(s.toJSON).extract[Station]
}
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Stations available:\n" + stations.map(_.name).mkString("\n"),
asUser = Some(true)
"Stations available:\n" + stations.map(_.name).mkString("\n")
)
}
}
......@@ -336,8 +322,7 @@ case object ListDelaySubscriptions extends SlackCommand with StationFinder with
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val delayInformation = slackUser.delayRegistration.map { d =>
val item = tableStations.getItem("eva", d.eva.toInt)
......@@ -346,12 +331,12 @@ case object ListDelaySubscriptions extends SlackCommand with StationFinder with
val until = d.until.getOrElse(LocalTime.parse("23:59")).format(timeFormatter)
val direction = d.direction.getOrElse("")
Attachment(
SlackAttachment(
text = Some(s":train2: ${d.trainRegex} \t:station: $station \t:clock1: $from - $until \t:arrow_right: $direction"),
callback_id = Some("unsubscribe_delay_id"),
color = Some("#3AA3E3"),
actions = Some(Seq(
ActionField(
SlackActionField(
"unsubscribe",
"Unsubscribe",
"button",
......@@ -362,10 +347,9 @@ case object ListDelaySubscriptions extends SlackCommand with StationFinder with
)
}.toSeq
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Your active delay subscriptions:",
asUser = Some(true),
attachments = Some(delayInformation)
)
}
......@@ -381,8 +365,7 @@ case object SearchTrain extends SlackCommand with StationFinder with ParameterPa
params: List[String]
)(
implicit slackUser: SlackUser,
slackApiClient: BlockingSlackApiClient,
system: ActorSystem
slackApiClient: SlackClient
): Unit = {
val station = params.head
val searchString = params.tail.mkString(" ")
......@@ -416,20 +399,18 @@ case object SearchTrain extends SlackCommand with StationFinder with ParameterPa
resp match {
case _: RequestFailure =>
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"No trains found",
asUser = Some(true)
"No trains found"
)
case results: RequestSuccess[SearchResponse] =>
val hits = results.result.hits
val trainStrings = hits.hits.map(r => parse(r.sourceAsString).extract[TimetableInformation].toString)
val more = if (hits.total > hits.hits.length) "\n..." else ""
slackApiClient.postChatMessage(
slackApiClient.postMessage(
slackUser.channel,
"Trains found:\n```" + trainStrings.mkString("\n") + "```" + more,
asUser = Some(true)
"Trains found:\n```" + trainStrings.mkString("\n") + "```" + more
)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment