- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 V4 的机器人框架。使用 API 调用,我试图获取数据并将其显示给用户,我定义了一个自定义方法来从 API 捕获数据并对其进行预处理,然后通过瀑布对话框将其发送给用户,并且我将此方法设为异步,并且在调用它的地方使用 await。
我面临的问题有两种情况 -
public static async Task<string>
GetEPcallsDoneAsync(ConversationData conversationData)
{
LogWriter.LogWrite("Info: Acessing end-points");
string responseMessage = null;
try
{
conversationData.fulFillmentMap = await
AnchorUtil.GetFulfillmentAsync(xxxxx);//response from API call to get data
if (conversationData.fulFillmentMap == null || (conversationData.fulFillmentMap.ContainsKey("status") && conversationData.fulFillmentMap["status"].ToString() != "200"))
{
responseMessage = "Sorry, something went wrong. Please try again later!";
}
else
{
conversationData.NLGresultMap = await
AnchorUtil.GetNLGAsync(conversationData.fulFillmentMap ,xxxx);//API call to get response to be displayed
if (conversationData.errorCaptureDict.ContainsKey("fulfillmentError") || conversationData.NLGresultMap.ContainsKey("NLGError"))
{
responseMessage = "Sorry, something went wrong:( Please try again later!!!";
}
else
{
responseMessage = FormatDataResponse(conversationData.NLGresultMap["REPLY"].ToString()); //response message
}
}
return responseMessage;
}
catch (HttpRequestException e)
{
LogWriter.LogWrite("Error: " + e.Message);
System.Console.WriteLine("Error: " + e.Message);
return null;
}
}
private async Task<DialogTurnResult> DoProcessInvocationStep(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
conversationData.index = 0; //var for some other purpose
conversationData.result = await
AnchorUtil.GetEPcallsDoneAsync(conversationData);
await _conversationStateAccessor.SetAsync(stepContext.Context, conversationData, cancellationToken);
return await stepContext.NextAsync(cancellationToken);
}
public class TopLevelDialog : ComponentDialog
{
private readonly IStatePropertyAccessor<ConversationData> _conversationStateAccessor;
ConversationData conversationData;
public TopLevelDialog(ConversationState conversationState)
: base(nameof(TopLevelDialog))
{
_conversationStateAccessor = conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ReviewSelectionDialog(conversationState));
AddDialog(new ESSelectionDialog());
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
StartSelectionStepAsync,
GetESResultStep,
DoProcessInvocationStep,
ResultStepAsync,
IterationStepAsync
}));
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> StartSelectionStepAsync (WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
conversationData = await _conversationStateAccessor.GetAsync(stepContext.Context, () => new ConversationData());
//code for functionality
await _conversationStateAccessor.SetAsync(stepContext.Context, conversationData, cancellationToken);
return await stepContext.NextAsync(null, cancellationToken);
}
//other dialog steps
}
最佳答案
您的两个问题可能都源于同一件事。您不能申报 conversationData
作为类级属性。您会遇到这样的并发问题,因为每个用户都会覆盖 conversationData
对于每个其他用户。您必须重新申报 conversationData
在每个步骤函数中。
例如,User A
启动瀑布对话框并进行到一半。 conversationData
在这一点上是正确的,并且完全代表了它应该做的。
现在 User B
开始对话。在 StartSelectionStepAsync
,他们只是重置 conversationData
因为conversationData = await _conversationStateAccessor.GetAsync(stepContext.Context, () => new ConversationData());
为了大家并且所有用户共享相同的 conversationData
因为 ConversationData conversationData;
.
所以现在,当 User A
继续他们的谈话,conversationData
将为空/空。
应该如何保存状态
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { Channels, MessageFactory } = require('botbuilder');
const {
AttachmentPrompt,
ChoiceFactory,
ChoicePrompt,
ComponentDialog,
ConfirmPrompt,
DialogSet,
DialogTurnStatus,
NumberPrompt,
TextPrompt,
WaterfallDialog
} = require('botbuilder-dialogs');
const { UserProfile } = require('../userProfile');
const ATTACHMENT_PROMPT = 'ATTACHMENT_PROMPT';
const CHOICE_PROMPT = 'CHOICE_PROMPT';
const CONFIRM_PROMPT = 'CONFIRM_PROMPT';
const NAME_PROMPT = 'NAME_PROMPT';
const NUMBER_PROMPT = 'NUMBER_PROMPT';
const USER_PROFILE = 'USER_PROFILE';
const WATERFALL_DIALOG = 'WATERFALL_DIALOG';
/**
* This is a "normal" dialog, where userState is stored properly using the accessor, this.userProfile.
* In this dialog example, we create the userProfile using the accessor in the first step, transportStep.
* We then pass prompt results through the remaining steps using step.values.
* In the final step, summaryStep, we save the userProfile using the accessor.
*/
class UserProfileDialogNormal extends ComponentDialog {
constructor(userState) {
super('userProfileDialogNormal');
this.userProfileAccessor = userState.createProperty(USER_PROFILE);
this.addDialog(new TextPrompt(NAME_PROMPT));
this.addDialog(new ChoicePrompt(CHOICE_PROMPT));
this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT));
this.addDialog(new NumberPrompt(NUMBER_PROMPT, this.agePromptValidator));
this.addDialog(new AttachmentPrompt(ATTACHMENT_PROMPT, this.picturePromptValidator));
this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
this.transportStep.bind(this),
this.nameStep.bind(this),
this.nameConfirmStep.bind(this),
this.ageStep.bind(this),
this.pictureStep.bind(this),
this.confirmStep.bind(this),
this.saveStep.bind(this)
]));
this.initialDialogId = WATERFALL_DIALOG;
}
/**
* The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system.
* If no dialog is active, it will start the default dialog.
* @param {*} turnContext
* @param {*} accessor
*/
async run(turnContext, accessor) {
const dialogSet = new DialogSet(accessor);
dialogSet.add(this);
const dialogContext = await dialogSet.createContext(turnContext);
const results = await dialogContext.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dialogContext.beginDialog(this.id);
}
}
async transportStep(step) {
// Get the userProfile if it exists, or create a new one if it doesn't.
const userProfile = await this.userProfileAccessor.get(step.context, new UserProfile());
// Pass the userProfile through step.values.
// This makes it so we don't have to call this.userProfileAccessor.get() in every step.
step.values.userProfile = userProfile;
// Skip this step if we already have the user's transport.
if (userProfile.transport) {
// ChoicePrompt results will show in the next step with step.result.value.
// Since we don't need to prompt, we can pass the ChoicePrompt result manually.
return await step.next({ value: userProfile.transport });
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
// Running a prompt here means the next WaterfallStep will be run when the user's response is received.
return await step.prompt(CHOICE_PROMPT, {
prompt: 'Please enter your mode of transport.',
choices: ChoiceFactory.toChoices(['Car', 'Bus', 'Bicycle'])
});
}
async nameStep(step) {
// Retrieve the userProfile from step.values.
const userProfile = step.values.userProfile;
// Set the transport property of the userProfile.
userProfile.transport = step.result.value;
// Pass the userProfile through step.values.
// This makes it so we don't have to call this.userProfileAccessor.get() in every step.
step.values.userProfile = userProfile;
// Skip the prompt if we already have the user's name.
if (userProfile.name) {
// We pass in a skipped bool so we know whether or not to send messages in the next step.
return await step.next({ value: userProfile.name, skipped: true });
}
return await step.prompt(NAME_PROMPT, 'Please enter your name.');
}
async nameConfirmStep(step) {
// Retrieve the userProfile from step.values and set the name property
const userProfile = step.values.userProfile;
// If userState is working correctly, we'll have userProfile.transport from the previous step.
if (!userProfile || !userProfile.transport) {
throw new Error(`transport property does not exist in userProfile.\nuserProfile:\n ${ JSON.stringify(userProfile) }`);
}
// Text prompt results normally end up in step.result, but if we skipped the prompt, it will be in step.result.value.
userProfile.name = step.result.value || step.result;
// step.values.userProfile.name is already set by reference, so there's no need to set it again to pass it to the next step.
// We can send messages to the user at any point in the WaterfallStep. Only do this if we didn't skip the prompt.
if (!step.result.skipped) {
await step.context.sendActivity(`Thanks ${ step.result }.`);
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
// Skip the prompt if we already have the user's age.
if (userProfile.age) {
return await step.next('yes');
}
return await step.prompt(CONFIRM_PROMPT, 'Do you want to give your age?', ['yes', 'no']);
}
async ageStep(step) {
// Retrieve the userProfile from step.values
const userProfile = step.values.userProfile;
// If userState is working correctly, we'll have userProfile.name from the previous step.
if (!userProfile || !userProfile.name) {
throw new Error(`name property does not exist in userProfile.\nuserProfile:\n ${ JSON.stringify(userProfile) }`);
}
// Skip the prompt if we already have the user's age.
if (userProfile.age) {
// We pass in a skipped bool so we know whether or not to send messages in the next step.
return await step.next({ value: userProfile.age, skipped: true });
}
if (step.result) {
// User said "yes" so we will be prompting for the age.
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
const promptOptions = { prompt: 'Please enter your age.', retryPrompt: 'The value entered must be greater than 0 and less than 150.' };
return await step.prompt(NUMBER_PROMPT, promptOptions);
} else {
// User said "no" so we will skip the next step. Give -1 as the age.
return await step.next(-1);
}
}
async pictureStep(step) {
// Retrieve the userProfile from step.values and set the age property
const userProfile = step.values.userProfile;
// We didn't set any additional properties on userProfile in the previous step, so no need to check for them here.
// Confirm prompt results normally end up in step.result, but if we skipped the prompt, it will be in step.result.value.
userProfile.age = step.result.value || step.result;
// step.values.userProfile.age is already set by reference, so there's no need to set it again to pass it to the next step.
if (!step.result.skipped) {
const msg = userProfile.age === -1 ? 'No age given.' : `I have your age as ${ userProfile.age }.`;
// We can send messages to the user at any point in the WaterfallStep. Only send it if we didn't skip the prompt.
await step.context.sendActivity(msg);
}
// Skip the prompt if we already have the user's picture.
if (userProfile.picture) {
return await step.next(userProfile.picture);
}
if (step.context.activity.channelId === Channels.msteams) {
// This attachment prompt example is not designed to work for Teams attachments, so skip it in this case
await step.context.sendActivity('Skipping attachment prompt in Teams channel...');
return await step.next(undefined);
} else {
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
var promptOptions = {
prompt: 'Please attach a profile picture (or type any message to skip).',
retryPrompt: 'The attachment must be a jpeg/png image file.'
};
return await step.prompt(ATTACHMENT_PROMPT, promptOptions);
}
}
async confirmStep(step) {
// Retrieve the userProfile from step.values and set the picture property
const userProfile = step.values.userProfile;
// If userState is working correctly, we'll have userProfile.age from the previous step.
if (!userProfile || !userProfile.age) {
throw new Error(`age property does not exist in userProfile.\nuserProfile:\n ${ JSON.stringify(userProfile) }`);
}
userProfile.picture = (step.result && typeof step.result === 'object' && step.result[0]) || 'no picture provided';
// step.values.userProfile.picture is already set by reference, so there's no need to set it again to pass it to the next step.
let msg = `I have your mode of transport as ${ userProfile.transport } and your name as ${ userProfile.name }`;
if (userProfile.age !== -1) {
msg += ` and your age as ${ userProfile.age }`;
}
msg += '.';
await step.context.sendActivity(msg);
if (userProfile.picture && userProfile.picture !== 'no picture provided') {
try {
await step.context.sendActivity(MessageFactory.attachment(userProfile.picture, 'This is your profile picture.'));
} catch (err) {
await step.context.sendActivity('A profile picture was saved but could not be displayed here.');
}
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await step.prompt(CONFIRM_PROMPT, { prompt: 'Would you like me to save this information?' });
}
async saveStep(step) {
if (step.result) {
// Get the current profile object from user state.
const userProfile = step.values.userProfile;
// Save the userProfile to userState.
await this.userProfileAccessor.set(step.context, userProfile);
await step.context.sendActivity('User Profile Saved.');
} else {
// Ensure the userProfile is cleared
await this.userProfileAccessor.set(step.context, {});
await step.context.sendActivity('Thanks. Your profile will not be kept.');
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return await step.endDialog();
}
async agePromptValidator(promptContext) {
// This condition is our validation rule. You can also change the value at this point.
return promptContext.recognized.succeeded && promptContext.recognized.value > 0 && promptContext.recognized.value < 150;
}
async picturePromptValidator(promptContext) {
if (promptContext.recognized.succeeded) {
var attachments = promptContext.recognized.value;
var validImages = [];
attachments.forEach(attachment => {
if (attachment.contentType === 'image/jpeg' || attachment.contentType === 'image/png') {
validImages.push(attachment);
}
});
promptContext.recognized.value = validImages;
// If none of the attachments are valid images, the retry prompt should be sent.
return !!validImages.length;
} else {
await promptContext.context.sendActivity('No attachments received. Proceeding without a profile picture...');
// We can return true from a validator function even if Recognized.Succeeded is false.
return true;
}
}
}
module.exports.UserProfileDialogNormal = UserProfileDialogNormal;
关于botframework - 聊天机器人中的空响应和跨 session 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60303969/
我在 C# 中为我的聊天机器人使用 botframework channel 直达线,最近我添加了 Bing 语音,用于文本到语音,反之亦然,但是当用户说话时,除了纯文本之外,是否还有音频文件作为消息
我有一个来自用户上传图片的字节数组。我需要使用 Bot-framework 在 Skype 和其他 channel 上将此字节数组呈现为图像 最佳答案 图像可以作为 base64 编码发送:
我正在使用自定义的 Microsoft Bot 框架 WebChat Client .当我的机器人无法为用户提供解决方案时,它能够与代理进行实时聊天服务。 我需要允许代理“先睹为快”当前正在输入 We
我正在运行 Windows 10 并想安装 BotFramework-Emulator。 链接 here说 Download packages for Mac, Windows, and Linux
我有一个机器人,它具有消息传递扩展功能。我遵循了一个例子 here .但是我没有使用卡片 View ,而是使用嵌入式 Web View 来显示我的自定义 UI(这是一个托管在 S3 中的 React
我需要维护聊天历史记录,并在页面刷新或关闭并打开窗口后将它们加载回窗口中。 问题:按钮/轮播/自适应卡/英雄卡事件/属性未加载(即,当我单击按钮或任何事件时,操作未发生)。描述:为了达到要求,我有两个
我正在尝试 Microsoft 的新 Botframework。使用 \n 发送消息时,消息中没有换行符。我该如何解决? 在 Telegram API 中有一个名为 parse_mode (https
我发布了一个使用机器人框架构建的机器人,但我不知道如何将它连接到企业内部网络上的 Skype for Business。 有谁知道是什么流程吗? ----- 埃德 最佳答案 尚不支持 Skype fo
我已经在 .Net Framework WebAPI 中使用 Microsoft.BotFramework 实现了一个聊天机器人,它正在运行,现在我想将它移植到 .Net Core 2,与其他项目共享
关闭。这个问题需要更多focused .它目前不接受答案。 想改善这个问题吗?更新问题,使其仅关注一个问题 editing this post . 9 个月前关闭。 Improve this ques
我在英雄卡中使用粗体文本时遇到了一些问题,目前我是这样构建我的英雄卡的: var workloadCard = new HeroCard { Title = $
nodejs中bot框架的输入指示器 你好, 我正在使用 microsoft bot framework v4, node js,我需要在聊天机器人中实现输入。在发送原始响应之前,机器人应该响应 li
我正在创建一个在电报 chanel 上运行的机器人。当用户与机器人聊天时,它需要知道谁在聊天。所以他们必须在我的服务器上进行身份验证。 我想机器人会发送一个按钮,其中包含我的身份验证端点的链接(例如:
我正在打开(使用 OpenUrl)我控制的外部网页。在加载此网页期间,我希望从服务器直接关闭团队模式,或者通过调用团队客户端重新发送带有参数的消息,以便机器人可以以关闭响应进行响应(我正在工作)。 我
我正在尝试发送 Teams notification与 hero card或 Adaptive Card .我可以发送一条简单的短信作为 notification . 我不知道如何包装 Hero ca
我创建了一个简单的 android 应用程序,它使用 restfull jersey WS 通过 JSON 格式发送消息 我应该在连接机器人的应用程序中输入哪个 URL? 该机器人如何接收消息并发回响
我正在创建一个允许用户与现有 Web 服务交互的机器人。我希望用户能够与我的机器人开始对话并检索针对他们的个性化信息。 如何将机器人用户与其现有帐户相匹配?并非所有 channel 都使用电子邮件地址
我用我的机器人尝试了这种电子邮件模式,但是在Skype上发布时不起作用。它总是拒绝任何正常的有效电子邮件。 [Describe("email "), Prompt(QuestionWhatIsYour
我有一个名为“表单搜索”的对话框,它有一个自适应卡片。当我单击提交按钮时,控件不会转到下一个流程,而是以错误结束。但如果使用英雄卡,按钮点击会触发下一个流程。可能是什么问题。 session.mess
我开发了一个聊天机器人并将其部署在 Skype 上。我有一件新东西要添加到机器人中。 如果用户在 bot 中请求办公室出租车,则 bot 必须接受用户输入(如目的地、emp-name 等)并向特定邮件
我是一名优秀的程序员,十分优秀!