首页 > 解决方案 > 所有用户 C# 的 Botframework v4 相同状态

问题描述

我正在尝试将我的 botframework APP v3 迁移到 v4,我按照 microsoft 文档中的说明进行操作,除了状态控制之外一切正常,在我的机器人中,状态是为所有用户和所有渠道(直线、电报)共享的。

我在 bot v4 中看到这个主题UserProfile 状态在用户之间持续存在,但即使强制使用 Web 随机 ID,状态仍然在用户之间共享。

我的 bot 是使用 Microsoft 提供的 Core bot 模板创建的。你可以在下面看到我的 C# 代码。由于瀑布对话框中只有业务规则,我将跳过发布 Maindialog,但如果需要,我也可以发布。

谢谢您的帮助。

启动.cs

public class Startup
{
    private const string BotOpenIdMetadataKey = "BotOpenIdMetadata";
    private static readonly AzureBlobStorage _UserStorage = new AzureBlobStorage("DefaultEndpointsProtocol=https;AccountName=evabotwebbe9c;AccountKey=wsU18jImJVSX+2vq6l0flx9Ou83hcDyrxie0tUN7fjxMV3bfHhYJuFobmq0h/TXU/pBBOvfpGVUlHtuqn7cNVw==;EndpointSuffix=core.windows.net", "botuserstate");
    private static readonly AzureBlobStorage _ConversationStorage = new AzureBlobStorage("DefaultEndpointsProtocol=https;AccountName=evabotwebbe9c;AccountKey=wsU18jImJVSX+2vq6l0flx9Ou83hcDyrxie0tUN7fjxMV3bfHhYJuFobmq0h/TXU/pBBOvfpGVUlHtuqn7cNVw==;EndpointSuffix=core.windows.net", "botconversationstate");        

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        if (!string.IsNullOrEmpty(Configuration[BotOpenIdMetadataKey]))
            ChannelValidation.OpenIdMetadataUrl = Configuration[BotOpenIdMetadataKey];

        // Create the credential provider to be used with the Bot Framework Adapter.
        services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

        // Create the Bot Framework Adapter with error handling enabled.
        services.AddSingleton<IBotFrameworkHttpAdapter, WebSocketEnabledHttpAdapter>();           

        // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
        services.AddSingleton<IStorage, AzureBlobStorage>();

        // Create the User state. (Used in this bot's Dialog implementation.)            
        var userState = new UserState(_UserStorage);

        // Create the Conversation state. (Used by the Dialog system itself.)            
        var conversationState = new ConversationState(_ConversationStorage);

        // Add the states as singletons
        services.AddSingleton(userState);
        services.AddSingleton(conversationState);            

        services.AddSingleton(sp =>
        {
            userState = sp.GetService<UserState>();
            conversationState = sp.GetService<ConversationState>();

            // Create the Conversation state. (Used by the Dialog system itself.)
            return new BotStateSet(userState, conversationState);
        });


        // The Dialog that will be run by the bot.
        services.AddSingleton<MainDialog>();            

        // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
        services.AddTransient<IBot, DialogAndWelcomeBot<MainDialog>>();
    }        

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseWebSockets();
        //app.UseHttpsRedirection();
        app.UseMvc();
    }
}

DialogAndWelcomeBot.cs

public class DialogAndWelcomeBot<T> : DialogBot<T> where T : Dialog
{
    protected IConfiguration Configuration;
    private BotState _conversationState;
    private BotState _userState;
    protected IStatePropertyAccessor<DialogState> accessor;        
    protected Dialogo dialogo;
    protected Dialog Dialog;

    public DialogAndWelcomeBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger, IConfiguration configuration)
        : base(conversationState, userState, dialog, logger)
    {            
        Configuration = configuration;
        dialogo = new Dialogo();
        _conversationState = conversationState;
        _userState = userState;
        Dialog = dialog;
    }

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        foreach (var member in membersAdded)
        {
            // Greet anyone that was not the target (recipient) of this message.
            // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                dialogo = await VerificarDialogo();                    
                await IniciarDialogo(turnContext, cancellationToken);
            }
        }
    }

private async Task IniciarDialogo(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        accessor = _conversationState.CreateProperty<DialogState>("DialogState");

        var dialog = Dialog;
        var dialogSet = new DialogSet(accessor);            
        dialogSet.Add(dialog);

        var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
        var results = await dialogContext.ContinueDialogAsync(cancellationToken);

        if (results.Status == DialogTurnStatus.Empty)
        {
            var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
            var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

            var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
            var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());

            await dialogContext.BeginDialogAsync(dialog.Id, dialogo, cancellationToken);
        }            
    }

对话机器人.cs

public class DialogBot<T> : ActivityHandler where T : Dialog
{
    private readonly Dialog Dialog;
    private BotState _conversationState;
    private BotState _userState;
    protected readonly ILogger Logger;        

    public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
    {
        _conversationState = conversationState;
        _userState = userState;
        Dialog = dialog;
        Logger = logger;            
    }

    public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        await base.OnTurnAsync(turnContext, cancellationToken);

        // Client notifying this bot took to long to respond (timed out)
        if (turnContext.Activity.Code == EndOfConversationCodes.BotTimedOut)
        {
            await turnContext.SendActivityAsync(MessageFactory.Text("Desculpe-me, mas parece que a conexão com a internet está ruim e isto irá afetar o desempenho da nossa conversa."), cancellationToken);
            return;
        }

        await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
        await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);

    }

    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        Logger.LogInformation("Message Activity log");
        // Run the Dialog with the new message Activity.

        await Dialog.Run(turnContext, _conversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);            
    }        
}

DialogExtensions.cs

public static class DialogExtensions
{
    public static async Task Run(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
    {
        var dialogSet = new DialogSet(accessor);
        dialogSet.Add(dialog);

        var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
        var results = await dialogContext.ContinueDialogAsync(cancellationToken);

        if (results.Status == DialogTurnStatus.Empty)
        {                
            await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
        }
        else
        {                
            await dialogContext.ReplaceDialogAsync(dialog.Id, null, cancellationToken);
        }
    }
}

将 Jscript 代码转换为 HTML 文件。

<script>
    function guid() {
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
            s4() + '-' + s4() + s4() + s4();
    }

    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    }        

    var user = {
        id: guid().toUpperCase(),
        name: 'User-' + Math.floor((1 + Math.random()) * 10000)
    };
    var botConnection = new BotChat.DirectLine({
        token: 'MyToken',
        user: user,
        webSocket: 'true'
    });          

    const speechOptions = {
     speechRecognizer: new BotChat.Speech.BrowserSpeechRecognizer(),
     speechSynthesizer: new BotChat.Speech.BrowserSpeechSynthesizer()
     };

    BotChat.App({
        user: user,            
        botConnection: botConnection,
        speechOptions: speechOptions,
        bot: { id: 'Bot'+ Math.floor((1 + Math.random()) * 10000)},
        resize: 'detect',            
    }, document.getElementById("bot"));
    botConnection
        .postActivity({
            from: user,
            name: 'none',
            type: 'event',
            value: 'none'
        })
        .subscribe(function (id) {
            console.log('"trigger setUserIdEvent" sent');
        });
</script>

标签: c#azurebotframeworkazure-bot-service

解决方案


是什么_UserStorage?...UserState该类应该使用您的IStorage实现,它会自动执行,因为您已经注入了一个IStorage如果您只是注入 UserState,di 将确保它是使用已经存在的 IStorage 创建的:services.AddSingleton<UserState>();

这里还不足以确定出了什么问题。

另外,这里发生了什么?

if (member.Id != turnContext.Activity.Recipient.Id)
{
     dialogo = await VerificarDialogo();                    
     await IniciarDialogo(turnContext, cancellationToken);
}

推荐阅读