BotFramework v4: как обмениваться информацией с WebChat в ComponentDialog? - PullRequest
0 голосов
/ 31 января 2020

Я использовал BotFramework SDK v4 (. NET) и WebChat v4 (React minizable-web-chat)

Контекст находится в ComponentDialog с WaterfallSteps:

  1. бот отправляет ChoicePrompt «Хотите, чтобы бот узнал ваше местоположение? ДА / НЕТ»
  2. Если ДА, бот отправляет событие «locationRequest»
  3. Веб-чат получает событие и показывает Кнопка GetLocation
  4. Когда пользователь нажимает на кнопку, он отправляет геолокации боту, и диалог продолжается

ComponentDialog:

public class FindNearestAgencyDialog : ComponentDialog
{
    private string choice;
    private string zipCode = string.Empty;

    public FindNearestAgencyDialog(string id)
    : base(id)
    {
        InitialDialogId = Id;
        WaterfallStep[] waterfallSteps = new WaterfallStep[]
        {
            ZipCodeStepAsync,
            LocationConfirmAsync,
            ZipCodeConfirmAsync,
            FindNearestAgencyAsync
        };

        AddDialog(new WaterfallDialog(Id, waterfallSteps));
        AddDialog(new TextPrompt(nameof(TextPrompt)));
        AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    }

    private static async Task<DialogTurnResult> ZipCodeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        return await stepContext.PromptAsync(nameof(ChoicePrompt),
            new PromptOptions
            {
                Prompt = stepContext.Context.Activity.CreateReply("Authoize bot to get your location?"),
                Choices = new[] { new Choice { Value = "Decline" }, new Choice { Value = "Accept" } }.ToList()
            });
    }

    private async Task<DialogTurnResult> LocationConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        choice = (stepContext.Result as FoundChoice)?.Value;
        if (choice == "Decline")
        {
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("What's your zip code ?") }, cancellationToken);
        }
        else
        {
            Activity activity = new Activity
            {
                Type = ActivityTypes.Event,
                Name = "locationRequest"
            };
            await stepContext.Context.SendActivityAsync(activity, cancellationToken);
            return await stepContext.NextAsync();
        }
    }

    private async Task<DialogTurnResult> ZipCodeConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        if (choice == "Refuser")
        {
            zipCode = (string)stepContext.Result;
            var msg = $"Je recherche l'agence la plus proche de votre code postal : {zipCode}.";
            await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
        }
        return await stepContext.NextAsync();
    }

    private async Task<DialogTurnResult> FindNearestAgencyAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {    // DO SOEMTHING
        // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
        return await stepContext.EndDialogAsync(stepContext.Result, cancellationToken: cancellationToken);
    }
}

WebChat ( на основе React minizable-web-chat):

import React from 'react';
import { createStore, createStyleSet } from 'botframework-webchat';
import WebChat from './WebChat';
import './fabric-icons-inline.css';
import './MinimizableWebChat.css';
export default class extends React.Component{

constructor(props) {
    super(props);

    this.handleFetchToken = this.handleFetchToken.bind(this);
    this.handleMaximizeButtonClick = this.handleMaximizeButtonClick.bind(this);
    this.handleMinimizeButtonClick = this.handleMinimizeButtonClick.bind(this);
    this.handleSwitchButtonClick = this.handleSwitchButtonClick.bind(this);
    this.handleLocationButtonClick = this.handleLocationButtonClick.bind(this);

    const store = createStore({}, ({ dispatch }) => next => action => {
      if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
        dispatch({
          type: 'WEB_CHAT/SEND_EVENT',
          payload: {
            name: 'webchat/join',
          }
        });
      }
      else if(action.type === 'DIRECT_LINE/INCOMING_ACTIVITY'){
        if (action.payload.activity.name === 'locationRequest') {
          this.setState(() => ({
            locationRequested: true
          }));
        }
      }
      return next(action);
    });

    this.state = {
      minimized: true,
      newMessage: false,
      locationRequested:false,
      side: 'right',
      store,
      styleSet: createStyleSet({
        backgroundColor: 'Transparent'
      }),
      token: 'token'
    };
  }

  async handleFetchToken() {
    if (!this.state.token) {
      const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
      const { token } = await res.json();

      this.setState(() => ({ token }));
    }
  }

  handleMaximizeButtonClick() {
    this.setState(() => ({
      minimized: false,
      newMessage: false
    }));
  }

  handleMinimizeButtonClick() {
    this.setState(() => ({
      minimized: true,
      newMessage: false
    }));
  }

  handleSwitchButtonClick() {
    this.setState(({ side }) => ({
      side: side === 'left' ? 'right' : 'left'
    }));
  }

  handleLocationButtonClick(){
    var x = document.getElementById("display");
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(showPosition);
      this.setState(() => ({
        locationRequested: false
      }));
          }
          else {
            x.innerHTML = "Geolocation API is not supported by this browser.";
        }

    function showPosition(position) {
        x.innerHTML = "Latitude: " + position.coords.latitude + "<br>Longitude: " + position.coords.longitude;
    }
  }

render() {
    const { state: {
      minimized,
      newMessage,
      locationRequested,
      side,
      store,
      styleSet,
      token
    } } = this;

    return (
      <div className="minimizable-web-chat">
        {
          minimized ?
            <button
              className="maximize"
              onClick={ this.handleMaximizeButtonClick }
            >
              <span className={ token ? 'ms-Icon ms-Icon--MessageFill' : 'ms-Icon ms-Icon--Message' } />
              {
                newMessage &&
                  <span className="ms-Icon ms-Icon--CircleShapeSolid red-dot" />
              }
            </button>
          :
            <div
              className={ side === 'left' ? 'chat-box left' : 'chat-box right' }
            >
              <header>
                <div className="filler" />
                <button
                  className="switch"
                  onClick={ this.handleSwitchButtonClick }
                >
                  <span className="ms-Icon ms-Icon--Switch" />
                </button>
                <button
                  className="minimize"
                  onClick={ this.handleMinimizeButtonClick }
                >
                  <span className="ms-Icon ms-Icon--ChromeMinimize" />
                </button>
              </header>
              <WebChat
                className="react-web-chat"
                onFetchToken={ this.handleFetchToken }
                store={ store }
                styleSet={ styleSet }
                token={ token }
              />
              {
                locationRequested ?
                <div>
                  <p id="display"></p>
                  <button onClick={this.handleLocationButtonClick}>
                    Gélolocation
                  </button>
                </div>
              :
              <div></div>
              }
            </div>
        }
      </div>
    );
  }
}

Как мы можем приостановить ComponentDialog, чтобы позволить пользователю нажать кнопку в веб-чате, отправить геолокацию боту, а затем возобновить ComponentDialog?

...