c# - 所有用户 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>
解决方案
是什么_UserStorage
?...UserState
该类应该使用您的IStorage
实现,它会自动执行,因为您已经注入了一个IStorage
如果您只是注入 UserState,di 将确保它是使用已经存在的 IStorage 创建的:services.AddSingleton<UserState>();
这里还不足以确定出了什么问题。
另外,这里发生了什么?
if (member.Id != turnContext.Activity.Recipient.Id)
{
dialogo = await VerificarDialogo();
await IniciarDialogo(turnContext, cancellationToken);
}
推荐阅读
- reactjs - 忽略初始 reducer 值并且只渲染一次
- json - 验证 JSON 文件是否匹配 TypeScript 接口
- sql - 获取没有 NULL 值的 SQL 不同行
- javascript - 将 NavBar 组件添加到 react-router-DOM 文件外部开关使我的内容都没有呈现
- angular - 如何在 Angular 8 中有效地捕获 textarea 值?
- apache-spark - Spark,EMR 上主节点的大小重要吗?
- java - 错误 java.lang.ClassNotFoundException: javax.enterprise.context.spi.Contextual
- tensorflow - TensorFlow 中如何添加张量?
- python - ValueError:不能使用“filtered_detections/map/while/strided_slice_1”作为“filtered_detections/map/while/ones/packed”的输入
- swift - 如何捕获自定义 UICollectionViewCell 的 didSelectItemAt