无法在 setTimeOut 中获取对话状态属性值



我正在使用 botBuilder SDK 4.3 for Node js。

我在对话框的构造函数中创建了一个 conversationState 属性。 在某些对话步骤中,我为该属性设置了一个值。

在另一步中,我尝试在像 So 这样的 setTimeOut 中获取该属性的值。

// Imports ...
class Reservation extends ComponentDialog {
constructor(id, conversationState, userState, dialogProps) {
super(id);
this.id = id;
this.conversationState = conversationState;
this.userState = userState;
this.initialDialogId = CONFIGS.MAIN_DIALOG_ID;
this.reservationNotifProp = conversationState.createProperty(
"reservationNotif"
);
... 
this.addDialog(
new WaterfallDialog(this.initialDialogId, [
this.askCheckIn.bind(this),
this.askCheckout.bind(this)
this.askNights.bind(this),
this.validateDates.bind(this),
.....
]
);
}
async askCheckIn (step) { ... }
async askCheckout (step) { ... }
async askNights (step) {
// ...
this.reservationNotifProp.set(step.context, false);
await this.conversationState.saveChanges(step.context);
const ref = this;
setTimeout(async () => {
const notif = await this.reservationNotifProp.get(step.context);
if (notif) {
console.log("Send Notif ...");
}
}, 50000);
}
async validateDates(step) {
// ...
this.reservationNotifProp.set(step.context, true);
await this.conversationState.saveChanges(step.context);
}
}

超时完成后,我收到此错误,notifundefined

(node:47504) UnhandledPromiseRejectionWarning: TypeError: Cannot perform 'get' on a proxy that has been revoked
at ConversationState.load (c:UsersMontacerDesktopqt-botnode_modulesbotbuildernode_modulesbotbuilder-coresrcbotState.ts:84:48)
at BotStatePropertyAccessor.get (c:UsersMontacerDesktopqt-botnode_modulesbotbuildernode_modulesbotbuilder-coresrcbotStatePropertyAccessor.ts:97:43)
at Timeout.setTimeout [as _onTimeout] (c:UsersMontacerDesktopqt-botdialogsreservation.js:366:63)
at ontimeout (timers.js:498:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:290:5)
warning.js:18
(node:47504) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
warning.js:18
(node:47504) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

有什么解决方案吗?

由于某种原因,BotFramework 不能很好地处理回调,这就是您收到"无法在已撤销的代理上执行'get'"错误的原因。一个解决方案(但很复杂)是创建一个主动消息 API 端点,从超时回调调用对它的请求,然后从主动消息执行其余的机器人调用。我建议在开始使用下面的代码之前先查看主动消息传递示例。

索引.js文件

我们将向 Restify 服务器添加一个/api/notify 端点,该端点将在超时结束时被命中。我建议向机器人添加一个方法来处理发送主动消息,以便可以保留机器人中包含的所有状态和对话框元素,而不是将它们提升到索引文件。请注意,必须将适配器作为参数传递给机器人。

let bot = new Bot(adapter, conversationState, userState);
...
server.get('/api/notify/:conversationId', (req, res) => {
bot.sendProactiveMessages(req.params.conversationId);
res.send(204);
});

对话

在对话框的这一步中,我们将向用户配置文件添加响应属性 - 您也可以将其添加到对话状态 - 并将默认值设置为 false。然后,无需配置回调来访问状态并向用户发送消息,只需使用 Axios 或 Request 等 HTTP 客户端发出 get 请求,并将会话 ID 作为 URL 参数发送到我们在上述步骤中刚刚创建的终结点。

当用户响应下一个提示时,将响应值更新为 true,以便我们可以判断用户是否从主动消息中响应。

async captureName(step) {
const profile = await this.profileAccessor.get(step.context);
profile.name = step.result;
profile.responded = false;
this.profileAccessor.set(step.context, profile);
const { conversation: { id }} = TurnContext.getConversationReference(step.context.activity);
setTimeout(() => {
axios.get(`http://localhost:3978/api/notify/${id}`)
.then(() => {})
.catch(error => console.log(error));
}, 60000);
return await step.next();
}
async promptForCity(step) {
return await step.prompt(CITY_PROMPT, "What city are your from?");
}
async captureCity(step) {
const profile = await this.profileAccessor.get(step.context);
profile.city = step.result;
profile.responded = true;
this.profileAccessor.set(step.context, profile);
return await step.next();
}

机器人

在主动消息传递示例中,所有对话引用都存储在一个对象中。我们可以使用 get 请求中的会话 ID 作为键值来检索会话引用,并使用该引用继续对话。从主动消息中,可以发送活动、访问和更新状态、取消对话框以及可以使用机器人执行的所有其他正常功能。

class Bot extends ActivityHandler{
constructor(adapter, conversationState, userState) {
super();
this.adapter = adapter;
this.conversationReferences = {};
this.conversationState = conversationState;
this.userState = userState;
// Configure properties
this.profileAccessor = this.userState.createProperty(USER_PROFILE);
this.dialogState = this.conversationState.createProperty(DIALOG_STATE);

}
async sendProactiveMessages(conversationId) {
const conversationReference = this.conversationReferences[conversationId];
conversationReference && await this.adapter.continueConversation(conversationReference, async context => {
const { responded } = await this.profileAccessor.get(context);
if (!responded) {
const dc = await this.dialogs.createContext(context);
await dc.cancelAllDialogs();
await context.sendActivity('Sorry you took too long to respond..');
await this.conversationState.saveChanges(context);
}
});
}
}

我知道这对于一个简单的动作来说有点复杂,但我希望这有所帮助!

请检查对话状态是否已在index.js中声明,如下所示。

const conversationState = new ConversationState(memmoryStorage);

最新更新