mirror of
https://github.com/dyrkin/luxmed-bot.git
synced 2026-01-05 12:25:30 +01:00
Implemented custom fsm to make the code more readable
This commit is contained in:
@@ -27,78 +27,55 @@ import akka.actor.{ActorRef, Props}
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Account._
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login._
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.service.DataService
|
||||
|
||||
class Account(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization, router: ActorRef) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||
class Account(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization, router: ActorRef) extends Conversation[Unit] with Localizable {
|
||||
|
||||
startWith(AskAction, null)
|
||||
|
||||
whenSafe(AskAction) {
|
||||
case Event(Next, _) =>
|
||||
def askAction: QA =
|
||||
question { _ =>
|
||||
val credentials = dataService.getUserCredentials(userId.userId)
|
||||
val currentAccount = credentials.find(c => c.accountId == userId.accountId).getOrElse(sys.error("Can't determine current account"))
|
||||
val buttons = Seq(Button(lang.addAccount, -1L), Button(lang.deleteAccount, -2L)) ++ credentials.map(c => Button(s"🔐️ ${c.username}", c.accountId))
|
||||
bot.sendMessage(userId.source, lang.pleaseChooseAccount(currentAccount.username), inlineKeyboard = createInlineKeyboard(buttons, columns = 1))
|
||||
goto(AwaitAction)
|
||||
}
|
||||
|
||||
whenSafe(AwaitAction) {
|
||||
case Event(cmd@Command(_, _, Some(actionStr)), _) =>
|
||||
val action = actionStr.toLong
|
||||
action match {
|
||||
case -1L =>
|
||||
router ! cmd.copy(message = cmd.message.copy(text = Some("/login")))
|
||||
goto(AskAction) using null
|
||||
case -2L =>
|
||||
bot.sendMessage(userId.source, "Not implemented yet")
|
||||
goto(AskAction) using null
|
||||
case accountId =>
|
||||
val accountMaybe = dataService.findUserCredentialsByAccountId(userId.userId, accountId)
|
||||
accountMaybe match {
|
||||
case Some(account) => //account was found
|
||||
val userMaybe = dataService.findUser(userId.userId)
|
||||
userMaybe.foreach { user =>
|
||||
user.activeAccountId = accountId
|
||||
dataService.saveUser(user)
|
||||
router ! SwitchUser(UserId(account.userId, account.accountId, userId.source))
|
||||
bot.sendMessage(userId.source, lang.accountSwitched(account.username))
|
||||
}
|
||||
goto(AskAction) using null
|
||||
case None =>
|
||||
error(s"This is not user [#${userId.userId}] account [#$accountId]")
|
||||
goto(AskAction) using null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
goto(AskAction) using null
|
||||
case e: Event =>
|
||||
error(s"Unhandled event in state:$stateName. Event: $e")
|
||||
stay()
|
||||
}
|
||||
|
||||
initialize()
|
||||
} answer {
|
||||
case Msg(cmd@Command(_, _, Some(actionStr)), _) =>
|
||||
val action = actionStr.toLong
|
||||
action match {
|
||||
case -1L =>
|
||||
router ! cmd.copy(message = cmd.message.copy(text = Some("/login")))
|
||||
stay()
|
||||
case -2L =>
|
||||
bot.sendMessage(userId.source, "Not implemented yet")
|
||||
stay()
|
||||
case accountId =>
|
||||
val accountMaybe = dataService.findUserCredentialsByAccountId(userId.userId, accountId)
|
||||
accountMaybe match {
|
||||
case Some(account) => //account was found
|
||||
val userMaybe = dataService.findUser(userId.userId)
|
||||
userMaybe.foreach { user =>
|
||||
user.activeAccountId = accountId
|
||||
dataService.saveUser(user)
|
||||
router ! SwitchUser(UserId(account.userId, account.accountId, userId.source))
|
||||
bot.sendMessage(userId.source, lang.accountSwitched(account.username))
|
||||
}
|
||||
stay()
|
||||
case None =>
|
||||
error(s"This is not user [#${userId.userId}] account [#$accountId]")
|
||||
stay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entryPoint(askAction)
|
||||
}
|
||||
|
||||
object Account {
|
||||
def props(userId: UserId, bot: Bot, dataService: DataService, localization: Localization, router: ActorRef): Props =
|
||||
Props(new Account(userId, bot, dataService, localization, router))
|
||||
|
||||
object AskAction extends FSMState
|
||||
|
||||
object AwaitAction extends FSMState
|
||||
|
||||
object AddAccount extends FSMState
|
||||
|
||||
object RemoveAccount extends FSMState
|
||||
|
||||
case class SwitchUser(userId: UserId)
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.DatePicker.{DateFromMode, DateToMode}
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.StaticData.StaticDataConfig
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.repository.model.Monitoring
|
||||
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
|
||||
@@ -81,6 +82,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
|
||||
|
||||
whenSafe(RequestDateFrom) {
|
||||
case Event(_, bookingData: BookingData) =>
|
||||
datePicker ! StartConversation
|
||||
datePicker ! DateFromMode
|
||||
datePicker ! bookingData.dateFrom
|
||||
goto(AwaitDateFrom)
|
||||
@@ -257,8 +259,8 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
|
||||
|
||||
private def reinit() = {
|
||||
invokeNext()
|
||||
datePicker ! Init
|
||||
staticData ! Init
|
||||
datePicker ! InitConversation
|
||||
staticData ! InitConversation
|
||||
termsPager ! Init
|
||||
goto(RequestCity) using BookingData()
|
||||
}
|
||||
|
||||
@@ -23,95 +23,70 @@
|
||||
*/
|
||||
package com.lbs.server.actor
|
||||
|
||||
import akka.actor.{PoisonPill, Props}
|
||||
import akka.actor.Props
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Bug._
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.repository.model
|
||||
import com.lbs.server.service.DataService
|
||||
import com.lbs.server.util.MessageExtractors
|
||||
|
||||
class Bug(val userId: UserId, bot: Bot, dataService: DataService, bugPagerActorFactory: ByUserIdWithOriginatorActorFactory,
|
||||
val localization: Localization) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||
val localization: Localization) extends Conversation[Unit] with Localizable {
|
||||
|
||||
private val bugPager = bugPagerActorFactory(userId, self)
|
||||
|
||||
startWith(RequestAction, null)
|
||||
|
||||
whenSafe(RequestAction) {
|
||||
case Event(Next, _) =>
|
||||
def askAction: Step =
|
||||
question { _ =>
|
||||
bot.sendMessage(userId.source, lang.bugAction, inlineKeyboard =
|
||||
createInlineKeyboard(Seq(Button(lang.createNewBug, Tags.SubmitNew), Button(lang.showSubmittedBugs, Tags.ListSubmitted))))
|
||||
goto(AwaitAction)
|
||||
}
|
||||
} answer {
|
||||
case Msg(Command(_, _, Some(Tags.SubmitNew)), _) =>
|
||||
goto(askBugDescription)
|
||||
case Msg(Command(_, _, Some(Tags.ListSubmitted)), _) =>
|
||||
goto(displaySubmittedBugs)
|
||||
}
|
||||
|
||||
whenSafe(AwaitAction) {
|
||||
case Event(Command(_, _, Some(Tags.SubmitNew)), _) =>
|
||||
bot.sendMessage(userId.source, lang.enterIssueDetails)
|
||||
goto(AwaitBugDescription)
|
||||
case Event(Command(_, _, Some(Tags.ListSubmitted)), _) =>
|
||||
invokeNext()
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
whenSafe(RequestData) {
|
||||
case Event(Next, _) =>
|
||||
def displaySubmittedBugs: IC =
|
||||
internalConfig { _ =>
|
||||
val bugs = dataService.getBugs(userId.userId)
|
||||
bugPager ! Init
|
||||
bugPager ! InitConversation
|
||||
bugPager ! StartConversation
|
||||
bugPager ! Right[Throwable, Seq[model.Bug]](bugs)
|
||||
goto(AwaitPage)
|
||||
}
|
||||
goto(processResponseFromPager)
|
||||
}
|
||||
|
||||
whenSafe(AwaitPage) {
|
||||
case Event(cmd: Command, _) =>
|
||||
bugPager ! cmd
|
||||
stay()
|
||||
case Event(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noSubmittedIssuesFound)
|
||||
goto(RequestData)
|
||||
}
|
||||
def processResponseFromPager: M =
|
||||
monologue {
|
||||
case Msg(cmd: Command, _) =>
|
||||
bugPager ! cmd
|
||||
stay()
|
||||
case Msg(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noSubmittedIssuesFound)
|
||||
end()
|
||||
}
|
||||
|
||||
whenSafe(AwaitBugDescription) {
|
||||
case Event(Command(_, MessageExtractors.Text(details), _), _) =>
|
||||
val bugId = dataService.submitBug(userId.userId, userId.source.sourceSystem.id, details)
|
||||
bot.sendMessage(userId.source, lang.bugHasBeenCreated(bugId.getOrElse(-1L)))
|
||||
goto(RequestAction) using null
|
||||
}
|
||||
def askBugDescription: Step =
|
||||
question { _ =>
|
||||
bot.sendMessage(userId.source, lang.enterIssueDetails)
|
||||
} answer {
|
||||
case Msg(Command(_, MessageExtractors.Text(details), _), _) =>
|
||||
val bugId = dataService.submitBug(userId.userId, userId.source.sourceSystem.id, details)
|
||||
bot.sendMessage(userId.source, lang.bugHasBeenCreated(bugId.getOrElse(-1L)))
|
||||
end()
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
bugPager ! Init
|
||||
goto(RequestAction)
|
||||
}
|
||||
|
||||
initialize()
|
||||
|
||||
override def postStop(): Unit = {
|
||||
bugPager ! PoisonPill
|
||||
super.postStop()
|
||||
}
|
||||
entryPoint(askAction)
|
||||
}
|
||||
|
||||
object Bug {
|
||||
def props(userId: UserId, bot: Bot, dataService: DataService, bugPagerActorFactory: ByUserIdWithOriginatorActorFactory, localization: Localization): Props =
|
||||
Props(new Bug(userId, bot, dataService, bugPagerActorFactory, localization))
|
||||
|
||||
object RequestBugDetails extends FSMState
|
||||
|
||||
object AwaitBugDescription extends FSMState
|
||||
|
||||
object RequestAction extends FSMState
|
||||
|
||||
object AwaitAction extends FSMState
|
||||
|
||||
object RequestData extends FSMState
|
||||
|
||||
object AwaitPage extends FSMState
|
||||
|
||||
object Tags {
|
||||
val SubmitNew = "submit"
|
||||
val ListSubmitted = "list"
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.lbs.bot.model.Command
|
||||
import com.lbs.common.Logger
|
||||
import com.lbs.server.actor.Chat._
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.service.{DataService, MonitoringService}
|
||||
import com.lbs.server.util.MessageExtractors._
|
||||
|
||||
@@ -66,7 +67,8 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
|
||||
|
||||
when(HistoryChat, historyActor) {
|
||||
case Event(Command(_, Text("/history"), _), _) =>
|
||||
historyActor ! Init
|
||||
historyActor ! InitConversation
|
||||
historyActor ! StartConversation
|
||||
stay()
|
||||
}
|
||||
|
||||
@@ -78,25 +80,29 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
|
||||
|
||||
when(BugChat, bugActor) {
|
||||
case Event(Command(_, Text("/bug"), _), _) =>
|
||||
bugActor ! Init
|
||||
bugActor ! InitConversation
|
||||
bugActor ! StartConversation
|
||||
goto(BugChat)
|
||||
}
|
||||
|
||||
when(MonitoringsChat, monitoringsActor) {
|
||||
case Event(Command(_, Text("/monitorings"), _), _) =>
|
||||
monitoringsActor ! Init
|
||||
monitoringsActor ! InitConversation
|
||||
monitoringsActor ! StartConversation
|
||||
stay()
|
||||
}
|
||||
|
||||
when(SettingsChat, settingsActor) {
|
||||
case Event(Command(_, Text("/settings"), _), _) =>
|
||||
settingsActor ! Init
|
||||
settingsActor ! InitConversation
|
||||
settingsActor ! StartConversation
|
||||
stay()
|
||||
}
|
||||
|
||||
when(AccountChat, accountActor) {
|
||||
case Event(Command(_, Text("/accounts"), _), _) =>
|
||||
accountActor ! Init
|
||||
accountActor ! InitConversation
|
||||
accountActor ! StartConversation
|
||||
stay()
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ import java.time.{LocalTime, ZonedDateTime}
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.DatePicker._
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
|
||||
/**
|
||||
@@ -42,59 +42,48 @@ import com.lbs.server.lang.{Localizable, Localization}
|
||||
* ⬇ ⬇ ⬇
|
||||
*
|
||||
*/
|
||||
class DatePicker(val userId: UserId, val bot: Bot, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, ZonedDateTime] with Localizable {
|
||||
|
||||
startWith(AwaitMode, null)
|
||||
class DatePicker(val userId: UserId, val bot: Bot, val localization: Localization, originator: ActorRef) extends Conversation[ZonedDateTime] with Localizable {
|
||||
|
||||
private var mode: Mode = DateFromMode
|
||||
|
||||
whenSafe(AwaitMode) {
|
||||
case Event(newMode: Mode, _) =>
|
||||
mode = newMode
|
||||
goto(RequestDate)
|
||||
}
|
||||
entryPoint(configure)
|
||||
|
||||
whenSafe(RequestDate) {
|
||||
case Event(initialDate: ZonedDateTime, _) =>
|
||||
def configure: EC =
|
||||
externalConfig {
|
||||
case Msg(newMode: Mode, _) =>
|
||||
mode = newMode
|
||||
stay()
|
||||
case Msg(initialDate: ZonedDateTime, _) =>
|
||||
goto(requestDate) using initialDate
|
||||
}
|
||||
|
||||
def requestDate: QA =
|
||||
question { initialDate =>
|
||||
val message = mode match {
|
||||
case DateFromMode => lang.chooseDateFrom
|
||||
case DateToMode => lang.chooseDateTo
|
||||
}
|
||||
bot.sendMessage(userId.source, message, inlineKeyboard = dateButtons(initialDate))
|
||||
goto(AwaitDate) using initialDate
|
||||
}
|
||||
} answer {
|
||||
case Msg(Command(_, msg, Some(Tags.Done)), finalDate) =>
|
||||
val (message, updatedDate) = mode match {
|
||||
case DateFromMode =>
|
||||
val startOfTheDay = finalDate.`with`(LocalTime.MIN)
|
||||
val dateFrom = if (startOfTheDay.isBefore(ZonedDateTime.now())) finalDate else startOfTheDay
|
||||
lang.dateFromIs(dateFrom) -> dateFrom
|
||||
case DateToMode =>
|
||||
val dateTo = finalDate.`with`(LocalTime.MAX).minusHours(2)
|
||||
lang.dateToIs(dateTo) -> dateTo
|
||||
}
|
||||
bot.sendEditMessage(userId.source, msg.messageId, message)
|
||||
originator ! updatedDate
|
||||
goto(configure) using null
|
||||
|
||||
whenSafe(AwaitDate) {
|
||||
case Event(Command(_, msg, Some(Tags.Done)), finalDate: ZonedDateTime) =>
|
||||
|
||||
val (message, updatedDate) = mode match {
|
||||
case DateFromMode =>
|
||||
val startOfTheDay = finalDate.`with`(LocalTime.MIN)
|
||||
val dateFrom = if (startOfTheDay.isBefore(ZonedDateTime.now())) finalDate else startOfTheDay
|
||||
lang.dateFromIs(dateFrom) -> dateFrom
|
||||
case DateToMode =>
|
||||
val dateTo = finalDate.`with`(LocalTime.MAX).minusHours(2)
|
||||
lang.dateToIs(dateTo) -> dateTo
|
||||
}
|
||||
bot.sendEditMessage(userId.source, msg.messageId, message)
|
||||
originator ! updatedDate
|
||||
goto(AwaitMode) using null
|
||||
|
||||
case Event(Command(_, msg, Some(tag)), date: ZonedDateTime) =>
|
||||
val modifiedDate = modifyDate(date, tag)
|
||||
bot.sendEditMessage(userId.source, msg.messageId, inlineKeyboard = dateButtons(modifiedDate))
|
||||
stay() using modifiedDate
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
goto(AwaitMode) using null
|
||||
case e: Event =>
|
||||
error(s"Unhandled event in state:$stateName. Event: $e")
|
||||
stay()
|
||||
}
|
||||
|
||||
initialize()
|
||||
case Msg(Command(_, msg, Some(tag)), date) =>
|
||||
val modifiedDate = modifyDate(date, tag)
|
||||
bot.sendEditMessage(userId.source, msg.messageId, inlineKeyboard = dateButtons(modifiedDate))
|
||||
stay() using modifiedDate
|
||||
}
|
||||
|
||||
private def modifyDate(date: ZonedDateTime, tag: String) = {
|
||||
val dateModifier = tag match {
|
||||
@@ -127,12 +116,6 @@ object DatePicker {
|
||||
def props(userId: UserId, bot: Bot, localization: Localization, originator: ActorRef): Props =
|
||||
Props(new DatePicker(userId, bot, localization, originator))
|
||||
|
||||
object RequestDate extends FSMState
|
||||
|
||||
object AwaitDate extends FSMState
|
||||
|
||||
object AwaitMode extends FSMState
|
||||
|
||||
trait Mode
|
||||
|
||||
object DateFromMode extends Mode
|
||||
|
||||
@@ -23,17 +23,23 @@
|
||||
*/
|
||||
package com.lbs.server.actor
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import akka.actor.Props
|
||||
import com.lbs.bot.Bot
|
||||
import com.lbs.bot.model.Command
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
|
||||
class Help(val userId: UserId, bot: Bot, val localization: Localization) extends Actor with Localizable {
|
||||
override def receive: Receive = {
|
||||
case _: Command =>
|
||||
bot.sendMessage(userId.source, lang.help)
|
||||
}
|
||||
class Help(val userId: UserId, bot: Bot, val localization: Localization) extends Conversation[Unit] with Localizable {
|
||||
|
||||
entryPoint(displayHelp)
|
||||
|
||||
def displayHelp: M =
|
||||
monologue {
|
||||
case Msg(_: Command, _) =>
|
||||
bot.sendMessage(userId.source, lang.help)
|
||||
stay()
|
||||
}
|
||||
}
|
||||
|
||||
object Help {
|
||||
|
||||
@@ -27,44 +27,38 @@ import akka.actor.{PoisonPill, Props}
|
||||
import com.lbs.api.json.model.HistoricVisit
|
||||
import com.lbs.bot.Bot
|
||||
import com.lbs.bot.model.Command
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.History.{AwaitPage, RequestData}
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.service.ApiService
|
||||
|
||||
class History(val userId: UserId, bot: Bot, apiService: ApiService, val localization: Localization, historyPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||
class History(val userId: UserId, bot: Bot, apiService: ApiService, val localization: Localization, historyPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends Conversation[Unit] with Localizable {
|
||||
|
||||
private val historyPager = historyPagerActorFactory(userId, self)
|
||||
|
||||
startWith(RequestData, null)
|
||||
entryPoint(prepareData)
|
||||
|
||||
whenSafe(RequestData) {
|
||||
case Event(Next, _) =>
|
||||
def prepareData: IC =
|
||||
internalConfig { _ =>
|
||||
val visits = apiService.visitsHistory(userId.accountId)
|
||||
historyPager ! InitConversation
|
||||
historyPager ! StartConversation
|
||||
historyPager ! visits
|
||||
goto(AwaitPage)
|
||||
}
|
||||
goto(processResponseFromPager)
|
||||
}
|
||||
|
||||
whenSafe(AwaitPage) {
|
||||
case Event(cmd: Command, _) =>
|
||||
historyPager ! cmd
|
||||
stay()
|
||||
case Event(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.visitsHistoryIsEmpty)
|
||||
goto(RequestData)
|
||||
case Event(_: HistoricVisit, _) =>
|
||||
goto(RequestData) using null
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
historyPager ! Init
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
initialize()
|
||||
def processResponseFromPager: M =
|
||||
monologue {
|
||||
case Msg(cmd: Command, _) =>
|
||||
historyPager ! cmd
|
||||
stay()
|
||||
case Msg(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.visitsHistoryIsEmpty)
|
||||
end()
|
||||
case Msg(_: HistoricVisit, _) =>
|
||||
end()
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
historyPager ! PoisonPill
|
||||
@@ -75,10 +69,4 @@ class History(val userId: UserId, bot: Bot, apiService: ApiService, val localiza
|
||||
object History {
|
||||
def props(userId: UserId, bot: Bot, apiService: ApiService, localization: Localization, historyPagerActorFactory: ByUserIdWithOriginatorActorFactory): Props =
|
||||
Props(new History(userId, bot, apiService, localization, historyPagerActorFactory))
|
||||
|
||||
object RequestData extends FSMState
|
||||
|
||||
object AwaitPage extends FSMState
|
||||
|
||||
|
||||
}
|
||||
@@ -26,77 +26,61 @@ package com.lbs.server.actor
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import com.lbs.bot.Bot
|
||||
import com.lbs.bot.model.{Command, MessageSource}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login._
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.service.{ApiService, DataService}
|
||||
import com.lbs.server.util.MessageExtractors
|
||||
import org.jasypt.util.text.TextEncryptor
|
||||
|
||||
class Login(source: MessageSource, bot: Bot, dataService: DataService, apiService: ApiService, textEncryptor: TextEncryptor, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||
class Login(source: MessageSource, bot: Bot, dataService: DataService, apiService: ApiService, textEncryptor: TextEncryptor, val localization: Localization, originator: ActorRef) extends Conversation[LoginData] with Localizable {
|
||||
|
||||
protected var userId: UserId = _
|
||||
|
||||
startWith(LogIn, LoginData())
|
||||
entryPoint(logIn)
|
||||
|
||||
private var forwardCommand: ForwardCommand = _
|
||||
|
||||
whenSafe(LogIn) {
|
||||
case Event(cmd: Command, LoginData(None, None)) =>
|
||||
forwardCommand = ForwardCommand(cmd)
|
||||
invokeNext()
|
||||
goto(RequestUsername)
|
||||
case Event(_, LoginData(Some(username), Some(password))) =>
|
||||
def logIn: M =
|
||||
monologue {
|
||||
case Msg(cmd: Command, LoginData(None, None)) =>
|
||||
forwardCommand = ForwardCommand(cmd)
|
||||
goto(requestUsername)
|
||||
}
|
||||
|
||||
def requestUsername: QA =
|
||||
question { _ =>
|
||||
bot.sendMessage(source, lang.provideUsername)
|
||||
} answer {
|
||||
case Msg(Command(_, MessageExtractors.TextOpt(username), _), _) =>
|
||||
goto(requestPassword) using LoginData(username = username)
|
||||
}
|
||||
|
||||
def requestPassword: QA =
|
||||
question { _ =>
|
||||
bot.sendMessage(source, lang.providePassword)
|
||||
} answer {
|
||||
case Msg(Command(_, MessageExtractors.TextOpt(password), _), loginData: LoginData) =>
|
||||
goto(processLoginInformation) using loginData.copy(password = password.map(textEncryptor.encrypt))
|
||||
}
|
||||
|
||||
def processLoginInformation: IC = {
|
||||
internalConfig { case LoginData(Some(username), Some(password)) =>
|
||||
val loginResult = apiService.login(username, password)
|
||||
loginResult match {
|
||||
case Left(error) =>
|
||||
bot.sendMessage(source, error.getMessage)
|
||||
invokeNext()
|
||||
goto(RequestUsername) using LoginData()
|
||||
goto(requestUsername)
|
||||
case Right(loggedIn) =>
|
||||
val credentials = dataService.saveCredentials(source, username, password)
|
||||
userId = UserId(credentials.userId, credentials.accountId, source)
|
||||
apiService.addSession(credentials.accountId, loggedIn.accessToken, loggedIn.tokenType)
|
||||
bot.sendMessage(source, lang.loginAndPasswordAreOk)
|
||||
originator ! LoggedIn(forwardCommand, credentials.userId, credentials.accountId)
|
||||
stay() using null
|
||||
end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
whenSafe(RequestUsername) {
|
||||
case Event(Next, _) =>
|
||||
bot.sendMessage(source, lang.provideUsername)
|
||||
goto(AwaitUsername)
|
||||
}
|
||||
|
||||
whenSafe(AwaitUsername) {
|
||||
case Event(Command(_, MessageExtractors.TextOpt(username), _), loginData: LoginData) =>
|
||||
invokeNext()
|
||||
goto(RequestPassword) using loginData.copy(username = username)
|
||||
}
|
||||
|
||||
whenSafe(RequestPassword) {
|
||||
case Event(Next, _) =>
|
||||
bot.sendMessage(source, lang.providePassword)
|
||||
goto(AwaitPassword)
|
||||
}
|
||||
|
||||
whenSafe(AwaitPassword) {
|
||||
case Event(Command(_, MessageExtractors.TextOpt(password), _), loginData: LoginData) =>
|
||||
invokeNext()
|
||||
goto(LogIn) using loginData.copy(password = password.map(textEncryptor.encrypt))
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
goto(LogIn) using LoginData()
|
||||
case e: Event =>
|
||||
error(s"Unhandled event in state:$stateName. Event: $e")
|
||||
stay()
|
||||
}
|
||||
|
||||
initialize()
|
||||
|
||||
}
|
||||
|
||||
object Login {
|
||||
@@ -104,17 +88,7 @@ object Login {
|
||||
def props(source: MessageSource, bot: Bot, dataService: DataService, apiService: ApiService, textEncryptor: TextEncryptor, localization: Localization, originator: ActorRef): Props =
|
||||
Props(new Login(source, bot, dataService, apiService, textEncryptor, localization, originator))
|
||||
|
||||
object LogIn extends FSMState
|
||||
|
||||
object RequestUsername extends FSMState
|
||||
|
||||
object AwaitUsername extends FSMState
|
||||
|
||||
object RequestPassword extends FSMState
|
||||
|
||||
object AwaitPassword extends FSMState
|
||||
|
||||
case class LoginData(username: Option[String] = None, password: Option[String] = None) extends FSMData
|
||||
case class LoginData(username: Option[String] = None, password: Option[String] = None)
|
||||
|
||||
case class ForwardCommand(cmd: Command)
|
||||
|
||||
|
||||
@@ -26,57 +26,54 @@ package com.lbs.server.actor
|
||||
import akka.actor.{PoisonPill, Props}
|
||||
import com.lbs.bot._
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.Monitorings.{AwaitDecision, AwaitPage, RequestData, Tags}
|
||||
import com.lbs.server.actor.Monitorings.Tags
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.repository.model.Monitoring
|
||||
import com.lbs.server.service.MonitoringService
|
||||
|
||||
class Monitorings(val userId: UserId, bot: Bot, monitoringService: MonitoringService, val localization: Localization, monitoringsPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends SafeFSM[FSMState, Monitoring] with Localizable {
|
||||
class Monitorings(val userId: UserId, bot: Bot, monitoringService: MonitoringService, val localization: Localization, monitoringsPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends Conversation[Monitoring] with Localizable {
|
||||
|
||||
private val monitoringsPager = monitoringsPagerActorFactory(userId, self)
|
||||
|
||||
startWith(RequestData, null)
|
||||
entryPoint(prepareData)
|
||||
|
||||
whenSafe(RequestData) {
|
||||
case Event(Next, _) =>
|
||||
def prepareData: IC =
|
||||
internalConfig { _ =>
|
||||
val monitorings = monitoringService.getActiveMonitorings(userId.accountId)
|
||||
monitoringsPager ! InitConversation
|
||||
monitoringsPager ! StartConversation
|
||||
monitoringsPager ! Right[Throwable, Seq[Monitoring]](monitorings)
|
||||
goto(AwaitPage)
|
||||
}
|
||||
goto(processResponseFromPager)
|
||||
}
|
||||
|
||||
whenSafe(AwaitPage) {
|
||||
case Event(cmd: Command, _) =>
|
||||
monitoringsPager ! cmd
|
||||
stay()
|
||||
case Event(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noActiveMonitorings)
|
||||
goto(RequestData)
|
||||
case Event(monitoring: Monitoring, _) =>
|
||||
def processResponseFromPager: M =
|
||||
monologue {
|
||||
case Msg(cmd: Command, _) =>
|
||||
monitoringsPager ! cmd
|
||||
stay()
|
||||
case Msg(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noActiveMonitorings)
|
||||
end()
|
||||
case Msg(monitoring: Monitoring, _) =>
|
||||
goto(askToDeactivateMonitoring) using monitoring
|
||||
}
|
||||
|
||||
def askToDeactivateMonitoring: QA =
|
||||
question { monitoring =>
|
||||
bot.sendMessage(userId.source, lang.deactivateMonitoring(monitoring), inlineKeyboard =
|
||||
createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
|
||||
goto(AwaitDecision) using monitoring
|
||||
}
|
||||
|
||||
whenSafe(AwaitDecision) {
|
||||
case Event(Command(_, _, Some(Tags.No)), _) =>
|
||||
bot.sendMessage(userId.source, lang.monitoringWasNotDeactivated)
|
||||
goto(RequestData)
|
||||
case Event(Command(_, _, Some(Tags.Yes)), monitoring: Monitoring) =>
|
||||
monitoringService.deactivateMonitoring(monitoring.recordId)
|
||||
bot.sendMessage(userId.source, lang.deactivated)
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
monitoringsPager ! Init
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
initialize()
|
||||
} answer {
|
||||
case Msg(Command(_, _, Some(Tags.No)), _) =>
|
||||
bot.sendMessage(userId.source, lang.monitoringWasNotDeactivated)
|
||||
end()
|
||||
case Msg(Command(_, _, Some(Tags.Yes)), monitoring: Monitoring) =>
|
||||
monitoringService.deactivateMonitoring(monitoring.recordId)
|
||||
bot.sendMessage(userId.source, lang.deactivated)
|
||||
end()
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
monitoringsPager ! PoisonPill
|
||||
@@ -88,12 +85,6 @@ object Monitorings {
|
||||
def props(userId: UserId, bot: Bot, monitoringService: MonitoringService, localization: Localization, monitoringsPagerActorFactory: ByUserIdWithOriginatorActorFactory): Props =
|
||||
Props(new Monitorings(userId, bot, monitoringService, localization, monitoringsPagerActorFactory))
|
||||
|
||||
object RequestData extends FSMState
|
||||
|
||||
object AwaitPage extends FSMState
|
||||
|
||||
object AwaitDecision extends FSMState
|
||||
|
||||
object Tags {
|
||||
val Yes = "yes"
|
||||
val No = "no"
|
||||
|
||||
@@ -27,57 +27,49 @@ import akka.actor.{ActorRef, Props}
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.common.Logger
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.Pager.{Tags, _}
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.util.MessageExtractors
|
||||
|
||||
class Pager[Data](val userId: UserId, bot: Bot, makeMessage: (Data, Int, Int) => String,
|
||||
makeHeader: (Int, Int) => String, selectionPrefix: Option[String],
|
||||
val localization: Localization, originator: ActorRef)
|
||||
extends SafeFSM[FSMState, FSMData] with Localizable with Logger {
|
||||
|
||||
private case class Page(page: Int, pages: Seq[Seq[Data]]) extends FSMData
|
||||
extends Conversation[(Registry[Data], Option[String])] with Localizable with Logger {
|
||||
|
||||
private val Selection = s"/${selectionPrefix.getOrElse("")}_(\\d+)_(\\d+)".r
|
||||
|
||||
startWith(PrepareData, null)
|
||||
entryPoint(awaitForData)
|
||||
|
||||
whenSafe(PrepareData) {
|
||||
case Event(Left(error: Throwable), _) =>
|
||||
bot.sendMessage(userId.source, error.getMessage)
|
||||
invokeNext()
|
||||
goto(PrepareData)
|
||||
case Event(Right(items: Seq[Data]), _) if items.isEmpty =>
|
||||
originator ! NoItemsFound
|
||||
goto(PrepareData) using null
|
||||
case Event(Right(items: Seq[Data]), _) =>
|
||||
invokeNext()
|
||||
goto(RequestData) using Page(0, items.grouped(Pager.PageSize).toList)
|
||||
}
|
||||
private def awaitForData: EC =
|
||||
externalConfig {
|
||||
case Msg(Left(error: Throwable), _) =>
|
||||
bot.sendMessage(userId.source, error.getMessage)
|
||||
end()
|
||||
case Msg(Right(items: Seq[Data]), _) if items.isEmpty =>
|
||||
originator ! NoItemsFound
|
||||
end()
|
||||
case Msg(Right(items: Seq[Data]), _) =>
|
||||
goto(displayPage) using Registry(0, items.grouped(Pager.PageSize).toList) -> None
|
||||
}
|
||||
|
||||
whenSafe(RequestData) {
|
||||
case Event(Next, page: Page) =>
|
||||
sendPage(page.page, page.pages)
|
||||
goto(AwaitData)
|
||||
}
|
||||
|
||||
whenSafe(AwaitData) {
|
||||
case Event(Command(_, msg, Some(Tags.Next)), termsData: Page) =>
|
||||
val page = termsData.page + 1
|
||||
sendPage(page, termsData.pages, Some(msg.messageId))
|
||||
stay() using termsData.copy(page = page)
|
||||
case Event(Command(_, msg, Some(Tags.Previous)), termsData: Page) =>
|
||||
val page = termsData.page - 1
|
||||
sendPage(page, termsData.pages, Some(msg.messageId))
|
||||
stay() using termsData.copy(page = page)
|
||||
case Event(Command(_, MessageExtractors.Text(Selection(pageStr, indexStr)), _), termsData: Page) if selectionPrefix.nonEmpty =>
|
||||
val page = pageStr.toInt
|
||||
val index = indexStr.toInt
|
||||
originator ! termsData.pages(page)(index)
|
||||
goto(PrepareData) using null
|
||||
}
|
||||
private def displayPage: QA =
|
||||
question { case (registry, massageIdMaybe) =>
|
||||
sendPage(registry.page, registry.pages, massageIdMaybe)
|
||||
} answer {
|
||||
case Msg(Command(_, msg, Some(Tags.Next)), (registry, _)) =>
|
||||
val page = registry.page + 1
|
||||
goto(displayPage) using registry.copy(page = page) -> Some(msg.messageId)
|
||||
case Msg(Command(_, msg, Some(Tags.Previous)), (registry, _)) =>
|
||||
val page = registry.page - 1
|
||||
goto(displayPage) using registry.copy(page = page) -> Some(msg.messageId)
|
||||
case Msg(Command(_, MessageExtractors.Text(Selection(pageStr, indexStr)), _), (registry, _)) if selectionPrefix.nonEmpty =>
|
||||
val page = pageStr.toInt
|
||||
val index = indexStr.toInt
|
||||
originator ! registry.pages(page)(index)
|
||||
end()
|
||||
}
|
||||
|
||||
private def sendPage(page: Int, data: Seq[Seq[Data]], messageId: Option[String] = None): Unit = {
|
||||
val pages = data.length
|
||||
@@ -94,31 +86,16 @@ class Pager[Data](val userId: UserId, bot: Bot, makeMessage: (Data, Int, Int) =>
|
||||
bot.sendMessage(userId.source, message, inlineKeyboard = createInlineKeyboard(buttons))
|
||||
}
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
goto(PrepareData) using null
|
||||
case e: Event =>
|
||||
error(s"Unhandled event in state:$stateName. Event: $e")
|
||||
stay()
|
||||
}
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
object Pager {
|
||||
|
||||
def props[Data](userId: UserId, bot: Bot,
|
||||
makeMessage: (Data, Int, Int) => String, makeHeader: (Int, Int) => String, dataPrefix: Option[String], localization: Localization, originator: ActorRef): Props =
|
||||
Props(new Pager[Data](userId, bot, makeMessage, makeHeader, dataPrefix, localization, originator))
|
||||
|
||||
val PageSize = 5
|
||||
|
||||
object PrepareData extends FSMState
|
||||
|
||||
object RequestData extends FSMState
|
||||
|
||||
object AwaitData extends FSMState
|
||||
case class Registry[Data](page: Int, pages: Seq[Seq[Data]])
|
||||
|
||||
object NoItemsFound
|
||||
|
||||
|
||||
@@ -26,57 +26,42 @@ package com.lbs.server.actor
|
||||
import akka.actor.Props
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.Settings._
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Lang, Localizable, Localization}
|
||||
import com.lbs.server.service.DataService
|
||||
|
||||
class Settings(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||
class Settings(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization) extends Conversation[Unit] with Localizable {
|
||||
|
||||
startWith(RequestAction, null)
|
||||
entryPoint(askForAction)
|
||||
|
||||
whenSafe(RequestAction) {
|
||||
case Event(Next, _) =>
|
||||
def askForAction: QA =
|
||||
question { _ =>
|
||||
bot.sendMessage(userId.source, lang.settingsHeader, inlineKeyboard =
|
||||
createInlineKeyboard(Seq(Button(lang.language, Tags.Language))))
|
||||
goto(AwaitAction)
|
||||
}
|
||||
} answer {
|
||||
case Msg(Command(_, _, Some(Tags.Language)), _) =>
|
||||
goto(askLanguage)
|
||||
}
|
||||
|
||||
whenSafe(AwaitAction) {
|
||||
case Event(Command(_, _, Some(Tags.Language)), _) =>
|
||||
def askLanguage: QA =
|
||||
question { _ =>
|
||||
bot.sendMessage(userId.source, lang.chooseLanguage,
|
||||
inlineKeyboard = createInlineKeyboard(Lang.Langs.map(l => Button(l.label, l.id)), columns = 1))
|
||||
goto(AwaitLanguage)
|
||||
}
|
||||
|
||||
whenSafe(AwaitLanguage) {
|
||||
case Event(Command(_, _, Some(langIdStr)), _) =>
|
||||
val langId = langIdStr.toInt
|
||||
localization.updateLanguage(userId.userId, Lang(langId))
|
||||
bot.sendMessage(userId.source, lang.languageUpdated)
|
||||
goto(RequestAction) using null
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
goto(RequestAction)
|
||||
}
|
||||
|
||||
initialize()
|
||||
} answer {
|
||||
case Msg(Command(_, _, Some(langIdStr)), _) =>
|
||||
val langId = langIdStr.toInt
|
||||
localization.updateLanguage(userId.userId, Lang(langId))
|
||||
bot.sendMessage(userId.source, lang.languageUpdated)
|
||||
end()
|
||||
}
|
||||
}
|
||||
|
||||
object Settings {
|
||||
def props(userId: UserId, bot: Bot, dataService: DataService, localization: Localization): Props =
|
||||
Props(new Settings(userId, bot, dataService, localization))
|
||||
|
||||
object AwaitLanguage extends FSMState
|
||||
|
||||
object RequestAction extends FSMState
|
||||
|
||||
object AwaitAction extends FSMState
|
||||
|
||||
object Tags {
|
||||
val Language = "language"
|
||||
}
|
||||
|
||||
@@ -27,94 +27,73 @@ import akka.actor.{ActorRef, Props}
|
||||
import com.lbs.api.json.model.IdName
|
||||
import com.lbs.bot.model.{Button, Command, TaggedButton}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.StaticData._
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
|
||||
class StaticData(val userId: UserId, bot: Bot, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, IdName] with Localizable {
|
||||
class StaticData(val userId: UserId, bot: Bot, val localization: Localization, originator: ActorRef) extends Conversation[List[TaggedButton]] with Localizable {
|
||||
|
||||
private def anySelectOption: List[TaggedButton] = if (config.isAnyAllowed) List(Button(lang.any, -1L)) else List()
|
||||
|
||||
startWith(AwaitConfig, null)
|
||||
|
||||
private var config: StaticDataConfig = _
|
||||
|
||||
private var callbackTags: List[TaggedButton] = List()
|
||||
entryPoint(AwaitConfig)
|
||||
|
||||
whenSafe(AwaitConfig) {
|
||||
case Event(newConfig: StaticDataConfig, _) =>
|
||||
config = newConfig
|
||||
invokeNext()
|
||||
goto(RequestStaticData)
|
||||
}
|
||||
def AwaitConfig: EC =
|
||||
externalConfig {
|
||||
case Msg(newConfig: StaticDataConfig, _) =>
|
||||
config = newConfig
|
||||
goto(askForLatestOption)
|
||||
}
|
||||
|
||||
whenSafe(RequestStaticData) {
|
||||
case Event(Next, _) =>
|
||||
def askForLatestOption: QA =
|
||||
question { _ =>
|
||||
originator ! LatestOptions
|
||||
stay()
|
||||
case Event(LatestOptions(options), _) if options.isEmpty =>
|
||||
callbackTags = anySelectOption
|
||||
bot.sendMessage(userId.source, lang.pleaseEnterStaticDataNameOrAny(config),
|
||||
inlineKeyboard = createInlineKeyboard(callbackTags))
|
||||
goto(AwaitStaticData)
|
||||
case Event(LatestOptions(options), _) if options.nonEmpty =>
|
||||
callbackTags = anySelectOption ++ options.map(data => Button(data.name, data.id))
|
||||
} answer {
|
||||
case Msg(LatestOptions(options), _) if options.isEmpty =>
|
||||
val callbackTags = anySelectOption
|
||||
goto(askForUserInput) using callbackTags
|
||||
case Msg(LatestOptions(options), _) if options.nonEmpty =>
|
||||
val callbackTags = anySelectOption ++ options.map(data => Button(data.name, data.id))
|
||||
goto(askForUserInput) using callbackTags
|
||||
}
|
||||
|
||||
def askForUserInput: QA =
|
||||
question { callbackTags =>
|
||||
bot.sendMessage(userId.source, lang.pleaseEnterStaticDataNameOrPrevious(config),
|
||||
inlineKeyboard = createInlineKeyboard(callbackTags, columns = 1))
|
||||
goto(AwaitStaticData)
|
||||
}
|
||||
} answer {
|
||||
case Msg(Command(_, msg, Some(tag)), callbackTags) =>
|
||||
val id = tag.toLong
|
||||
val label = callbackTags.find(_.tag == tag).map(_.label).getOrElse(sys.error("Unable to get callback tag label"))
|
||||
bot.sendEditMessage(userId.source, msg.messageId, lang.staticDataIs(config, label))
|
||||
originator ! IdName(id, label)
|
||||
end()
|
||||
|
||||
whenSafe(AwaitStaticData) {
|
||||
case Event(Command(_, msg, Some(tag)), _) =>
|
||||
val id = tag.toLong
|
||||
val label = callbackTags.find(_.tag == tag).map(_.label).getOrElse(sys.error("Unable to get callback tag label"))
|
||||
bot.sendEditMessage(userId.source, msg.messageId, lang.staticDataIs(config, label))
|
||||
originator ! IdName(id, label)
|
||||
goto(AwaitConfig)
|
||||
case Msg(Command(_, msg, _), _) =>
|
||||
val searchText = msg.text.get.toLowerCase
|
||||
originator ! FindOptions(searchText)
|
||||
stay()
|
||||
|
||||
case Event(Command(_, msg, _), _) =>
|
||||
val searchText = msg.text.get.toLowerCase
|
||||
originator ! FindOptions(searchText)
|
||||
stay()
|
||||
case Msg(FoundOptions(Right(options)), _) if options.nonEmpty =>
|
||||
val callbackTags = anySelectOption ::: options.map(c => Button(c.name, c.id))
|
||||
goto(askForUserInput) using callbackTags
|
||||
|
||||
case Event(FoundOptions(Right(options)), _) if options.nonEmpty =>
|
||||
callbackTags = anySelectOption ::: options.map(c => Button(c.name, c.id))
|
||||
bot.sendMessage(userId.source, lang.pleaseChooseStaticDataNameOrAny(config),
|
||||
inlineKeyboard = createInlineKeyboard(callbackTags, columns = 1))
|
||||
stay()
|
||||
case Msg(FoundOptions(Right(options)), _) if options.isEmpty =>
|
||||
val callbackTags = anySelectOption
|
||||
goto(askForUserInput) using callbackTags
|
||||
|
||||
case Event(FoundOptions(Right(options)), _) if options.isEmpty =>
|
||||
callbackTags = anySelectOption
|
||||
bot.sendMessage(userId.source, lang.staticNotFound(config), inlineKeyboard = createInlineKeyboard(callbackTags))
|
||||
stay()
|
||||
|
||||
case Event(FoundOptions(Left(ex)), _) =>
|
||||
bot.sendMessage(userId.source, ex.getMessage)
|
||||
stay()
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
goto(AwaitConfig) using null
|
||||
case e: Event =>
|
||||
error(s"Unhandled event in state:$stateName. Event: $e")
|
||||
stay()
|
||||
}
|
||||
|
||||
initialize()
|
||||
case Msg(FoundOptions(Left(ex)), _) =>
|
||||
bot.sendMessage(userId.source, ex.getMessage)
|
||||
end()
|
||||
}
|
||||
}
|
||||
|
||||
object StaticData {
|
||||
def props(userId: UserId, bot: Bot, localization: Localization, originator: ActorRef): Props =
|
||||
Props(new StaticData(userId, bot, localization, originator))
|
||||
|
||||
object AwaitConfig extends FSMState
|
||||
|
||||
object RequestStaticData extends FSMState
|
||||
|
||||
object AwaitStaticData extends FSMState
|
||||
|
||||
case class StaticDataConfig(name: String, example: String, isAnyAllowed: Boolean)
|
||||
|
||||
object LatestOptions
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Yevhen Zadyra
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Yevhen Zadyra
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.lbs.server.actor
|
||||
|
||||
import akka.actor.ActorRef
|
||||
@@ -28,6 +28,7 @@ import com.lbs.api.json.model.IdName
|
||||
import com.lbs.bot.model.Command
|
||||
import com.lbs.server.actor.Book.BookingData
|
||||
import com.lbs.server.actor.StaticData.{FindOptions, FoundOptions, LatestOptions, StaticDataConfig}
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
|
||||
trait StaticDataForBooking extends SafeFSM[FSMState, FSMData] {
|
||||
|
||||
@@ -53,6 +54,8 @@ trait StaticDataForBooking extends SafeFSM[FSMState, FSMData] {
|
||||
protected def requestStaticData(requestState: FSMState, awaitState: FSMState, staticDataConfig: => StaticDataConfig)(functions: BookingData => FSMState => StateFunction)(requestNext: FSMState): Unit = {
|
||||
whenSafe(requestState) {
|
||||
case Event(_, _) =>
|
||||
staticData ! InitConversation
|
||||
staticData ! StartConversation
|
||||
staticData ! staticDataConfig
|
||||
goto(awaitState)
|
||||
}
|
||||
|
||||
@@ -27,59 +27,56 @@ import akka.actor.{PoisonPill, Props}
|
||||
import com.lbs.api.json.model.ReservedVisit
|
||||
import com.lbs.bot.model.{Button, Command}
|
||||
import com.lbs.bot.{Bot, _}
|
||||
import com.lbs.server.actor.Chat.Init
|
||||
import com.lbs.server.actor.Login.UserId
|
||||
import com.lbs.server.actor.Visits.{AwaitDecision, AwaitPage, RequestData, Tags}
|
||||
import com.lbs.server.actor.Visits.Tags
|
||||
import com.lbs.server.actor.conversation.Conversation
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
import com.lbs.server.lang.{Localizable, Localization}
|
||||
import com.lbs.server.service.ApiService
|
||||
|
||||
class Visits(val userId: UserId, bot: Bot, apiService: ApiService, val localization: Localization,
|
||||
visitsPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends SafeFSM[FSMState, ReservedVisit] with Localizable {
|
||||
visitsPagerActorFactory: ByUserIdWithOriginatorActorFactory) extends Conversation[ReservedVisit] with Localizable {
|
||||
|
||||
private val reservedVisitsPager = visitsPagerActorFactory(userId, self)
|
||||
|
||||
startWith(RequestData, null)
|
||||
entryPoint(prepareData)
|
||||
|
||||
whenSafe(RequestData) {
|
||||
case Event(Next, _) =>
|
||||
def prepareData: IC =
|
||||
internalConfig { _ =>
|
||||
val visits = apiService.reservedVisits(userId.accountId)
|
||||
reservedVisitsPager ! InitConversation
|
||||
reservedVisitsPager ! StartConversation
|
||||
reservedVisitsPager ! visits
|
||||
goto(AwaitPage)
|
||||
}
|
||||
goto(processResponseFromPager)
|
||||
}
|
||||
|
||||
whenSafe(AwaitPage) {
|
||||
case Event(cmd: Command, _) =>
|
||||
reservedVisitsPager ! cmd
|
||||
stay()
|
||||
case Event(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noUpcomingVisits)
|
||||
goto(RequestData)
|
||||
case Event(visit: ReservedVisit, _) =>
|
||||
def processResponseFromPager: M =
|
||||
monologue {
|
||||
case Msg(cmd: Command, _) =>
|
||||
reservedVisitsPager ! cmd
|
||||
stay()
|
||||
case Msg(Pager.NoItemsFound, _) =>
|
||||
bot.sendMessage(userId.source, lang.noUpcomingVisits)
|
||||
end()
|
||||
case Msg(visit: ReservedVisit, _) =>
|
||||
goto(askToCancelVisit) using visit
|
||||
}
|
||||
|
||||
def askToCancelVisit: QA =
|
||||
question { visit =>
|
||||
bot.sendMessage(userId.source, lang.areYouSureToCancelAppointment(visit),
|
||||
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
|
||||
goto(AwaitDecision) using visit
|
||||
}
|
||||
|
||||
whenSafe(AwaitDecision) {
|
||||
case Event(Command(_, _, Some(Tags.No)), _) =>
|
||||
bot.sendMessage(userId.source, lang.appointmentWasNotCancelled)
|
||||
goto(RequestData)
|
||||
case Event(Command(_, _, Some(Tags.Yes)), visit: ReservedVisit) =>
|
||||
apiService.deleteReservation(userId.accountId, visit.reservationId) match {
|
||||
case Left(ex) => bot.sendMessage(userId.source, lang.unableToCancelUpcomingVisit(ex.getMessage))
|
||||
case Right(r) => bot.sendMessage(userId.source, lang.appointmentHasBeenCancelled)
|
||||
}
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
whenUnhandledSafe {
|
||||
case Event(Init, _) =>
|
||||
invokeNext()
|
||||
reservedVisitsPager ! Init
|
||||
goto(RequestData)
|
||||
}
|
||||
|
||||
initialize()
|
||||
} answer {
|
||||
case Msg(Command(_, _, Some(Tags.No)), _) =>
|
||||
bot.sendMessage(userId.source, lang.appointmentWasNotCancelled)
|
||||
end()
|
||||
case Msg(Command(_, _, Some(Tags.Yes)), visit: ReservedVisit) =>
|
||||
apiService.deleteReservation(userId.accountId, visit.reservationId) match {
|
||||
case Left(ex) => bot.sendMessage(userId.source, lang.unableToCancelUpcomingVisit(ex.getMessage))
|
||||
case Right(r) => bot.sendMessage(userId.source, lang.appointmentHasBeenCancelled)
|
||||
}
|
||||
end()
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
reservedVisitsPager ! PoisonPill
|
||||
@@ -92,12 +89,6 @@ object Visits {
|
||||
visitsPagerActorFactory: ByUserIdWithOriginatorActorFactory): Props =
|
||||
Props(new Visits(userId, bot, apiService, localization, visitsPagerActorFactory))
|
||||
|
||||
object RequestData extends FSMState
|
||||
|
||||
object AwaitPage extends FSMState
|
||||
|
||||
object AwaitDecision extends FSMState
|
||||
|
||||
object Tags {
|
||||
val Yes = "yes"
|
||||
val No = "no"
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.lbs.server.actor.conversation
|
||||
|
||||
import akka.actor.Actor
|
||||
import com.lbs.common.Logger
|
||||
import com.lbs.server.actor.conversation.Conversation.{ContinueConversation, InitConversation, StartConversation}
|
||||
|
||||
trait Conversation[D] extends Actor with Domain[D] with Logger {
|
||||
private var currentData: D = _
|
||||
|
||||
private var currentStep: Step = _
|
||||
|
||||
private var startWithData: D = _
|
||||
|
||||
private var startWithStep: Step = _
|
||||
|
||||
private val defaultMsgHandler: AnswerFn = {
|
||||
case Msg(any, data) =>
|
||||
debug(s"Unhandled fact message received. [$any, $data]")
|
||||
NextStep(currentStep, Some(data))
|
||||
}
|
||||
|
||||
private var msgHandler: AnswerFn = defaultMsgHandler
|
||||
|
||||
private var runAfterInit: () => Unit = () => {}
|
||||
|
||||
override def receive: Receive = {
|
||||
case InitConversation => init()
|
||||
case StartConversation | ContinueConversation =>
|
||||
currentStep match {
|
||||
case qa: QuestionAnswer => qa.question.questionFn(currentData)
|
||||
case InternalConfiguration(fn) =>
|
||||
val nextStep = fn(currentData)
|
||||
moveToNextStep(nextStep)
|
||||
case _ => //do nothing
|
||||
}
|
||||
case any => makeTransition(any)
|
||||
}
|
||||
|
||||
private def moveToNextStep(nextStep: NextStep): Unit = {
|
||||
currentStep = nextStep.step
|
||||
nextStep.data.foreach { data =>
|
||||
currentData = data
|
||||
}
|
||||
}
|
||||
|
||||
private def makeTransition(any: Any): Unit = {
|
||||
def handle[X](unit: X, fn: PartialFunction[X, NextStep], defaultFn: PartialFunction[X, NextStep]): Unit = {
|
||||
val nextStep = if (fn.isDefinedAt(unit)) fn(unit) else defaultFn(unit)
|
||||
moveToNextStep(nextStep)
|
||||
}
|
||||
|
||||
currentStep match {
|
||||
case ExternalConfiguration(fn) =>
|
||||
val conf = Msg(any, currentData)
|
||||
handle(conf, fn, msgHandler)
|
||||
case QuestionAnswer(_, Answer(fn)) =>
|
||||
val fact = Msg(any, currentData)
|
||||
handle(fact, fn, msgHandler)
|
||||
case Monologue(fn) =>
|
||||
val fact = Msg(any, currentData)
|
||||
handle(fact, fn, msgHandler)
|
||||
case _ => //do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private def init(): Unit = {
|
||||
require(startWithStep != null, "Entry point must be defined")
|
||||
currentStep = startWithStep
|
||||
currentData = startWithData
|
||||
runAfterInit()
|
||||
}
|
||||
|
||||
override def preStart(): Unit = {
|
||||
init()
|
||||
}
|
||||
|
||||
protected def monologue(answerFn: AnswerFn): Monologue = Monologue(answerFn)
|
||||
|
||||
protected def question(questionFn: D => Unit): Question = Question(questionFn)
|
||||
|
||||
protected def externalConfig(receiveConfFunction: ExternalConfigFn): ExternalConfiguration = ExternalConfiguration(receiveConfFunction)
|
||||
|
||||
protected def internalConfig(receiveConfFunction: InternalConfigFn): InternalConfiguration = InternalConfiguration(receiveConfFunction)
|
||||
|
||||
protected def end(): NextStep = NextStep(End)
|
||||
|
||||
protected def goto(step: Step): NextStep = {
|
||||
self ! ContinueConversation
|
||||
NextStep(step)
|
||||
}
|
||||
|
||||
protected def stay(): NextStep = NextStep(currentStep)
|
||||
|
||||
protected def whenUnhandledMsg(receiveMsgFn: AnswerFn): Unit = {
|
||||
msgHandler = receiveMsgFn orElse defaultMsgHandler
|
||||
}
|
||||
|
||||
protected def afterInit(fn: => Unit): Unit = {
|
||||
runAfterInit = () => fn
|
||||
}
|
||||
|
||||
protected def entryPoint(step: Step, data: D): Unit = {
|
||||
startWithStep = step
|
||||
startWithData = data
|
||||
}
|
||||
|
||||
protected def entryPoint(step: Step): Unit = {
|
||||
entryPoint(step, null.asInstanceOf[D])
|
||||
}
|
||||
}
|
||||
|
||||
object Conversation {
|
||||
|
||||
object StartConversation
|
||||
|
||||
object ContinueConversation
|
||||
|
||||
object InitConversation
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.lbs.server.actor.conversation
|
||||
|
||||
trait Domain[D] {
|
||||
protected type QuestionFn = D => Unit
|
||||
|
||||
protected type AnswerFn = PartialFunction[Msg, NextStep]
|
||||
|
||||
protected type ExternalConfigFn = AnswerFn
|
||||
|
||||
protected type InternalConfigFn = D => NextStep
|
||||
|
||||
protected case class Msg(message: Any, data: D)
|
||||
|
||||
sealed trait Step
|
||||
|
||||
private[conversation] object End extends Step
|
||||
|
||||
protected case class ExternalConfiguration(configFn: ExternalConfigFn) extends Step
|
||||
|
||||
protected case class InternalConfiguration(configFn: InternalConfigFn) extends Step
|
||||
|
||||
protected case class QuestionAnswer(question: Question, answer: Answer) extends Step
|
||||
|
||||
protected case class Monologue(answerFn: AnswerFn) extends Step
|
||||
|
||||
private[conversation] case class NextStep(step: Step, data: Option[D] = None)
|
||||
|
||||
private[conversation] case class Question(questionFn: QuestionFn)
|
||||
|
||||
private[conversation] case class Answer(answerFn: AnswerFn)
|
||||
|
||||
protected type QA = QuestionAnswer
|
||||
|
||||
protected type EC = ExternalConfiguration
|
||||
|
||||
protected type IC = InternalConfiguration
|
||||
|
||||
protected type M = Monologue
|
||||
|
||||
protected implicit class RichQuestion(question: Question) {
|
||||
def answer(answerFn: AnswerFn): QuestionAnswer = QuestionAnswer(question, Answer(answerFn))
|
||||
}
|
||||
|
||||
protected implicit class NextStepOps(nextStep: NextStep) {
|
||||
def using(data: D): NextStep = {
|
||||
nextStep.copy(data = Some(data))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.lbs.server.actor.conversation
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import com.lbs.server.actor.AkkaTestKit
|
||||
import com.lbs.server.actor.conversation.Conversation.{InitConversation, StartConversation}
|
||||
|
||||
class ConversationSpec extends AkkaTestKit {
|
||||
|
||||
"Actor must send complete data" when {
|
||||
"conversation is done" in {
|
||||
|
||||
case class Data(configured: Boolean = false, hello: String = null, world: String = null, people: String = null)
|
||||
|
||||
object Hello
|
||||
|
||||
object World
|
||||
|
||||
object Dialogue
|
||||
|
||||
class TestActor(originator: ActorRef) extends Conversation[Data] {
|
||||
|
||||
private var conf: String = _
|
||||
|
||||
def configure: EC =
|
||||
externalConfig {
|
||||
case Msg(confStr: String, data) =>
|
||||
conf = confStr
|
||||
goto(askHello) using data.copy(configured = true)
|
||||
}
|
||||
|
||||
def askHello: QA =
|
||||
question { data =>
|
||||
self ! Hello
|
||||
} answer {
|
||||
case Msg(Hello, data) =>
|
||||
goto(askWorld) using data.copy(hello = "hello")
|
||||
}
|
||||
|
||||
def askWorld: QA =
|
||||
question { data =>
|
||||
self ! World
|
||||
} answer {
|
||||
case Msg(World, data) =>
|
||||
goto(askDialogue) using data.copy(world = "world")
|
||||
}
|
||||
|
||||
def askDialogue: QA =
|
||||
question { data =>
|
||||
self ! Dialogue
|
||||
} answer {
|
||||
case Msg(Dialogue, data) =>
|
||||
originator ! data.copy(people = "dialogue") -> conf
|
||||
end()
|
||||
}
|
||||
|
||||
entryPoint(configure, Some(Data()))
|
||||
}
|
||||
|
||||
val expected = Data(configured = true, "hello", "world", "dialogue") -> "myconf"
|
||||
|
||||
val originator = TestProbe()
|
||||
val actor = system.actorOf(Props(new TestActor(originator.ref)))
|
||||
actor ! StartConversation
|
||||
actor ! expected._2
|
||||
originator.expectMsg(expected)
|
||||
|
||||
//reinit
|
||||
actor ! InitConversation
|
||||
actor ! StartConversation
|
||||
actor ! expected._2
|
||||
originator.expectMsg(expected)
|
||||
}
|
||||
|
||||
"configuration is done" in {
|
||||
|
||||
case class Data(configured: Boolean = false, message1: String = null, message2: String = null)
|
||||
|
||||
object InvokeEnrichMessage
|
||||
|
||||
class TestActor(originator: ActorRef) extends Conversation[Data] {
|
||||
|
||||
def configure1: IC =
|
||||
internalConfig { _ =>
|
||||
goto(configure2) using Data(configured = true)
|
||||
}
|
||||
|
||||
def configure2: IC =
|
||||
internalConfig { data =>
|
||||
goto(askMessage2) using data.copy(message1 = "hello")
|
||||
}
|
||||
|
||||
def askMessage2: QA =
|
||||
question { _ =>
|
||||
self ! InvokeEnrichMessage
|
||||
} answer {
|
||||
case Msg(InvokeEnrichMessage, data) =>
|
||||
originator ! data.copy(message2 = "world")
|
||||
end()
|
||||
}
|
||||
|
||||
entryPoint(configure1, Some(Data()))
|
||||
}
|
||||
|
||||
val expected = Data(configured = true, message1 = "hello", message2 = "world")
|
||||
|
||||
val originator = TestProbe()
|
||||
val actor = system.actorOf(Props(new TestActor(originator.ref)))
|
||||
actor ! StartConversation
|
||||
originator.expectMsg(expected)
|
||||
|
||||
//reinit
|
||||
actor ! InitConversation
|
||||
actor ! StartConversation
|
||||
originator.expectMsg(expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user