Markup fixes

This commit is contained in:
Pavel Kachalouski
2019-03-05 22:03:01 +01:00
parent 526af6f0e1
commit 08ea1fe449
3 changed files with 52 additions and 29 deletions

View File

@@ -11,4 +11,5 @@ case class BotUri(botId: String) {
val deleteWebhook: Uri = baseUri.withPath(baseUri.path / "deleteWebhook") val deleteWebhook: Uri = baseUri.withPath(baseUri.path / "deleteWebhook")
val getWebhookInfo: Uri = baseUri.withPath(baseUri.path / "getWebhookInfo") val getWebhookInfo: Uri = baseUri.withPath(baseUri.path / "getWebhookInfo")
val sendMessage: Uri = baseUri.withPath(baseUri.path / "sendMessage") val sendMessage: Uri = baseUri.withPath(baseUri.path / "sendMessage")
val editMessageReplyMarkup: Uri = baseUri.withPath(baseUri.path / "editMessageReplyMarkup")
} }

View File

@@ -5,6 +5,8 @@ import akka.actor.typed.scaladsl.{Behaviors, StashBuffer}
import akka.actor.typed.{ActorRef, Behavior, DispatcherSelector, SupervisorStrategy} import akka.actor.typed.{ActorRef, Behavior, DispatcherSelector, SupervisorStrategy}
import akka.http.scaladsl.Http import akka.http.scaladsl.Http
import akka.http.scaladsl.model._ import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Sink, Source}
import akka.util.{ByteString, Timeout} import akka.util.{ByteString, Timeout}
import eu.xeppaka.bot.TelegramEntities._ import eu.xeppaka.bot.TelegramEntities._
import eu.xeppaka.bot.TelegramEntitiesDerivations._ import eu.xeppaka.bot.TelegramEntitiesDerivations._
@@ -50,16 +52,21 @@ object CheckDeliveryDialog {
|/list - list watched parcels |/list - list watched parcels
|/remove - remove parcel from a watching list |/remove - remove parcel from a watching list
""".stripMargin """.stripMargin
private val replyKeyboardRemoveMarkup = Some(ReplyKeyboardRemove()) private val commandsKeyboard = Some(ReplyKeyboardMarkup(
Seq(Seq(KeyboardButton("/add"), KeyboardButton("/list"), KeyboardButton("/remove"))),
resize_keyboard = Some(true),
one_time_keyboard = Some(true)
))
def behavior(chatId: Long, botUri: BotUri): Behavior[Command] = Behaviors.setup[Command] { ctx => def behavior(chatId: Long, botUri: BotUri): Behavior[Command] = Behaviors.setup[Command] { ctx =>
implicit val materializer: ActorMaterializer = ActorMaterializer()(ctx.system.toUntyped)
implicit val executionContext: ExecutionContext = ctx.system.dispatchers.lookup(DispatcherSelector.default()) implicit val executionContext: ExecutionContext = ctx.system.dispatchers.lookup(DispatcherSelector.default())
val http = Http()(ctx.system.toUntyped) val http = Http()(ctx.system.toUntyped)
val stashBuffer = StashBuffer[Command](100) val stashBuffer = StashBuffer[Command](100)
val deliveryStateAdapter: ActorRef[CzechPostDeliveryCheck.DeliveryStateChanged] = ctx.messageAdapter(stateChanged => DeliveryStateChanged(stateChanged.state)) val deliveryStateAdapter: ActorRef[CzechPostDeliveryCheck.DeliveryStateChanged] = ctx.messageAdapter(stateChanged => DeliveryStateChanged(stateChanged.state))
val czechPostDeliveryCheck = ctx.spawnAnonymous(Behaviors.supervise(CzechPostDeliveryCheck.behavior(chatId.toString, deliveryStateAdapter)).onFailure(SupervisorStrategy.restart)) val czechPostDeliveryCheck = ctx.spawnAnonymous(Behaviors.supervise(CzechPostDeliveryCheck.behavior(chatId.toString, deliveryStateAdapter)).onFailure(SupervisorStrategy.restart))
def initial: Behavior[Command] = waitCommand def initial: Behavior[Command] = sendMessage(SendMessage(chatId, "Waiting for a command...", reply_markup = commandsKeyboard), waitCommand, initial)
def waitCommand: Behavior[Command] = Behaviors.receiveMessage { def waitCommand: Behavior[Command] = Behaviors.receiveMessage {
case ProcessMessage(msg, replyTo) => case ProcessMessage(msg, replyTo) =>
@@ -161,7 +168,7 @@ object CheckDeliveryDialog {
case ListParcelsSuccess(parcelsList) => case ListParcelsSuccess(parcelsList) =>
if (parcelsList.nonEmpty) { if (parcelsList.nonEmpty) {
val keyboardButtons = parcelsList.toSeq.sorted.grouped(3).map(_.map(id => KeyboardButton(id))).toSeq val keyboardButtons = parcelsList.toSeq.sorted.grouped(3).map(_.map(id => KeyboardButton(id))).toSeq
val markup = ReplyKeyboardMarkup(keyboard = keyboardButtons, resize_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, waitParcelId(parcelId => removeParcelId(parcelId)), onFailure) sendMessage(message, waitParcelId(parcelId => removeParcelId(parcelId)), onFailure)
} else { } else {
@@ -191,16 +198,16 @@ object CheckDeliveryDialog {
Behaviors.receiveMessage { Behaviors.receiveMessage {
case RemoveParcelSuccess => case RemoveParcelSuccess =>
val message = SendMessage(chatId, s"Parcel $parcelId was removed from the watch list.", reply_markup = replyKeyboardRemoveMarkup) val message = SendMessage(chatId, s"Parcel $parcelId was removed from the watch list.")
sendMessage(message, initial, initial) sendMessage(message, initial, initial)
case RemoveParcelFailure(exception) => case RemoveParcelFailure(exception) =>
exception match { exception match {
case CzechPostDeliveryCheck.ParcelIdNotFound(_) => case CzechPostDeliveryCheck.ParcelIdNotFound(_) =>
val message = SendMessage(chatId, s"Parcel $parcelId is not found in the list of the watched parcels.", reply_markup = replyKeyboardRemoveMarkup) val message = SendMessage(chatId, s"Parcel $parcelId is not found in the list of the watched parcels.")
sendMessage(message, initial, initial) sendMessage(message, initial, initial)
case _ => case _ =>
ctx.log.error(exception, "action=add_parcel result=failure") ctx.log.error(exception, "action=add_parcel result=failure")
val message = SendMessage(chatId, s"Remove of the parcel failed. Please try again.", reply_markup = replyKeyboardRemoveMarkup) val message = SendMessage(chatId, s"Remove of the parcel failed. Please try again.")
sendMessage(message, initial, initial) sendMessage(message, initial, initial)
} }
case otherMessage => case otherMessage =>
@@ -209,15 +216,15 @@ object CheckDeliveryDialog {
} }
} }
// def selectPostType(onFinish: PostType => Behavior[Command]): Behavior[Command] = Behaviors.receiveMessage { // def selectPostType(onFinish: PostType => Behavior[Command]): Behavior[Command] = Behaviors.receiveMessage {
// //
// case ProcessMessage(msg, replyTo) => // case ProcessMessage(msg, replyTo) =>
// val button1 = KeyboardButton("button1") // val button1 = KeyboardButton("button1")
// val button2 = KeyboardButton("button2") // val button2 = KeyboardButton("button2")
// val keyboard = ReplyKeyboardMarkup(Seq(Seq(button1, button2))) // val keyboard = ReplyKeyboardMarkup(Seq(Seq(button1, button2)))
// val message = SendMessage(chatId, "Please enter parcel ID.", reply_markup = Some(keyboard)) // val message = SendMessage(chatId, "Please enter parcel ID.", reply_markup = Some(keyboard))
// sendMessage(message, waitParcelId(parcelId => addParcel(parcelId)), initial) // sendMessage(message, waitParcelId(parcelId => addParcel(parcelId)), initial)
// } // }
def waitParcelId(onFinish: String => Behavior[Command]): Behavior[Command] = Behaviors.receiveMessage { def waitParcelId(onFinish: String => Behavior[Command]): Behavior[Command] = Behaviors.receiveMessage {
case ProcessMessage(msg, replyTo) => case ProcessMessage(msg, replyTo) =>
@@ -246,27 +253,36 @@ object CheckDeliveryDialog {
ctx.log.debug("action=send_message status=started chat_id={} message={}", chatId, json) ctx.log.debug("action=send_message status=started chat_id={} message={}", chatId, json)
http Source
.singleRequest(request) .single(request)
.onComplete { .initialDelay(2.seconds * attempt)
case Success(response) => if (response.status.isSuccess()) { .mapAsync(1) { request =>
ctx.log.debug("action=send_message status=finished result=success chat_id={}", chatId) http
ctx.self ! SendMessageSuccess .singleRequest(request)
} else { .transform {
ctx.log.error("action=send_message status=finished result=failure chat_id={} http_code={}", chatId, response.status.value) case Success(response) =>
ctx.self ! SendMessageFailure(new RuntimeException(s"Error while sending message. HTTP status: ${response.status}.")) if (response.status.isSuccess()) {
} Success(SendMessageSuccess)
case Failure(exception) => } else {
ctx.log.error(exception, "action=send_message status=finished result=failure chat_id={}", chatId) Success(SendMessageFailure(new RuntimeException(s"Error while sending message. HTTP status: ${response.status}.")))
ctx.self ! SendMessageFailure(exception) }
case Failure(exception) =>
ctx.log.error(exception, "action=send_message status=finished result=failure chat_id={}", chatId)
Success(SendMessageFailure(exception))
}
} }
.to(Sink.foreach(ctx.self ! _))
.run()
Behaviors.receiveMessage { Behaviors.receiveMessage {
case SendMessageSuccess => case SendMessageSuccess =>
ctx.log.debug("action=send_message status=finished result=success chat_id={}", chatId)
stashBuffer.unstashAll(ctx, onSuccess) stashBuffer.unstashAll(ctx, onSuccess)
case SendMessageFailure(exception) => case SendMessageFailure(exception) =>
ctx.log.error(exception, "action=send_message status=finished result=failure chat_id={} attempt={}", chatId, attempt)
if (attempt >= 5) { if (attempt >= 5) {
ctx.log.error(exception, "action=send_message result=failure") ctx.log.error(exception, "action=send_message result=failure message=attempts threshold exceeded")
stashBuffer.unstashAll(ctx, onFailure) stashBuffer.unstashAll(ctx, onFailure)
} else { } else {
sendMessage(message, onSuccess, onFailure, attempt + 1) sendMessage(message, onSuccess, onFailure, attempt + 1)

View File

@@ -115,6 +115,12 @@ object TelegramEntities {
language_code: Option[String] = None language_code: Option[String] = None
) )
case class EditMessageReplyMarkup(chat_id: Option[Long],
message_id: Option[Int],
inline_message_id: Option[String],
reply_markup: Option[InlineKeyboardMarkup]
)
case class SendMessage(chat_id: Long, case class SendMessage(chat_id: Long,
text: String, text: String,
parse_mode: Option[String] = None, parse_mode: Option[String] = None,