import { ConversationState, TurnContext, Promiseable } from 'botbuilder' import botbuilderReduxMiddleware, { getStore as getReduxStore } from 'botbuilder-redux/dist' import { Middleware, Store, createStore, applyMiddleware, compose as composeFunc } from 'redux' import rootReducer from '../reducers' import { NlpResult, Ability, ConvoState, WolfState, IncomingSlotData, SetSlotDataFunctions } from '../types' import intake from '../stages/intake' import fillSlot from '../stages/fillSlot' import evaluate from '../stages/evaluate' import execute from '../stages/execute' import outtake, { OuttakeResult } from '../stages/outtake' import { fillSlot as fillSlotAction, enableSlot, disableSlot, setSlotDone, addSlotToOnFillStack } from '../actions' const userMessageDataKey = Symbol('userMessageDataKey') const wolfMessagesKey = Symbol('wolfMessageKey') /** * Create WolfStore * * @param middlewares * @param compose */ export const createWolfStore = ( middlewares?: Middleware[], compose?: any ) => ( wolfStateFromConvoState: {[key: string]: any} | null ) => { if (typeof middlewares === 'undefined') { middlewares = [] } if (typeof compose === 'undefined') { compose = composeFunc } const defaultWolfState = { messageData: null, slotStatus: [], slotData: [], abilityStatus: [], promptedSlotStack: [], focusedAbility: null, outputMessageQueue: [], filledSlotsOnCurrentTurn: [], abilitiesCompleteOnCurrentTurn: [], defaultAbility: null, runOnFillStack: [] } const state = wolfStateFromConvoState || defaultWolfState return createStore(rootReducer, state, compose(applyMiddleware(...middlewares))) } /** * wolf middleware * * @return Promise<> */ export default function initializeWolfStoreMiddleware( conversationStore: ConversationState, userMessageData: (context: TurnContext) => Promiseable, getAbilitiesFunc: (context: TurnContext) => Promiseable, defaultAbility: string, storeCreator: (wolfStateFromConvoState: {[key: string]: any} | null) => Store, getSlotDataFunc?: (context: TurnContext, setSlotFuncs: SetSlotDataFunctions) => Promiseable ) { return [ botbuilderReduxMiddleware(conversationStore, storeCreator, '__WOLF_STORE__'), { onTurn: async (context: TurnContext, next: () => any) => { if (context.activity.type !== 'message') { await next() } else { const store = getStore(context) const nlpResult: NlpResult = await userMessageData(context) const abilities: Ability[] = await getAbilitiesFunc(context) const convoState: ConvoState = conversationStore.get(context) || {} const incomingSlotData: IncomingSlotData[] = getSlotDataFunc ? await getSlotDataFunc(context, { setSlotValue: (abilityName, slotName, value) => { store.dispatch(fillSlotAction(slotName, abilityName, value)) }, setSlotEnabled: (abilityName, slotName, enable) => { if (enable) { store.dispatch(enableSlot({abilityName, slotName})) return } store.dispatch(disableSlot({abilityName, slotName})) }, setSlotDone: (abilityName, slotName, done) => { store.dispatch(setSlotDone({abilityName, slotName}, done)) }, fulfillSlot: (abilityName, slotName, value) => { store.dispatch(addSlotToOnFillStack({slotName, abilityName}, value)) } }) : [] intake(store, nlpResult, incomingSlotData, defaultAbility) fillSlot(store, convoState, abilities) evaluate(store, abilities, convoState) const executeResult = execute(store, convoState, abilities) if (executeResult) { const { runOnComplete, addMessage } = executeResult const messages = await runOnComplete() messages.forEach(addMessage) } const messagesObj = outtake(store) // save the messages in context.services context.services.set(wolfMessagesKey, messagesObj) await next() } } }] } export function getStore(context: TurnContext): Store { return getReduxStore(context, '__WOLF_STORE__') } export function getMessages(context: TurnContext): OuttakeResult { return context.services.get(wolfMessagesKey) }