Bot Framework

De Banane Atomic
Révision datée du 22 octobre 2018 à 14:38 par Nicolas (discussion | contributions) (→‎Skype Web Contol)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigationAller à la recherche

Liens

Principe de fonctionnement

  • Le bot rejoint la conversation en envoyant une Activity ConversationUpdate du Bot Framework Service au bot.
  • Les utilisateurs rejoignent en envoyant une Activity ConversationUpdate du Bot Framework Service au bot.
  • Les utilisateurs envoient des Activity Message du Bot Framework Service au bot.
  • Le bot répond en envoyant des Activity Message du bot au Bot Framework Service.

Créer un bot dans Azure

Création des services

  1. Azure → All resources → Add → AI + Machine Learning → Web App Bot
    1. Pricing tier
      • F0 - gratuit - limité à 10.000 messages
      • S1 - 0,49$ / 1.000 messages
    2. Bot Template
      • Echo Bot
      • Basic Bot (Language Understanding, Bot Analytics, Storage, Web app
        Créé 4 services: Web App Bot, App Service, Storage account, Application Insights
      • Enterprise Bot (Basic Bot + CosmosDB, Dispatch, QnA Maker, Authentication, Content Moderator, App Insights, PowerBI)
      • Custom Assistant (Enterprise Bot + Linked Accounts ans Skills)
      • Language Understanding Bot (LUIS app)
      • QnA Bot
  2. Test: sélectionner le service Web App Bot → Test in Web Chat

Modifier le code du bot

  • Récupérer le code source: sélectionner le service Web App BotBuildDownload Bot source code
  • Compléter le fichier appsettings.json avec les infos se trouvant dans Web App BotApplication Settings
appsettings.json
{
  "botFilePath": "MyBot.bot",
  "botFileSecret": "..."
}
  • Lancer l'application : F5
  • Utiliser le botframework emulator : Url endpoint http://localhost:3978/api/messages

Publier les modifications sur Azure

  • clique-droit sur le projet → Publish
  • utiliser le mot de passe du fichier publish.cmd

Page de test

Azure → My Web App Bot → Channels

  1. Ajouter le channel DirectLine : Add a featured channel → Configure Direct Line chanel
  2. Copier les Secret Keys
Html.svg
<!DOCTYPE html>
<html>
  <head>
   <meta charset="UTF-8"> 
    <link href="https://cdn.botframework.com/botframework-webchat/latest/botchat.css" rel="stylesheet" />
  </head>
  <body>
    <div id="bot"/>
    <script src="https://cdn.botframework.com/botframework-webchat/latest/botchat.js"></script>
    <script>
      BotChat.App({
        directLine: { secret: '' },
        user: { id: '', name: ''},
        bot: { id: 'MyBot' },
        resize: 'detect'
      }, document.getElementById("bot"));
    </script>
  </body>
</html>

Projet C#

MyBot.cs
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
    var activity = turnContext.Activity;

    if (activity.Type == ActivityTypes.Message)
    {
        // Perform a call to LUIS to retrieve results for the current activity message.
        var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(turnContext, cancellationToken).ConfigureAwait(false);

        var topScoringIntent = luisResults?.GetTopScoringIntent();
        var topIntent = topScoringIntent.Value.intent;

        switch (topIntent)
        {
            case GreetingIntent:
                await turnContext.SendActivityAsync("Hello.");
                break;
            case HelpIntent:
                await turnContext.SendActivityAsync("Let me try to provide some help.");
                await turnContext.SendActivityAsync("I understand greetings, being asked for help, or being asked to cancel what I am doing.");
                break;
            case CancelIntent:
                await turnContext.SendActivityAsync("I have nothing to cancel.");
                break;
            case NoneIntent:
            default:
                // Help or no intent identified, either way, let's provide some help.
                // to the user
                await turnContext.SendActivityAsync("I didn't understand what you just said to me.");
                break;
        }
    }
    else if (activity.Type == ActivityTypes.ConversationUpdate)
    {
        if (activity.MembersAdded.Any())
        {
            // Iterate over all new members added to the conversation.
            foreach (var member in activity.MembersAdded)
            {
                // Greet anyone that was not the target (recipient) of this message.
                // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
                if (member.Id != activity.Recipient.Id)
                {
                    var welcomeCard = CreateAdaptiveCardAttachment();
                    var response = CreateResponse(activity, welcomeCard);
                    await turnContext.SendActivityAsync(response).ConfigureAwait(false);
                }
            }
        }
    }
}

old

Controllers/MessageController.cs
[BotAuthentication]
public class MessagesController : ApiController
{
    /// <summary>
    /// POST: api/Messages
    /// Receive a message from a user and reply to it
    /// </summary>
    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
        }
        else
        {
            HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

    private Activity HandleSystemMessage(Activity message)
    {
        if (message.Type == ActivityTypes.DeleteUserData)
        {
            // Implement user deletion here
            // If we handle user deletion, return a real message
        }
        else if (message.Type == ActivityTypes.ConversationUpdate)
        {
            // Handle conversation state changes, like members being added and removed
            // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
            // Not available in all channels
        }
        else if (message.Type == ActivityTypes.ContactRelationUpdate)
        {
            // Handle add/remove from contact lists
            // Activity.From + Activity.Action represent what happened
        }
        else if (message.Type == ActivityTypes.Typing)
        {
            // Handle knowing tha the user is typing
        }
        else if (message.Type == ActivityTypes.Ping)
        {
        }

        return null;
    }
Dialogs/RootDialog.cs
[Serializable]
public class RootDialog : IDialog<object>
{
    public Task StartAsync(IDialogContext context)
    {
        context.Wait(MessageReceivedAsync);

        return Task.CompletedTask;
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
    {
        var activity = await result as Activity;

        // calculate something for us to return
        int length = (activity.Text ?? string.Empty).Length;

        // return our reply to the user
        await context.PostAsync($"You sent {activity.Text} which was {length} characters");

        context.Wait(MessageReceivedAsync);
    }
}

BotFramework-Emulator

Permet de tester un projet ChatBot qui tourne en local.
Pour un projet déployé sur Azure, utiliser ngrok ou une page HTML de test avec la DirectLine.

endpoint http://localhost:3979/api/messages VS → Project → Properties → Web → Project Url + /api/messages
Microsoft App ID
(inutile pour test local)
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Azure → Bot Channels Registration App → Settings → Microsoft App ID
Microsoft App Password
(inutile pour test local)
Azure → Bot Channels Registration App → Settings → Microsoft App ID → Manage
Application Registration Portal → Bot Channels Registration App → Application Secrets → Password
%AppData%\botframework-emulator\botframework-emulator\server.json
"users": {
   "currentUserId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
   "usersById": {
       "default-user": {
           "id": "default-user",
           "name": "User"
       },
       "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx": {
           "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
           "name": "MyUser"
       }
   }

ngrok

Permet de tester un bot hébergé sur Azure.

Powershell.svg
.\ngrok.exe http -host-header=rewrite 9000
  • Azure → My Bot Channels Registration → modifier le endpoint du Bot avec l'adresse https fournie par ngrok + /api/messages ???
  • Dans l'emulateur, utiliser l'adresse https fournie par ngrok + /api/messages ???
POST Response code 502 (Bad Gateway)

OLD - Publier sur Azure

  1. Azure → créer une Web App Bot (Web App + Bot Channels Registration)
  2. Publier
    • VS → Publish (création d'une Web App (App Service) au besoin)
    • Azure → Create a resource → Web App → Get publish profile (à importer dans VS)
  3. Définir le Messaging endpoint: Azure → My Bot Channels Registration → Settings https://<url-app-service>/api/messages
  4. Récupérer l'AppId: Azure → My Bot Channels Registration → Settings → Microsoft App ID
  5. Récupérer le AppSecret: Azure → My Bot Channels Registration → Settings → Microsoft App ID → Manage → Create New Password
    • The application was not found → utiliser Edge
  6. Ajouter le AppId et le AppSecret au web.config ou aux Application settings de la Web App
web.config
<configuration>
  <appSettings>
    <add key="BotId" value="YourBotId" />
    <add key="MicrosoftAppId" value="" />
    <add key="MicrosoftAppPassword" value="" />

Test: Azure → My Bot Channels Registration → Test in Web Chat

FormFlow

Dialogs/RootDialog.cs
var cocktailForm = new FormDialog<Cocktail>(
    new Cocktail(), 
    () => Cocktail.BuildForm(), 
    FormOptions.PromptInStart);
context.Call<Cocktail>(cocktailForm, CocktailFormComplete);

private async Task CocktailFormComplete(IDialogContext context, IAwaitable<Cocktail> result)
{
    Cocktail cocktail = null;
    try
    {
        cocktail = await result;
    }
    catch (OperationCanceledException)
    {
        await context.PostAsync("You canceled the form!");
        return;
    }

    if (cocktail != null)
    {
        await context.PostAsync(cocktail.Choice.ToString());
    }
    else
    {
        await context.PostAsync("Form returned empty response!");
    }

    context.Wait(MessageReceivedAsync);
}
Cocktail.cs
[Serializable]
public class Cocktail
{
    [Prompt("Choisis ton cocktail: {||}")]
    public CocktailChoice Choice { get; set; }

    public static IForm<Cocktail> BuildForm()
    {
        return new FormBuilder<Cocktail>()
                .Message("Bienvenu au bar!")
                .Build();
    }
}

public enum CocktailChoice
{
    Rien, CubaLibre, Daïquiri, Mojito, Caïpirinha
};

Card

Csharp.svg
var card = new SigninCard("Please sign-in to use this functionality.", 
    new List<CardAction>()
    {
        new CardAction()
        {
            Title = "Sign in",
            Type = ActionTypes.OpenUrl,
            Value = "https://login.microsoftonline.com"
        }
    });
message.Attachments.Add(card.ToAttachment());
Format
Web Teams
saut de ligne \n\n <br/>
gras **gras** gras

Channel

Csharp.svg
switch (context.Activity.ChannelId)
{
    case ChannelIds.Msteams:
        break;
    default:
        break;
}

PromptDialog

Permet de poser une question attendant une réponse et de boucler tant une réponse correcte n'a pas été fournie.

Csharp.svg
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var activity = await result as Activity;

    if (activity.Text == "reboot")
    {
        PromptDialog.Confirm(
            context,
            AfterResetAsync,
            "Voulez-vous vraiment lancer la commande reboot",
            "Je n'ai pas compris votre réponse (yes/no)",
            promptStyle: PromptStyle.None);
    }
}

private async Task AfterRebootAsync(IDialogContext context, IAwaitable<bool> result)
{
    var confirm = await result;
    if (confirm)
    {
        await context.PostAsync("Lancement du Reboot");
    }
    else
    {
        await context.PostAsync("Annulation du Reboot");
    }
    context.Wait(MessageReceivedAsync);
}

Text analytics

Services

Language detection The API returns the detected language and a numeric score between 0 and 1. Scores close to 1 indicate 100% certainty that the identified language is true. A total of 120 languages are supported.
Key phrase extraction The API returns a list of strings denoting the key talking points in the input text.
Sentiment analysis The API returns a numeric score between 0 and 1. Scores close to 1 indicate positive sentiment, and scores close to 0 indicate negative sentiment.
Identify entities in your text Detect all named entities in the text, such as organizations, people, and locations.

Azure

  • Web App Bot
  • Functions Bot
  • Bot Channels Registration

Channel

Skype

Embed a bot in a website

Skype Web Contol

Html.svg
<span class="skype-button bubble" data-bot-id="xxx"
      data-color="#00AFF0">
</span>

<span class="skype-chat"
      data-color-message="#80DDFF"
      data-show-header="false"
      data-can-upload-file="false"
      data-theme="dark">
</span>

<script src="https://swc.cdn.skype.com/sdk/v1/sdk.min.js"></script>

Fenêtre blanche dans Chrome

Erreur: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.
chrome://settings/content/cookies

  • Block third-party cookie = Off
  • OU ajouter swc.cdn.skype.com dans les domaines autorisés Allow