Skip to content

Commit

Permalink
feat(core): added dialog engine hooks APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
slvnperron committed May 9, 2018
1 parent 9880a78 commit 5e214ff
Showing 1 changed file with 91 additions and 1 deletion.
92 changes: 91 additions & 1 deletion packages/core/botpress/src/dialog/engine.js
@@ -1,6 +1,7 @@
import _ from 'lodash'
import Promise from 'bluebird'
import { VM } from 'vm2'
import mware from 'mware'

const loggerShim = { debug: () => {} }
const callSubflowRegex = /(.+\.flow\.json)\s?@?\s?(.+)?/i // e.g. './login.flow.json' or './login.flow.json @ username'
Expand All @@ -25,6 +26,79 @@ class DialogEngine {
this.functions = {}
this.functionMetadataProvider = null

/**
* @typedef {Function} DialogEngine~DialogMiddleware
* @param {object} ctx A mutable context object
* @param {function} next Call this to continue processing
*/

/**
* Middleware triggered before a new session is started.
* > **Note:** This middleware allows you to alter `ctx.flowName` to change
* > which flow will be selected to start the conversation.
* @function DialogEngine#onBeforeCreated
* @param {DialogEngine~DialogMiddleware} middleware
* @example
bp.dialogEngine.onBeforeCreated((ctx, next) => {
ctx.flowName = 'example.flow.json'
next()
})
*/
this.onBeforeCreated = mware()

/**
* Middleware triggered **after** a new session is started.
* `ctx` is not mutable.
* @function DialogEngine#onAfterCreated
* @param {DialogEngine~DialogMiddleware} middleware
* @example
bp.dialogEngine.onAfterCreated((ctx, next) => {
// Do something here
next()
})
*/
this.onAfterCreated = mware()

/**
* Middleware triggered **before** a conversation is ended for any reason.
* `ctx` is not mutable.
* @function DialogEngine#onBeforeEnd
* @param {DialogEngine~DialogMiddleware} middleware
* @example
bp.dialogEngine.onBeforeEnd((ctx, next) => {
// Do something here
next()
})
*/
this.onBeforeEnd = mware()

/**
* Middleware triggered **before** a node is entered (before the `onEnter` execution).
* > **⚠️ Warn:** It is **not** recommended to mutate `ctx.node` for now, it might break in a future version of Botpress.
* @function DialogEngine#onBeforeNodeEnter
* @param {DialogEngine~DialogMiddleware} middleware
* @example
bp.dialogEngine.onBeforeNodeEnter((ctx, next) => {
// Do something here
next()
})
*/
this.onBeforeNodeEnter = mware()

/**
* Middleware triggered **before** a conversation/session times out.
* > **Note:** You can't prevent it from timing out at this point.
* > You also can't change the timeout behavior/location at this time.
* @function DialogEngine#onBeforeSessionTimeout
* @param {DialogEngine~DialogMiddleware} middleware
* @example
bp.dialogEngine.onBeforeSessionTimeout((ctx, next) => {
// Do something here
next()
})
*/
this.onBeforeSessionTimeout = mware()

flowProvider.on('flowsChanged', () => Object.assign(this, { flows: [], flowsLoaded: false }))
}

Expand Down Expand Up @@ -275,6 +349,9 @@ class DialogEngine {
onError = fn => this.errorHandlers.push(fn)

async _processTimeout(stateId, userState, context, event) {
const beforeCtx = { stateId }
await Promise.fromCallback(callback => this.onBeforeSessionTimeout.run(beforeCtx, callback))

const currentNodeTimeout = _.get(DialogEngine._findNode(context.currentFlow, context.node), 'timeoutNode')
const currentFlowTimeout = _.get(context, 'currentFlow.timeoutNode')
const fallbackTimeoutNode = DialogEngine._findNode(context.currentFlow, 'timeout')
Expand Down Expand Up @@ -358,6 +435,9 @@ class DialogEngine {

await this._setContext(stateId, context)

const beforeCtx = { stateId, node }
await Promise.fromCallback(callback => this.onBeforeNodeEnter.run(beforeCtx, callback))

if (node.onEnter) {
this._trace('!!', 'ENTR', '', context, userState)
userState = await this._processInstructions(node.onEnter, userState, event, context)
Expand Down Expand Up @@ -414,16 +494,23 @@ class DialogEngine {
}

async _endFlow(stateId) {
const beforeCtx = { stateId }
await Promise.fromCallback(callback => this.onBeforeEnd.run(beforeCtx, callback))

this._trace('--', 'ENDF', '', null, null)
await this.stateManager.deleteState(stateId, ['context'])

return null
}

async _getOrCreateContext(stateId) {
let state = await this._getContext(stateId)

if (!state || !state.currentFlow) {
const flow = this._findFlow(this.defaultFlow, true)
const beforeCtx = { stateId, flowName: this.defaultFlow }
await Promise.fromCallback(callback => this.onBeforeCreated.run(beforeCtx, callback))

const flow = this._findFlow(beforeCtx.flowName, true)

if (!flow) {
throw new Error(`Could not find the default flow "${this.defaultFlow}"`)
Expand All @@ -435,6 +522,9 @@ class DialogEngine {
}

await this._setContext(stateId, state)

const afterCtx = { ...beforeCtx }
await Promise.fromCallback(callback => this.onAfterCreated.run(afterCtx, callback))
}

return state
Expand Down

0 comments on commit 5e214ff

Please sign in to comment.