Невозможно получить значение свойства dialogState внутри setTimeOut - PullRequest
2 голосов
/ 23 апреля 2019

Я использую BotBuilder SDK 4.3 для узла js.

Я создал свойство разговорState внутри конструктора диалога. На некоторых этапах диалога я устанавливаю значение для этого свойства.

На другом шаге я попытался получить значение этого свойства внутри setTimeOut, например, So.

// 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.

Какие-нибудь решения?

1 Ответ

4 голосов
/ 19 июня 2019

По какой-то причине BotFramework плохо работает с обратными вызовами, поэтому вы получаете сообщение об ошибке «Невозможно выполнить get для прокси-сервера, который был отозван». Решение, хотя и сложное, состоит в том, чтобы создать упреждающую конечную точку API сообщения, вызвать к ней запрос из обратного вызова тайм-аута, а затем выполнить остальные вызовы бота из упреждающего сообщения. Я бы рекомендовал взглянуть на образец Proactive Messaging , прежде чем приступить к работе с кодом ниже.

index.js file

Мы собираемся добавить конечную точку / 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. Затем вместо настройки обратного вызова для доступа к состоянию и сообщения пользователю, просто используйте HTTP-клиент, такой как Axios или Request, чтобы сделать запрос get с идентификатором диалога в качестве параметра 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();
}

Bot

В примере проактивного обмена сообщениями все ссылки на диалоги хранятся в объекте. Мы можем использовать идентификатор разговора из запроса get в качестве значения ключа для получения ссылки на разговор и использовать эту ссылку для продолжения диалога. Из активного сообщения вы можете отправлять действия, доступ и состояние обновления, отменять диалоги и все другие обычные функции, которые вы можете выполнять с ботом.

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);
            }
        });
    }
}

Я знаю, что это немного сложно для простого действия, но я надеюсь, что это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...