Beispiel #1
0
        /// <summary>
        /// Runs dialog system in the context of an ITurnContext.
        /// </summary>
        /// <param name="context">turn context.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>result of the running the logic against the activity.</returns>
        public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default)
        {
            var botStateSet = new BotStateSet();

            // Preload TurnState with DM TurnState.
            foreach (var pair in InitialTurnState)
            {
                context.TurnState.Set(pair.Key, pair.Value);
            }

            // register DialogManager with TurnState.
            context.TurnState.Set(this);

            if (ConversationState == null)
            {
                ConversationState = context.TurnState.Get <ConversationState>() ?? throw new InvalidOperationException($"Unable to get an instance of {nameof(ConversationState)} from turnContext.");
            }
            else
            {
                context.TurnState.Set(ConversationState);
            }

            botStateSet.Add(ConversationState);

            if (UserState == null)
            {
                UserState = context.TurnState.Get <UserState>();
            }
            else
            {
                context.TurnState.Set(UserState);
            }

            if (UserState != null)
            {
                botStateSet.Add(UserState);
            }

            // create property accessors
            var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LastAccess);
            var lastAccess         = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false);

            // Check for expired conversation
            if (ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)ExpireAfter))
            {
                // Clear conversation state
                await ConversationState.ClearStateAsync(context, cancellationToken).ConfigureAwait(false);
            }

            lastAccess = DateTime.UtcNow;
            await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken).ConfigureAwait(false);

            // get dialog stack
            var dialogsProperty = ConversationState.CreateProperty <DialogState>(_dialogStateProperty);
            var dialogState     = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken).ConfigureAwait(false);

            // Create DialogContext
            var dc = new DialogContext(Dialogs, context, dialogState);

            // Call the common dialog "continue/begin" execution pattern shared with the classic RunAsync extension method
            var turnResult = await DialogExtensions.InternalRunAsync(context, _rootDialogId, dc, StateConfiguration, cancellationToken).ConfigureAwait(false);

            // save BotState changes
            await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false);

            return(new DialogManagerResult {
                TurnResult = turnResult
            });
        }
Beispiel #2
0
        /// <summary>
        /// Runs dialog system in the context of an ITurnContext.
        /// </summary>
        /// <param name="context">turn context.</param>
        /// <param name="cancellationToken">cancelation token.</param>
        /// <returns>result of the running the logic against the activity.</returns>
        public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            var botStateSet = new BotStateSet();

            // preload turnstate with DM turnstate
            foreach (var pair in this.TurnState)
            {
                context.TurnState.Set(pair.Key, pair.Value);
            }

            if (this.ConversationState == null)
            {
                this.ConversationState = context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException(nameof(this.ConversationState));
            }
            else
            {
                context.TurnState.Set(this.ConversationState);
            }

            botStateSet.Add(this.ConversationState);

            if (this.UserState == null)
            {
                this.UserState = context.TurnState.Get <UserState>();
            }

            if (this.UserState != null)
            {
                botStateSet.Add(this.UserState);
            }

            // create property accessors
            var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LASTACCESS);
            var lastAccess         = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false);

            // Check for expired conversation
            var now = DateTime.UtcNow;

            if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter))
            {
                // Clear conversation state
                await ConversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false);
            }

            lastAccess = DateTime.UtcNow;
            await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false);

            // get dialog stack
            var         dialogsProperty = ConversationState.CreateProperty <DialogState>(this.dialogStateProperty);
            DialogState dialogState     = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false);

            // Create DialogContext
            var dc = new DialogContext(this.Dialogs, context, dialogState);

            // get the dialogstatemanager configuration
            var dialogStateManager = new DialogStateManager(dc);
            await dialogStateManager.LoadAllScopesAsync(cancellationToken).ConfigureAwait(false);

            dc.Context.TurnState.Add(dialogStateManager);

            DialogTurnResult turnResult = null;

            // Loop as long as we are getting valid OnError handled we should continue executing the actions for the turn.
            //
            // NOTE: We loop around this block because each pass through we either complete the turn and break out of the loop
            // or we have had an exception AND there was an OnError action which captured the error.  We need to continue the
            // turn based on the actions the OnError handler introduced.
            while (true)
            {
                try
                {
                    if (dc.ActiveDialog == null)
                    {
                        // start root dialog
                        turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        // Continue execution
                        // - This will apply any queued up interruptions and execute the current/next step(s).
                        turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false);

                        if (turnResult.Status == DialogTurnStatus.Empty)
                        {
                            // restart root dialog
                            turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false);
                        }
                    }

                    // turn successfully completed, break the loop
                    break;
                }
                catch (Exception err)
                {
                    // fire error event, bubbling from the leaf.
                    var handled = await dc.EmitEventAsync(DialogEvents.Error, err, bubble : true, fromLeaf : true, cancellationToken : cancellationToken).ConfigureAwait(false);

                    if (!handled)
                    {
                        // error was NOT handled, throw the exception and end the turn. (This will trigger the Adapter.OnError handler and end the entire dialog stack)
                        throw;
                    }
                }
            }

            // save all state scopes to their respective botState locations.
            await dc.GetState().SaveAllChangesAsync(cancellationToken).ConfigureAwait(false);

            // save botstate changes
            await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false);

            // send trace of memory
            var snapshot      = dc.GetState().GetMemorySnapshot();
            var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State");
            await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false);

            return(new DialogManagerResult()
            {
                TurnResult = turnResult
            });
        }
        /// <summary>
        /// Runs dialog system in the context of an ITurnContext.
        /// </summary>
        /// <param name="context">turn context.</param>
        /// <param name="cancellationToken">cancelation token.</param>
        /// <returns>result of the running the logic against the activity.</returns>
        public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            BotStateSet       botStateSet       = new BotStateSet();
            ConversationState conversationState = this.ConversationState ?? context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException($"{nameof(ConversationState)} is not found in the turn context. Have you called adapter.UseState() with a configured ConversationState object?");
            UserState         userState         = this.UserState ?? context.TurnState.Get <UserState>();

            if (conversationState != null)
            {
                botStateSet.Add(conversationState);
            }

            if (userState != null)
            {
                botStateSet.Add(userState);
            }

            // create property accessors
            var lastAccessProperty = conversationState.CreateProperty <DateTime>(LASTACCESS);
            var lastAccess         = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false);

            // Check for expired conversation
            var now = DateTime.UtcNow;

            if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter))
            {
                // Clear conversation state
                await conversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false);
            }

            lastAccess = DateTime.UtcNow;
            await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false);

            // get dialog stack
            var         dialogsProperty = conversationState.CreateProperty <DialogState>(DIALOGS);
            DialogState dialogState     = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false);

            // Create DialogContext
            var dc = new DialogContext(this.dialogSet, context, dialogState);

            // set DSM configuration
            dc.SetStateConfiguration(this.StateConfiguration ?? DialogStateManager.CreateStandardConfiguration(conversationState, userState));

            // load scopes
            await dc.GetState().LoadAllScopesAsync(cancellationToken).ConfigureAwait(false);

            DialogTurnResult turnResult = null;

            if (dc.ActiveDialog == null)
            {
                // start root dialog
                turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false);
            }
            else
            {
                // Continue execution
                // - This will apply any queued up interruptions and execute the current/next step(s).
                turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false);

                if (turnResult.Status == DialogTurnStatus.Empty)
                {
                    // restart root dialog
                    turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false);
                }
            }

            // save all state scopes to their respective stores.
            await dc.GetState().SaveAllChangesAsync(cancellationToken).ConfigureAwait(false);

            // save botstate changes
            await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false);

            // send trace of memory
            var snapshot      = dc.GetState().GetMemorySnapshot();
            var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State");
            await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false);

            return(new DialogManagerResult()
            {
                TurnResult = turnResult
            });
        }
        /// <summary>
        /// Runs dialog system in the context of an ITurnContext.
        /// </summary>
        /// <param name="context">turn context.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>result of the running the logic against the activity.</returns>
        public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default)
        {
            var botStateSet = new BotStateSet();

            // Preload TurnState with DM TurnState.
            foreach (var pair in InitialTurnState)
            {
                context.TurnState.Set(pair.Key, pair.Value);
            }

            // register DialogManager with TurnState.
            context.TurnState.Set(this);

            if (ConversationState == null)
            {
                ConversationState = context.TurnState.Get <ConversationState>() ?? throw new InvalidOperationException($"Unable to get an instance of {nameof(ConversationState)} from turnContext.");
            }
            else
            {
                context.TurnState.Set(ConversationState);
            }

            botStateSet.Add(ConversationState);

            if (UserState == null)
            {
                UserState = context.TurnState.Get <UserState>();
            }
            else
            {
                context.TurnState.Set(UserState);
            }

            if (UserState != null)
            {
                botStateSet.Add(UserState);
            }

            // create property accessors
            var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LastAccess);
            var lastAccess         = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false);

            // Check for expired conversation
            if (ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)ExpireAfter))
            {
                // Clear conversation state
                await ConversationState.ClearStateAsync(context, cancellationToken).ConfigureAwait(false);
            }

            lastAccess = DateTime.UtcNow;
            await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken).ConfigureAwait(false);

            // get dialog stack
            var dialogsProperty = ConversationState.CreateProperty <DialogState>(_dialogStateProperty);
            var dialogState     = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken).ConfigureAwait(false);

            // Create DialogContext
            var dc = new DialogContext(Dialogs, context, dialogState);

            // promote initial TurnState into dc.services for contextual services
            foreach (var service in dc.Services)
            {
                dc.Services[service.Key] = service.Value;
            }

            // map TurnState into root dialog context.services
            foreach (var service in context.TurnState)
            {
                dc.Services[service.Key] = service.Value;
            }

            // get the DialogStateManager configuration
            var dialogStateManager = new DialogStateManager(dc, StateConfiguration);
            await dialogStateManager.LoadAllScopesAsync(cancellationToken).ConfigureAwait(false);

            dc.Context.TurnState.Add(dialogStateManager);

            DialogTurnResult turnResult = null;

            // Loop as long as we are getting valid OnError handled we should continue executing the actions for the turn.
            //
            // NOTE: We loop around this block because each pass through we either complete the turn and break out of the loop
            // or we have had an exception AND there was an OnError action which captured the error.  We need to continue the
            // turn based on the actions the OnError handler introduced.
            var endOfTurn = false;

            while (!endOfTurn)
            {
                try
                {
                    if (context.TurnState.Get <IIdentity>(BotAdapter.BotIdentityKey) is ClaimsIdentity claimIdentity && SkillValidation.IsSkillClaim(claimIdentity.Claims))
                    {
                        // The bot is running as a skill.
                        turnResult = await HandleSkillOnTurnAsync(dc, cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        // The bot is running as root bot.
                        turnResult = await HandleBotOnTurnAsync(dc, cancellationToken).ConfigureAwait(false);
                    }

                    // turn successfully completed, break the loop
                    endOfTurn = true;
                }
 public DefaultTestAdapter(BotStateSet botStateSet)
     : base(sendTraceActivity: false)
 {
     Use(new EventDebuggerMiddleware());
     Use(new AutoSaveStateMiddleware(botStateSet));
 }
Beispiel #6
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            try
            {
                services.AddBot <AADv2Bot>(options =>
                {
                    options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);

                    // The CatchExceptionMiddleware provides a top-level exception handler for your bot.
                    // Any exceptions thrown by other Middleware, or by your OnTurn method, will be
                    // caught here. To facillitate debugging, the exception is sent out, via Trace,
                    // to the emulator. Trace activities are NOT displayed to users, so in addition
                    // an "Ooops" message is sent.


                    // The Memory Storage used here is for local bot debugging only. When the bot
                    // is restarted, anything stored in memory will be gone.

                    IStorage dataStore = new MemoryStorage();
                    var convoState     = new ConversationState(dataStore);
                    var userState      = new UserState(dataStore);
                    // The File data store, shown here, is suitable for bots that run on
                    // a single machine and need durable state across application restarts.
                    // IStorage dataStore = new FileStorage(System.IO.Path.GetTempPath());

                    // For production bots use the Azure Table Store, Azure Blob, or
                    // Azure CosmosDB storage provides, as seen below. To include any of
                    // the Azure based storage providers, add the Microsoft.Bot.Builder.Azure
                    // Nuget package to your solution. That package is found at:
                    //      https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/

                    // IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureTableStorage("AzureTablesConnectionString", "TableName");
                    // IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage("AzureBlobConnectionString", "containerName");

                    options.State.Add(convoState);
                    options.State.Add(userState);

                    // Add State to BotStateSet Middleware (that require auto-save)
                    // The BotStateSet Middleware forces state storage to auto-save when the Bot is complete processing the message.
                    // Note: Developers may choose not to add all the State providers to this Middleware if save is not required.
                    var stateSet = new BotStateSet(options.State.ToArray());
                    options.Middleware.Add(stateSet);
                });
                services.AddSingleton <AADv2BotAccessors>(sp =>
                {
                    var options = sp.GetRequiredService <IOptions <BotFrameworkOptions> >().Value;
                    if (options == null)
                    {
                        throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
                    }

                    var conversationState = options.State.OfType <ConversationState>().FirstOrDefault();
                    var userState         = options.State.OfType <UserState>().FirstOrDefault();
                    if (conversationState == null)
                    {
                        throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors.");
                    }

                    // Create Custom State Property Accessors
                    // State Property Accessors enable components to read and write individual properties, without having to
                    // pass the entire State object.
                    var accessors = new AADv2BotAccessors
                    {
                        CommandState            = userState.CreateProperty <string>(AADv2BotAccessors.CommandStateName),
                        ConversationDialogState = conversationState.CreateProperty <DialogState>(AADv2BotAccessors.DialogStateName),
                    };

                    return(accessors);
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
        public async Task BotStateSet_Chain()
        {
            var storage = new MemoryStorage();

            // setup userstate
            var userState    = new UserState(storage);
            var userProperty = userState.CreateProperty <int>("userCount");

            // setup convState
            var convState    = new ConversationState(storage);
            var convProperty = convState.CreateProperty <int>("convCount");
            var bss          = new BotStateSet()
                               .Use(userState)
                               .Use(convState);
            var adapter = new TestAdapter()
                          .Use(bss);

            const int          USER_INITITAL_COUNT        = 100;
            const int          CONVERSATION_INITIAL_COUNT = 10;
            BotCallbackHandler botLogic = async(context, cancellationToken) =>
            {
                // get userCount and convCount from botStateSet
                var userCount = await userProperty.GetAsync(context, () => USER_INITITAL_COUNT).ConfigureAwait(false);

                var convCount = await convProperty.GetAsync(context, () => CONVERSATION_INITIAL_COUNT).ConfigureAwait(false);

                if (context.Activity.Type == ActivityTypes.Message)
                {
                    if (context.Activity.Text == "get userCount")
                    {
                        await context.SendActivityAsync(context.Activity.CreateReply($"{userCount}"));
                    }
                    else if (context.Activity.Text == "get convCount")
                    {
                        await context.SendActivityAsync(context.Activity.CreateReply($"{convCount}"));
                    }
                }

                // increment userCount and set property using accessor.  To be saved later by BotStateSet
                userCount++;
                await userProperty.SetAsync(context, userCount);

                // increment convCount and set property using accessor.  To be saved later by BotStateSet
                convCount++;
                await convProperty.SetAsync(context, convCount);
            };

            await new TestFlow(adapter, botLogic)
            .Send("test1")
            .Send("get userCount")
            .AssertReply((USER_INITITAL_COUNT + 1).ToString())
            .Send("get userCount")
            .AssertReply((USER_INITITAL_COUNT + 2).ToString())
            .Send("get convCount")
            .AssertReply((CONVERSATION_INITIAL_COUNT + 3).ToString())
            .StartTestAsync();

            // new adapter on new conversation
            var bss2 = new BotStateSet()
                       .Use(userState)
                       .Use(convState);

            adapter = new TestAdapter(new ConversationReference
            {
                ChannelId    = "test",
                ServiceUrl   = "https://test.com",
                User         = new ChannelAccount("user1", "User1"),
                Bot          = new ChannelAccount("bot", "Bot"),
                Conversation = new ConversationAccount(false, "convo2", "Conversation2")
            })
                      .Use(bss2);

            await new TestFlow(adapter, botLogic)
            .Send("get userCount")
            .AssertReply((USER_INITITAL_COUNT + 4).ToString(), "user count should continue on new conversation")
            .Send("get convCount")
            .AssertReply((CONVERSATION_INITIAL_COUNT + 1).ToString(), "conversationCount for conversation2 should be reset")
            .StartTestAsync();
        }