This commit is contained in:
@@ -18,6 +18,7 @@ lazy val `telegram-bot-delivery` = (project in file("."))
|
|||||||
akkaHttp,
|
akkaHttp,
|
||||||
akkaStream,
|
akkaStream,
|
||||||
akkaPersistence,
|
akkaPersistence,
|
||||||
|
akkaPersistenceCassandra,
|
||||||
levelDbJni,
|
levelDbJni,
|
||||||
circleCore,
|
circleCore,
|
||||||
circleGeneric,
|
circleGeneric,
|
||||||
@@ -31,6 +32,7 @@ lazy val `telegram-bot-delivery` = (project in file("."))
|
|||||||
Docker / daemonUserUid := Some("1001"),
|
Docker / daemonUserUid := Some("1001"),
|
||||||
Docker / daemonUser := "telegram-bot",
|
Docker / daemonUser := "telegram-bot",
|
||||||
Docker / defaultLinuxInstallLocation := "/opt/telegram-bot-delivery",
|
Docker / defaultLinuxInstallLocation := "/opt/telegram-bot-delivery",
|
||||||
|
version := "1.0.1"
|
||||||
)
|
)
|
||||||
.enablePlugins(JavaServerAppPackaging)
|
.enablePlugins(JavaServerAppPackaging)
|
||||||
.enablePlugins(DockerPlugin)
|
.enablePlugins(DockerPlugin)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ object Dependencies {
|
|||||||
object Versions {
|
object Versions {
|
||||||
val akkaVersion = "2.5.26"
|
val akkaVersion = "2.5.26"
|
||||||
val akkaHttpVersion = "10.1.10"
|
val akkaHttpVersion = "10.1.10"
|
||||||
|
val akkaPersistenceCassandraVersion = "0.100"
|
||||||
val levelDbJniVersion = "1.8"
|
val levelDbJniVersion = "1.8"
|
||||||
val circeVersion = "0.12.3"
|
val circeVersion = "0.12.3"
|
||||||
val akkaHttpCirceVersion = "1.29.1"
|
val akkaHttpCirceVersion = "1.29.1"
|
||||||
@@ -18,6 +19,7 @@ object Dependencies {
|
|||||||
val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaVersion
|
val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaVersion
|
||||||
val akkaHttp = "com.typesafe.akka" %% "akka-http" % akkaHttpVersion
|
val akkaHttp = "com.typesafe.akka" %% "akka-http" % akkaHttpVersion
|
||||||
val akkaPersistence = "com.typesafe.akka" %% "akka-persistence-typed" % akkaVersion
|
val akkaPersistence = "com.typesafe.akka" %% "akka-persistence-typed" % akkaVersion
|
||||||
|
val akkaPersistenceCassandra = "com.typesafe.akka" %% "akka-persistence-cassandra" % "0.100"
|
||||||
val levelDbJni = "org.fusesource.leveldbjni" % "leveldbjni-all" % levelDbJniVersion
|
val levelDbJni = "org.fusesource.leveldbjni" % "leveldbjni-all" % levelDbJniVersion
|
||||||
val circleCore = "io.circe" %% "circe-core" % circeVersion
|
val circleCore = "io.circe" %% "circe-core" % circeVersion
|
||||||
val circleGeneric = "io.circe" %% "circe-generic" % circeVersion
|
val circleGeneric = "io.circe" %% "circe-generic" % circeVersion
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
sbt.version=1.2.8
|
sbt.version=1.3.3
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ akka {
|
|||||||
|
|
||||||
persistence {
|
persistence {
|
||||||
journal {
|
journal {
|
||||||
plugin = "akka.persistence.journal.leveldb"
|
plugin = "cassandra-journal"
|
||||||
auto-start-journals = ["akka.persistence.journal.leveldb"]
|
|
||||||
leveldb.dir = "journal-check-delivery"
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot-store {
|
|
||||||
plugin = "akka.persistence.snapshot-store.local"
|
|
||||||
auto-start-snapshot-stores = ["akka.persistence.snapshot-store.local"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cassandra-journal {
|
||||||
|
contact-points = ["cassandra"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ object CheckDeliveryDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def listParcels: Behavior[Command] = Behaviors.setup { ctx =>
|
def listParcels: Behavior[Command] = Behaviors.setup { ctx =>
|
||||||
case class ListParcelsSuccess(parcelsList: Set[String]) extends Command
|
case class ListParcelsSuccess(parcelsList: Seq[String]) extends Command
|
||||||
case class ListParcelsFailure(exception: Throwable) extends Command
|
case class ListParcelsFailure(exception: Throwable) extends Command
|
||||||
implicit val timeout: Timeout = 5.seconds
|
implicit val timeout: Timeout = 5.seconds
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ object CheckDeliveryDialog {
|
|||||||
|
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case ListParcelsSuccess(parcelsList) =>
|
case ListParcelsSuccess(parcelsList) =>
|
||||||
val messageText = "*List of your watched parcels:*\n" + (if (parcelsList.nonEmpty) parcelsList.toSeq.sorted.mkString("\n") else "(empty)")
|
val messageText = "*List of your watched parcels:*\n" + (if (parcelsList.nonEmpty) parcelsList.sorted.mkString("\n") else "(empty)")
|
||||||
val message = SendMessage(chatId, messageText, Some("Markdown"), reply_markup = commandsKeyboard)
|
val message = SendMessage(chatId, messageText, Some("Markdown"), reply_markup = commandsKeyboard)
|
||||||
sendMessage(message, waitCommand, waitCommand)
|
sendMessage(message, waitCommand, waitCommand)
|
||||||
case ListParcelsFailure(exception) =>
|
case ListParcelsFailure(exception) =>
|
||||||
@@ -154,19 +154,19 @@ object CheckDeliveryDialog {
|
|||||||
|
|
||||||
def removeParcel(onSuccess: => Behavior[Command], onFailure: => Behavior[Command]): Behavior[Command] =
|
def removeParcel(onSuccess: => Behavior[Command], onFailure: => Behavior[Command]): Behavior[Command] =
|
||||||
Behaviors.setup { ctx =>
|
Behaviors.setup { ctx =>
|
||||||
case class ListParcelsSuccess(parcelsList: Set[String]) extends Command
|
case class ListParcelIdsSuccess(parcelsList: Seq[String]) extends Command
|
||||||
case class ListParcelsFailure(exception: Throwable) extends Command
|
case class ListParcelIdsFailure(exception: Throwable) extends Command
|
||||||
implicit val timeout: Timeout = 5.seconds
|
implicit val timeout: Timeout = 5.seconds
|
||||||
|
|
||||||
ctx.ask[CzechPostDeliveryCheck.Command, CzechPostDeliveryCheck.ListParcelsResult](czechPostDeliveryCheck)(ref => CzechPostDeliveryCheck.ListParcels(ref)) {
|
ctx.ask[CzechPostDeliveryCheck.Command, CzechPostDeliveryCheck.ListParcelIdsResult](czechPostDeliveryCheck)(ref => CzechPostDeliveryCheck.ListParcelIds(ref)) {
|
||||||
case Success(CzechPostDeliveryCheck.ListParcelsResult(parcelsList)) => ListParcelsSuccess(parcelsList)
|
case Success(CzechPostDeliveryCheck.ListParcelIdsResult(parcelsList)) => ListParcelIdsSuccess(parcelsList)
|
||||||
case Failure(exception) => ListParcelsFailure(exception)
|
case Failure(exception) => ListParcelIdsFailure(exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case ListParcelsSuccess(parcelsList) =>
|
case ListParcelIdsSuccess(parcelsList) =>
|
||||||
if (parcelsList.nonEmpty) {
|
if (parcelsList.nonEmpty) {
|
||||||
val keyboardButtons = parcelsList.toSeq.sorted.grouped(3).map(_.map(id => KeyboardButton(id))).toSeq
|
val keyboardButtons = parcelsList.sorted.grouped(3).map(_.map(id => KeyboardButton(id))).toSeq
|
||||||
val markup = ReplyKeyboardMarkup(keyboard = keyboardButtons, resize_keyboard = Some(true), one_time_keyboard = Some(true))
|
val markup = ReplyKeyboardMarkup(keyboard = keyboardButtons, resize_keyboard = Some(true), one_time_keyboard = Some(true))
|
||||||
val message = SendMessage(chatId, "Please enter a parcel id to remove.", reply_markup = Some(markup))
|
val message = SendMessage(chatId, "Please enter a parcel id to remove.", reply_markup = Some(markup))
|
||||||
sendMessage(message, waitTextMessage(parcelId => removeParcelId(parcelId)), onFailure)
|
sendMessage(message, waitTextMessage(parcelId => removeParcelId(parcelId)), onFailure)
|
||||||
@@ -174,7 +174,7 @@ object CheckDeliveryDialog {
|
|||||||
val message = SendMessage(chatId, "You don't have watched parcels. There is nothing to remove.", reply_markup = commandsKeyboard)
|
val message = SendMessage(chatId, "You don't have watched parcels. There is nothing to remove.", reply_markup = commandsKeyboard)
|
||||||
sendMessage(message, onSuccess, onFailure)
|
sendMessage(message, onSuccess, onFailure)
|
||||||
}
|
}
|
||||||
case ListParcelsFailure(exception) =>
|
case ListParcelIdsFailure(exception) =>
|
||||||
ctx.log.error(exception, "action=list_parcels result=failure chat_id={}", chatId)
|
ctx.log.error(exception, "action=list_parcels result=failure chat_id={}", chatId)
|
||||||
val message = SendMessage(chatId, "Failed to get a list of your watched parcels. Please try again later.", reply_markup = commandsKeyboard)
|
val message = SendMessage(chatId, "Failed to get a list of your watched parcels. Please try again later.", reply_markup = commandsKeyboard)
|
||||||
sendMessage(message, waitCommand, waitCommand)
|
sendMessage(message, waitCommand, waitCommand)
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ object CzechPostDeliveryCheck {
|
|||||||
sealed trait Command
|
sealed trait Command
|
||||||
sealed trait CommandResult
|
sealed trait CommandResult
|
||||||
sealed trait Event
|
sealed trait Event
|
||||||
case class ParcelState(comment: String, attributes: Option[Entities.Attributes] = None, states: Set[Entities.State] = Set.empty) {
|
case class Parcel(comment: String, attributes: Option[Entities.Attributes] = None, states: Set[Entities.State] = Set.empty) {
|
||||||
def prettyPrint(parcelId: String): String = {
|
def fullStatePrint(parcelId: String): String = {
|
||||||
val statesString = states
|
val statesString = states
|
||||||
.toSeq
|
.toSeq
|
||||||
.sortBy(state => czechPostDateFormat.parse(state.date))
|
.sortBy(state => czechPostDateFormat.parse(state.date))
|
||||||
@@ -72,13 +72,27 @@ object CzechPostDeliveryCheck {
|
|||||||
|===========================
|
|===========================
|
||||||
|$statesString""".stripMargin
|
|$statesString""".stripMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def latestStatePrint(parcelId: String): String = {
|
||||||
|
val state = latestState
|
||||||
|
s"$parcelId ($comment) - ${printDateFormat.format(czechPostDateFormat.parse(state.date))} - ${state.text}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private def latestState: Entities.State = states.toSeq.maxBy(state => czechPostDateFormat.parse(state.date))
|
||||||
|
}
|
||||||
|
|
||||||
|
case class State(parcelStates: Map[String, Parcel] = Map.empty) {
|
||||||
|
def latestStatesPrint: Seq[String] = parcelStates
|
||||||
|
.map { case (id, parcel) => parcel.latestStatePrint(id) }
|
||||||
|
.to(Seq)
|
||||||
}
|
}
|
||||||
case class State(parcelStates: Map[String, ParcelState] = Map.empty)
|
|
||||||
|
|
||||||
case class AddParcel(parcelId: String, comment: String, replyTo: ActorRef[CommandResult]) extends Command
|
case class AddParcel(parcelId: String, comment: String, replyTo: ActorRef[CommandResult]) extends Command
|
||||||
case class RemoveParcel(parcelId: String, replyTo: ActorRef[CommandResult]) extends Command
|
case class RemoveParcel(parcelId: String, replyTo: ActorRef[CommandResult]) extends Command
|
||||||
case class ListParcels(replyTo: ActorRef[ListParcelsResult]) extends Command
|
case class ListParcels(replyTo: ActorRef[ListParcelsResult]) extends Command
|
||||||
case class ListParcelsResult(parcelsList: Set[String])
|
case class ListParcelsResult(parcelsList: Seq[String])
|
||||||
|
case class ListParcelIds(replyTo: ActorRef[ListParcelIdsResult]) extends Command
|
||||||
|
case class ListParcelIdsResult(parcelIds: Seq[String])
|
||||||
|
|
||||||
case object CommandResultSuccess extends CommandResult
|
case object CommandResultSuccess extends CommandResult
|
||||||
case class CommandResultFailure(exception: Throwable) extends CommandResult
|
case class CommandResultFailure(exception: Throwable) extends CommandResult
|
||||||
@@ -166,10 +180,16 @@ object CzechPostDeliveryCheck {
|
|||||||
case ListParcels(replyTo) =>
|
case ListParcels(replyTo) =>
|
||||||
Effect.none
|
Effect.none
|
||||||
.thenRun { state =>
|
.thenRun { state =>
|
||||||
val parcelsList = state.parcelStates.keySet
|
val parcelsList = state.latestStatesPrint
|
||||||
replyTo ! ListParcelsResult(parcelsList)
|
replyTo ! ListParcelsResult(parcelsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ListParcelIds(replyTo) =>
|
||||||
|
Effect.none
|
||||||
|
.thenRun { state =>
|
||||||
|
replyTo ! ListParcelIdsResult(state.parcelStates.keys.toSeq)
|
||||||
|
}
|
||||||
|
|
||||||
case CheckParcels =>
|
case CheckParcels =>
|
||||||
Effect
|
Effect
|
||||||
.none
|
.none
|
||||||
@@ -223,7 +243,7 @@ object CzechPostDeliveryCheck {
|
|||||||
.persist(attributesChangedEvents ++ stateEvents)
|
.persist(attributesChangedEvents ++ stateEvents)
|
||||||
.thenRun(_ => {
|
.thenRun(_ => {
|
||||||
if (newStates.nonEmpty) {
|
if (newStates.nonEmpty) {
|
||||||
stateReporter ! DeliveryStateChanged(ParcelState(comment, None, newStates).prettyPrint(parcelId))
|
stateReporter ! DeliveryStateChanged(Parcel(comment, None, newStates).fullStatePrint(parcelId))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -232,7 +252,7 @@ object CzechPostDeliveryCheck {
|
|||||||
val eventHandler: EventHandler[State, Event] = (state, evt) => {
|
val eventHandler: EventHandler[State, Event] = (state, evt) => {
|
||||||
evt match {
|
evt match {
|
||||||
case ParcelAdded(parcelId, comment) =>
|
case ParcelAdded(parcelId, comment) =>
|
||||||
state.copy(parcelStates = state.parcelStates + (parcelId -> ParcelState(comment)))
|
state.copy(parcelStates = state.parcelStates + (parcelId -> Parcel(comment)))
|
||||||
case ParcelRemoved(parcelId) => state.copy(parcelStates = state.parcelStates - parcelId)
|
case ParcelRemoved(parcelId) => state.copy(parcelStates = state.parcelStates - parcelId)
|
||||||
case ParcelHistoryStateAdded(parcelId, newState) =>
|
case ParcelHistoryStateAdded(parcelId, newState) =>
|
||||||
val parcelState = state.parcelStates(parcelId)
|
val parcelState = state.parcelStates(parcelId)
|
||||||
|
|||||||
Reference in New Issue
Block a user