【问题标题】:Cannot get conversationState property value inside a setTimeOut无法在 setTimeOut 中获取 conversationState 属性值
【发布时间】:2019-04-23 17:01:48
【问题描述】:

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

我在对话框的构造函数中创建了一个 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);
  }
}

超时结束后,我收到此错误,notif 是 undefined:

(node:47504) UnhandledPromiseRejectionWarning: TypeError: Cannot perform 'get' on a proxy that has been revoked
    at ConversationState.load (c:\Users\Montacer\Desktop\qt-bot\node_modules\botbuilder\node_modules\botbuilder-core\src\botState.ts:84:48)
    at BotStatePropertyAccessor.get (c:\Users\Montacer\Desktop\qt-bot\node_modules\botbuilder\node_modules\botbuilder-core\src\botStatePropertyAccessor.ts:97:43)
    at Timeout.setTimeout [as _onTimeout] (c:\Users\Montacer\Desktop\qt-bot\dialogs\reservation.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。如果用户在 50000 毫秒内没有响应,您是否尝试发送通知?

标签: javascript azure botframework


【解决方案1】:

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

index.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 客户端向我们刚刚在上一步中创建的端点发出带有会话 id 作为 URL 参数的 get 请求。

当用户响应下一个提示时,将响应值更新为 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);
            }
        });
    }
}

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

【讨论】:

    【解决方案2】:

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

    const conversationState = new ConversationState(memmoryStorage);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-26
      • 1970-01-01
      相关资源
      最近更新 更多