
definition.StoryStep.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2017/2021 e-voyageurs technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.tock.bot.definition
import ai.tock.bot.admin.story.StoryDefinitionStepMetric
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.user.UserTimeline
import java.util.concurrent.ConcurrentHashMap
/**
* step -> intent default behaviour.
*/
internal val stepToIntentRepository = ConcurrentHashMap, IntentAware>()
/**
* Use this step when you want to set a null [StoryStep].
*/
val noStep = object : SimpleStoryStep {
override val name: String = "_NO_STEP_"
}
/**
* A step is a part of a [StoryDefinition].
* Used to manage workflow in a [StoryHandler].
*/
interface StoryStep {
/**
* The name of the step.
*/
val name: String
/**
* The custom answer for this step.
* When returning a null value,
* it means that the step is not able to answer to the current request.
*
* Default implementation returns null.
*/
fun answer(): T.() -> Any? = { null }
/**
* Returns [intent] or the [StoryDefinition.mainIntent] if [intent] is null.
*/
val baseIntent: IntentAware get() = intent ?: stepToIntentRepository[this] ?: error("no intent for $this")
/**
* The main intent of the step.
* If not null and if the current intent is equals to [intent],
* this step will be automatically selected to be the current step.
*/
val intent: IntentAware? get() = null
/**
* Same behaviour than [intent] in the rare case when the step handle more than one intent.
*/
val otherStarterIntents: Set get() = emptySet()
/**
* The secondary intents of this step. If detected and if the current step is this step,
* the current step remains this step.
*/
val secondaryIntents: Set get() = emptySet()
/**
* Does this Step has to be selected from the Bus?
* This method is called if [StoryHandlerBase.checkPreconditions] does not call [BotBus.end].
* If this functions returns true, the step is selected and remaining steps are not tested.
*/
fun selectFromBus(): BotBus.() -> Boolean = { false }
/**
* Does this Step has to be automatically selected from the action context?
* if returns true, the step is selected.
*/
fun selectFromAction(userTimeline: UserTimeline, dialog: Dialog, action: Action, intent: Intent?): Boolean =
intent != null && selectFromActionAndEntityStepSelection(action, intent) ?: supportStarterIntent(intent)
/**
* Does this Step has to be automatically selected from the dialog context?
* if returns true, the step is selected.
*/
fun selectFromDialog(userTimeline: UserTimeline, dialog: Dialog, intent: Intent?): Boolean =
intent != null && selectFromDialogAndEntityStepSelection(dialog, intent) ?: supportStarterIntent(intent)
/**
* Does this step hast to be selected from its [entityStepSelection]?
* Returns null if there is no [entityStepSelection].
*/
fun selectFromActionAndEntityStepSelection(action: Action, intent: Intent? = null): Boolean? =
entityStepSelection?.let { e ->
if (intent != null && this.intent != null && !supportStarterIntent(intent)) false
else if (e.value == null) action.hasEntity(e.entityRole)
else action.hasEntityPredefinedValue(e.entityRole, e.value)
}
/**
* Does this step hast to be selected from its [entityStepSelection]?
* Returns null if there is no [entityStepSelection].
*/
fun selectFromDialogAndEntityStepSelection(dialog: Dialog, intent: Intent? = null): Boolean? =
entityStepSelection?.let { e ->
if (intent != null && this.intent != null && !supportStarterIntent(intent)) false
else if (e.value == null) dialog.state.hasEntity(e.entityRole)
else dialog.state.hasEntityPredefinedValue(e.entityRole, e.value)
}
/**
* Does this step support this intent as starter intent?
*/
fun supportStarterIntent(i: Intent): Boolean =
intent?.wrap(i) == true || otherStarterIntents.any { it.wrap(i) }
/**
* Does this step support this intent?
*/
fun supportIntent(i: Intent): Boolean = supportStarterIntent(i) || secondaryIntents.any { it.wrap(i) }
/**
* The optional children of the step.
*/
val children: Set> get() = emptySet()
/**
* Flag indicating if it's the step has no children.
*/
val hasNoChildren: Boolean get() = children.isEmpty()
/**
* If not null, entity has to be set in the current action to trigger the step.
*/
val entityStepSelection: EntityStepSelection? get() = null
/**
* The step metrics.
*/
val metrics: List get() = emptyList()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy