Refactoring. Moved some functions. Router is a conversation now, not an actor

This commit is contained in:
Eugene Zadyra
2018-07-25 17:33:44 +02:00
parent f2cdb7b346
commit 2f573a5c20
5 changed files with 115 additions and 108 deletions

View File

@@ -23,7 +23,7 @@
*/
package com.lbs.server
import akka.actor.{ActorRef, ActorSystem}
import akka.actor.ActorSystem
import com.lbs.api.json.model.{AvailableVisitsTermPresentation, HistoricVisit, ReservedVisit}
import com.lbs.bot.Bot
import com.lbs.bot.telegram.TelegramBot
@@ -162,7 +162,7 @@ class BootConfig {
Some("cancel"), localization, originator)(actorSystem)
@Bean
def router: ActorRef = actorSystem.actorOf(Router.props(authFactory))
def router: Router = new Router(authFactory)(actorSystem)
@Bean
def telegram: TelegramBot = new TelegramBot(router ! _, telegramBotToken)

View File

@@ -23,7 +23,7 @@
*/
package com.lbs.server.conversation
import akka.actor.{ActorRef, ActorSystem}
import akka.actor.ActorSystem
import com.lbs.bot.model.Button
import com.lbs.bot.{Bot, _}
import com.lbs.server.conversation.Account._
@@ -33,7 +33,7 @@ import com.lbs.server.lang.{Localizable, Localization}
import com.lbs.server.service.DataService
import com.lbs.server.util.MessageExtractors.CallbackCommand
class Account(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization, router: ActorRef)(val actorSystem: ActorSystem) extends Conversation[Unit] with Localizable {
class Account(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization, router: Router)(val actorSystem: ActorSystem) extends Conversation[Unit] with Localizable {
entryPoint(askAction)
@@ -49,32 +49,33 @@ class Account(val userId: UserId, bot: Bot, dataService: DataService, val locali
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()
}
switchAccount(accountId)
}
end()
}
private def switchAccount(accountId: Long): Unit = {
val accountMaybe = dataService.findUserCredentialsByAccountId(userId.userId, accountId)
accountMaybe match {
case Some(account) =>
val userMaybe = dataService.findUser(userId.userId)
userMaybe.foreach { user =>
user.activeAccountId = accountId
dataService.saveUser(user)
router ! SwitchAccount(UserId(account.userId, account.accountId, userId.source))
bot.sendMessage(userId.source, lang.accountSwitched(account.username))
}
case None =>
error(s"This is not user [#${userId.userId}] account [#$accountId]")
}
}
}
object Account {
case class SwitchUser(userId: UserId)
case class SwitchAccount(userId: UserId)
}

View File

@@ -50,64 +50,72 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
entryPoint(helpChat)
private def helpChat: Step = dialogue(help) {
case Msg(cmd@TextCommand("/help"), _) =>
help ! cmd
stay()
case Msg(cmd@TextCommand("/start"), _) =>
help ! cmd
stay()
}
private def helpChat: Step =
dialogue(help) {
case Msg(cmd@TextCommand("/help"), _) =>
help ! cmd
stay()
case Msg(cmd@TextCommand("/start"), _) =>
help ! cmd
stay()
}
private def bookChat: Step = dialogue(book) {
case Msg(TextCommand("/book"), _) =>
book.restart()
stay()
}
private def bookChat: Step =
dialogue(book) {
case Msg(TextCommand("/book"), _) =>
book.restart()
stay()
}
private def historyChat: Step = dialogue(history) {
case Msg(TextCommand("/history"), _) =>
history.restart()
stay()
}
private def historyChat: Step =
dialogue(history) {
case Msg(TextCommand("/history"), _) =>
history.restart()
stay()
}
private def visitsChat: Step = dialogue(visits) {
case Msg(TextCommand("/reserved"), _) =>
visits.restart()
stay()
}
private def visitsChat: Step =
dialogue(visits) {
case Msg(TextCommand("/reserved"), _) =>
visits.restart()
stay()
}
private def bugChat: Step = dialogue(bug) {
case Msg(TextCommand("/bug"), _) =>
bug.restart()
stay()
}
private def bugChat: Step =
dialogue(bug) {
case Msg(TextCommand("/bug"), _) =>
bug.restart()
stay()
}
private def monitoringsChat: Step = dialogue(monitorings) {
case Msg(TextCommand("/monitorings"), _) =>
monitorings.restart()
stay()
}
private def monitoringsChat: Step =
dialogue(monitorings) {
case Msg(TextCommand("/monitorings"), _) =>
monitorings.restart()
stay()
}
private def settingsChat: Step = dialogue(settings) {
case Msg(TextCommand("/settings"), _) =>
settings.restart()
stay()
}
private def settingsChat: Step =
dialogue(settings) {
case Msg(TextCommand("/settings"), _) =>
settings.restart()
stay()
}
private def accountChat: Step = dialogue(account) {
case Msg(TextCommand("/accounts"), _) =>
account.restart()
stay()
}
private def accountChat: Step =
dialogue(account) {
case Msg(TextCommand("/accounts"), _) =>
account.restart()
stay()
}
private def dialogue(interactional: Interactional)(mainStateFunction: MessageProcessorFn): Step =
private def dialogue(interactional: Interactional)(mainMessageProcessor: MessageProcessorFn): Step =
monologue {
case event: Msg =>
if (mainStateFunction.isDefinedAt(event)) mainStateFunction(event)
if (mainMessageProcessor.isDefinedAt(event)) mainMessageProcessor(event)
else {
val secondaryStateFunction = secondaryState(interactional)
secondaryStateFunction(event)
val defaultMessageProcessor = secondaryState(interactional)
defaultMessageProcessor(event)
}
}
@@ -139,7 +147,7 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
case Msg(cmd@TextCommand("/accounts"), _) =>
self ! cmd
goto(accountChat)
case Msg(cmd@TextCommand(MonitoringId(monitoringIdStr, scheduleIdStr, timeStr)), _) =>
case Msg(TextCommand(MonitoringId(monitoringIdStr, scheduleIdStr, timeStr)), _) =>
val monitoringId = monitoringIdStr.toLong
val scheduleId = scheduleIdStr.toLong
val time = timeStr.toLong

View File

@@ -23,17 +23,19 @@
*/
package com.lbs.server.conversation
import akka.actor.{Actor, Cancellable, Props}
import akka.actor.{ActorSystem, Cancellable}
import com.lbs.bot.model.{Command, MessageSource}
import com.lbs.common.Logger
import com.lbs.server.conversation.Account.SwitchUser
import com.lbs.server.conversation.Router.DestroyChat
import com.lbs.server.conversation.Account.SwitchAccount
import com.lbs.server.conversation.base.Conversation
import scala.collection.mutable
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration.DurationLong
class Router(authFactory: MessageSourceTo[Auth]) extends Actor with Logger {
class Router(authFactory: MessageSourceTo[Auth])(val actorSystem: ActorSystem) extends Conversation[Unit] with Logger {
private case class DestroyChat(source: MessageSource)
private val chats = mutable.Map.empty[MessageSource, Auth]
@@ -41,38 +43,41 @@ class Router(authFactory: MessageSourceTo[Auth]) extends Actor with Logger {
private val idleTimeout = 1.hour
private implicit val dispatcher: ExecutionContextExecutor = context.system.dispatcher
private implicit val dispatcher: ExecutionContextExecutor = actorSystem.dispatcher
override def receive: Receive = {
case cmd@Command(source, _, _) =>
scheduleIdleChatDestroyer(source)
val chat = chats.get(source) match {
case Some(actor) => actor
case None => addNewChat(source)
}
chat ! cmd
case DestroyChat(source) =>
destroyChat(source)
case SwitchUser(userId) =>
switchUser(userId)
case what => info(s"Unknown message: $what")
}
entryPoint(routeMessage)
private def addNewChat(source: MessageSource): Auth = {
val actor = authFactory(source)
chats += source -> actor
actor
private def routeMessage: Step =
monologue {
case Msg(cmd@Command(source, _, _), _) =>
val chat = instantiateChatOrGet(source)
chat ! cmd
stay()
case Msg(DestroyChat(source), _) =>
info(s"Destroying chat for $source due to $idleTimeout of inactivity")
destroyChat(source)
stay()
case Msg(SwitchAccount(userId), _) =>
switchAccount(userId)
stay()
case msg: Msg =>
info(s"Unknown message received: $msg")
stay()
}
private def instantiateChatOrGet(source: MessageSource) = {
scheduleIdleChatDestroyer(source)
chats.getOrElseUpdate(source, authFactory(source))
}
private def destroyChat(source: MessageSource): Unit = {
info(s"Destroying chat for $source due to $idleTimeout of inactivity")
timers.remove(source)
removeChat(source)
}
private def switchUser(userId: Login.UserId): Unit = {
private def switchAccount(userId: Login.UserId): Unit = {
removeChat(userId.source)
addNewChat(userId.source)
chats += userId.source -> authFactory(userId.source)
}
private def removeChat(source: MessageSource): Unit = {
@@ -81,20 +86,13 @@ class Router(authFactory: MessageSourceTo[Auth]) extends Actor with Logger {
private def scheduleIdleChatDestroyer(source: MessageSource): Unit = {
timers.remove(source).foreach(_.cancel())
val cancellable = context.system.scheduler.scheduleOnce(idleTimeout) {
val cancellable = actorSystem.scheduler.scheduleOnce(idleTimeout) {
self ! DestroyChat(source)
}
timers += source -> cancellable
}
override def postStop(): Unit = {
chats.foreach(chat => removeChat(chat._1))
beforeDestroy {
chats.foreach(chat => destroyChat(chat._1))
}
}
object Router {
def props(authFactory: MessageSourceTo[Auth]) = Props(new Router(authFactory))
case class DestroyChat(source: MessageSource)
}

View File

@@ -15,14 +15,14 @@ trait Interactional extends Logger {
protected def actorSystem: ActorSystem
protected val self: Interactional = this
private[base] def initializeConversation(): Unit
private[base] def executeCurrentStep(): Unit
private[base] def makeStepTransition(any: Any): Unit
protected val self: Interactional = this
private var onDestroy: () => Unit = () => {}
private def actorCreator: Actor = new Actor {