Как поддерживать беседу в чате с бот-фреймворком более часа?

Я просмотрел документацию по botframework-webchat и не смог найти никакой документации о том, как правильно обрабатывать разговоры продолжительностью более 1 часа. Эта ситуация наиболее вероятна, если веб-страница остается неактивной в фоновом режиме в течение длительного периода времени.

Прямое соединение поддерживается, пока веб-чат остается активным на веб-странице. Проблема возникает после обновления страницы.

Первоначальное краткосрочное решение - сохранить соответствующую информацию о разговоре в хранилище сеанса, например токен. Проблема в том, что токен для разговора обновляется каждые 15 минут. Обновленный токен должен быть получен, чтобы поддерживать диалог при обновлении страницы.

Я уверен, что существует хитрое решение для получения обновленного токена из объекта клиента прямой линии с помощью обратного вызова события.

В идеале я ищу чистый фреймворк, разработанный для решения этой ситуации.

Хотя рабочее решение лучше, чем отсутствие решения.

Соответствующая ссылка: https://github.com/microsoft/BotFramework-WebChat

Спасибо.


person chris00010010    schedule 05.02.2020    source источник
comment
Принятие / голосование ответа служит большему сообществу Stack Overflow и всем, у кого есть аналогичный вопрос. Если вы считаете, что моего ответа было достаточно, примите его и проголосуйте за него. Если нет, дайте мне знать, чем еще я могу помочь!   -  person Steven Kanberg    schedule 07.02.2020


Ответы (3)


Вы можете добиться этого, внедрив файлы cookie на стороне клиента. вы можете установить срок действия куки-файлов на 60 минут, и вы можете использовать водяной знак, чтобы ваш чат оставался постоянным в течение одного часа. Передача файлов cookie в службу ботов и из нее.

person ankit chauhan    schedule 13.02.2020

Вы можете добиться этого, установив «токен-сервер». В приведенном ниже примере я запускаю это локально, когда разрабатываю / тестирую своего бота.

Вы можете использовать любой пакет, какой захотите, однако я остановился на «restify», потому что я включил его в index.js файл моего бота. Я просто создаю новый сервер отдельно от сервера бота и назначаю ему собственный порт. Затем, когда я запускаю бота, он тоже запускается автоматически. Поместите свои appIds, appPasswords и секреты в файл .env.

Затем на своей веб-странице, на которой размещен ваш бот, просто вызовите конечную точку, чтобы получить токен. Вы также заметите, что код проверяет, существует ли уже токен. Если да, то он устанавливает интервал с таймером для обновления токена. Интервал в 1500000 мс настроен на запуск до истечения срока действия токена (1800000 мс). Таким образом, токен всегда обновляется. (Просто пришло мне в голову: может быть, разумно записать оставшееся время и количество времени, которое прошло, если пользователь ушел, чтобы установить интервал на точное число, чтобы он обновлялся, когда он должен. В противном случае интервал будет сброшен, когда время истечения будет намного меньше.)

Также я добавил закомментированный код. Это если вы хотите, чтобы ваши разговоры продолжались после обновления страницы или пользователя, уходящего и возвращающегося. Таким образом, текущие разговоры не теряются, а токен остается активным. Может быть необязательно в зависимости от ваших потребностей, но хорошо работает с вышеперечисленным.

Надеюсь на помощь!


Сервер токенов

/**
 * Creates token server
 */
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');

const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });

const corsToken = corsMiddleware({
  origins: [ '*' ]
});

// Create HTTP server.
let server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
  extended: false
}));

server.listen(process.env.port || process.env.PORT || 3500, function() {
  console.log(`\n${ server.name } listening to ${ server.url }.`);
});

// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
  // userId must start with `dl_`
  const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
  const options = {
    method: 'POST',
    uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
    headers: {
      'Authorization': `Bearer ${ process.env.directLineSecret }`
    },
    json: {
      user: {
        ID: userId
      }
    }
  };
  request.post(options, (error, response, body) => {
    // response.statusCode = 400;
    if (!error && response.statusCode < 300) {
      res.send(body);
      console.log('Someone requested a token...');
    } else if (response.statusCode === 400) {
      res.send(400);
    } else {
      res.status(500);
      res.send('Call to retrieve token from DirectLine failed');
    }
  });
});

// Listen for incoming requests.
server.post('/directline/refresh', (req, res) => {
  // userId must start with `dl_`
  const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
  const options = {
    method: 'POST',
    uri: 'https://directline.botframework.com/v3/directline/tokens/refresh',
    headers: {
      'Authorization': `Bearer ${ req.body.token }`,
      'Content-Type': 'application/json'
    },
    json: {
      user: {
        ID: userId
      }
    }
  };
  request.post(options, (error, response, body) => {
    if (!error && response.statusCode < 300) {
      res.send(body);
      console.log('Someone refreshed a token...');
    } else {
      res.status(500);
      res.send('Call to retrieve token from DirectLine failed');
    }
  });
});

webchat.html

<script>
  (async function () {
    let { token, conversationId } = sessionStorage;

    [...]

    if ( !token || errorCode === "TokenExpired" ) {
      let res = await fetch( 'http://localhost:3500/directline/token', { method: 'POST' } );

      const { token: directLineToken, conversationId, error } = await res.json();
      // sessionStorage[ 'token' ] = directLineToken;
      // sessionStorage[ 'conversationId' ] = conversationId;
      token = directLineToken;
    }

    if (token) {
      await setInterval(async () => {
        let res = await fetch( 'http://localhost:3500/directline/refresh', {
          method: 'POST',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify( { token: token } )
        } );
        const { token: directLineToken, conversationId } = await res.json();
        // sessionStorage[ 'token' ] = directLineToken;
        // sessionStorage[ 'conversationId' ] = conversationId;
        token = directLineToken;
      }, 1500000)
    }

    // if ( conversationId ) {
    //   let res = await fetch( `https://webchat.botframework.com/v3/directline/conversations/${ conversationId }`, {
    //     method: 'GET',
    //     headers: {
    //       'Authorization': `Bearer ${ token }`,
    //       'Content-Type': 'application/json'
    //     },
    //   } );

    //   const { conversationId: conversation_Id, error } = await res.json();
    //   if(error) {
    //     console.log(error.code)
    //     errorCode = error.code;
    //   }
    //   conversationId = conversation_Id;
    // }

    [...]

    window.ReactDOM.render(
      <ReactWebChat
        directLine={ window.WebChat.createDirectLine({ token });
      />
    ),
    document.getElementById( 'webchat' );
  });
</script>
person Steven Kanberg    schedule 07.02.2020
comment
Кстати, справочные документы: здесь. - person Steven Kanberg; 07.02.2020
comment
Если у вас есть веб-страница, которая живет дольше 15 минут, то компонент веб-чата реагирует на внутреннее обновление токена. В этом примере рассматривается только прямое обновление токена. Приведенный выше ответ не извлекает обновленный токен из компонента веб-чата. Токен, хранящийся в sessionstorage, уже был обновлен компонентом веб-чата через 15 минут. Это приведет к тому, что исходный токен в хранилище сеансов станет недействительным. - person chris00010010; 10.02.2020
comment
Даже если токен, хранящийся в хранилище сеанса, обновлен. Маркер по-прежнему автоматически обновляется внутренним компонентом веб-чата. Это приведет к нескольким одновременным вызовам для обновления токена. Можно ли обновлять один и тот же токен несколько раз? Я предполагаю, что токены поддерживаются в единой линейной цепочке для каждого разговора. - person chris00010010; 10.02.2020
comment
Моя ситуация похожа на то, что представлено здесь. github.com/microsoft/ Разница в том, что возможность подключения не потеряно. Мне просто нужен обновленный токен из внутреннего обновления. - person chris00010010; 10.02.2020
comment
Это близко к решению, которое я нашел, но не совсем. - person chris00010010; 13.02.2020
comment
@ chris00010010, если само соединение потеряно ... то обновленный / внутренний обновленный токен не имеет смысла. Потому что теперь вам нужно повторно подключиться с помощью Recoonect API, который должен об этом позаботиться. поправьте меня, если я ошибаюсь ?? - person kumar kundan; 10.07.2020

Решение заключалось в хранении идентификатора разговора в хранилище сеанса вместо токена. После обновления страницы будет получен новый токен.

https://github.com/microsoft/BotFramework-WebChat/issues/2899

https://github.com/microsoft/BotFramework-WebChat/issues/2396#issuecomment-530931579

Это решение работает, но не оптимально. Лучшим решением было бы получить активный токен в прямом объекте и сохранить его в хранилище сеанса. Проблема в том, что на данный момент не существует способа чисто получить обновленный токен из объекта прямой линии.

person chris00010010    schedule 13.02.2020