Plan du site  
pixel
pixel

Articles - Étudiants SUPINFO

Microsoft Bot Framework - Le chatbot Coupe du Monde 2018 - Partie 2

Par Thanh Vang THACH Publié le 12/07/2018 à 12:13:05 Noter cet article:
(0 votes)
Avis favorable du comité de lecture

Création du chatbot Coupe du Monde 2018

Dans cet article nous allons créer un chatbot qui va nous donner les derniers résultats de la Coupe du Monde 2018, ainsi que les informations sur cette compétition.

Cet article est divisé en deux parties. Cette seconde partie se concentre sur le développement du chatbot avec l'aide du Bot Framwork de Microsoft.

Configuration de LUIS

Dans un premier temps, il faut configurer le service LUIS. Ce service nous permet de comprendre les messages que les utilisateurs vont envoyer au chatbot. Pour configurer LUIS, il faut se rendre sur ce site https://www.luis.ai/applications. Je vous invite à lire mon article précédent pour configurer les clés API nécessaires pour connecter LUIS au chatbot (le lien ici : https://www.supinfo.com/articles/single/6644-microsoft-bot-framework-partie-3-configurer-luis).

Nous allons rentrer les entités dans LUIS, une entité est comme un objet que LUIS va identifier dans une phrase. Par exemple, dans la phrase : "Quel temps fait-il a Montpellier ?", LUIS va identifier dans la phrase que Montpellier est une ville. Renseigner les entités dans LUIS nous rend l'interaction entre le chabot et l'utilisateur plus humain car LUIS appliquera la méthode correspondante à la demande de l'utilisateur et reconnaître les entités dans une phrase. Les entités dans notre cas va nous permettre de reconnaître les groupes de la Coupe du Monde. Nous allons donc créer une entité Group de type simple pour savoir quel groupe de la Coupe du Monde l'utilisateur veut connaître le classement.

Après avoir entré l'entité Group, nous pouvons configurer les Intents. Les Intents sont les types de phrases que LUIS va reconnaître et donc ensuite permettera au chatbot de répondre de la bonne manière. Dans notre cas, il y aura quatre Intents : None, Help, Fixture (pour avoir les résultats des rencontres de la coupe du monde) et Standing (pour avoir le classement d'un groupe demandé par l'utilisateur).

Avant de coder la classe RootLuisDialog, nous allons créer des AdaptiveCard pour rendre l'interface un peu plus sympa pour l'utilisateur.

Adaptive Card

Les Adaptive Cards rendent donc le dialogue entre le chatbot et l'utilisateur plus agréable de visuel. Pour plus de détails sur les Adaptive Cards, Microsoft a une documention sur l'utilisation et la création des cartes sous ce site suivant : https://adaptivecards.io/.

Dans ces cartes, on peut y incorporer du textes, des images et des boutons. Pour notre projet, il y a un bouton "Détails" qui va, après avoir cliqué dessus, ouvrir un navigateur internet avec une page google renvoyant le résultat de recherche sur ce match.

J'ai donc créé une classe AdaptiveCardProvider qui fournit des listes de cartes, ainsi que des cartes modèles qui seront modifiées par la classe AdaptiveCardProvider grâce aux données fournises par le service FootballData (voir article précédent). Cette classe sera appelée plus tard par RootLuisDialog.

Code de la classe Adaptive :

            
public class AdaptiveCardProvider
{

    public static SuggestedActions GetSuggestedActions()
    {
        var suggestedActions = new SuggestedActions()
        {
            Actions = new List<CardAction>()
            {
                new CardAction(){ Title = "Donne moi les résultats", Type=ActionTypes.ImBack, Value="Donne moi les résultats" },
                new CardAction(){ Title = "Donne moi le classement du groupe A", Type=ActionTypes.ImBack, Value="Donne moi le classement du groupe A" }
            }
        };
        return suggestedActions;
    }

    private static async Task<string> GetCard(string cardName)
    {
        var path = HostingEnvironment.MapPath($"/Adaptive/{cardName}.json");
        if (!File.Exists(path))
        {
            return null;
        }
        using (var file = File.OpenText(path))
        {
            string json = await file.ReadToEndAsync();
            return json;
        }
    }

    private static async Task<AdaptiveCard> GetFixtureCard(Fixture fixture)
    {
        AdaptiveCard adaptiveCard = new AdaptiveCard();
        var json = await GetCard("FixtureAdaptiveCard");
        dynamic card = JsonConvert.DeserializeObject(json);
        int? goalsHomeTeam, goalsAwayTeam;
        if(fixture.Result.ExtraTime == null)
        {
            goalsHomeTeam = fixture.Result.GoalsHomeTeam;
            goalsAwayTeam = fixture.Result.GoalsAwayTeam;
        }
        else
        {
            goalsHomeTeam = fixture.Result.ExtraTime.GoalsHomeTeam;
            goalsAwayTeam = fixture.Result.ExtraTime.GoalsAwayTeam;
        }
        if(fixture.Result.PenaltyShootout != null)
        {
            card.body[0].items[0]["text"] = fixture.HomeTeamName + " " + goalsHomeTeam + " ( " + fixture.Result.PenaltyShootout.GoalsHomeTeam +  " )" + " - " + goalsAwayTeam + " ( " + fixture.Result.PenaltyShootout.GoalsAwayTeam + " ) " + fixture.AwayTeamName;
        }
        else
        {
            card.body[0].items[0]["text"] = fixture.HomeTeamName + " " + goalsHomeTeam + " - " + goalsAwayTeam + " " + fixture.AwayTeamName;
        }              

        card.body[1].items[0]["text"] = fixture.Date;
        card.body[2].items[0]["text"] = "Matchday : " + fixture.Matchday;
        var action = card.actions;
        card.actions[0].url += "+" + fixture.HomeTeamName + "+" + fixture.AwayTeamName;

        string cardJson = JsonConvert.SerializeObject(card);
        adaptiveCard = AdaptiveCard.FromJson(cardJson).Card;
        return adaptiveCard;
    }

    public static async Task<List<Attachment>> GetFixturesCard()
    {
        var attachements = new List<Attachment>();
        var fixtures = await FootballDataService.getFixtures();
        
        foreach (var item in fixtures)
        {
            if(item.Status == "FINISHED")
            {
                var card = await GetFixtureCard(item);
                var attachement = new Attachment()
                {
                    Content = card,
                    ContentType = AdaptiveCard.ContentType,
                    Name = "Card"
                };
                attachements.Add(attachement);
            }                
        }
        return attachements;
    }

    public static async Task<List<Attachment>> GetStandingCard(string group)
    {
        var attachements = new List<Attachment>();

        var standing = await FootballDataService.getStandings();
        var groupRank = new List<GroupRank>();

        AdaptiveCard adaptiveCard = new AdaptiveCard();

        switch (group)
        {
            case "a":
                groupRank = standing.A;
                break;
            case "b":
                groupRank = standing.B;
                break;
            case "c":
                groupRank = standing.C;
                break;
            case "d":
                groupRank = standing.D;
                break;
            case "e":
                groupRank = standing.E;
                break;
            case "f":
                groupRank = standing.F;
                break;
            case "g":
                groupRank = standing.G;
                break;
            case "h":
                groupRank = standing.H;
                break;
            default:
                break;
        }

        var json = await GetCard("StandingAdaptiveCard");
        dynamic card = JsonConvert.DeserializeObject(json);

        card.body[0].text += group.ToUpper();
        for (int i = 1; i < 5; i++)
        {
            card.body[i].columns[0].items[0].url = groupRank[i - 1].CrestURI;
            card.body[i].columns[1].items[0].text = "#" + groupRank[i - 1].Rank + " " + groupRank[i - 1].Team;
            card.body[i].columns[1].items[1].columns[0].items[0].text += groupRank[i - 1].PlayedGames;
            card.body[i].columns[1].items[1].columns[1].items[0].text += groupRank[i - 1].Points;
            card.body[i].columns[1].items[1].columns[2].items[0].text += groupRank[i - 1].Goals;
            card.body[i].columns[1].items[1].columns[3].items[0].text += groupRank[i - 1].GoalsAgainst;
            card.body[i].columns[1].items[1].columns[4].items[0].text += groupRank[i - 1].GoalDifference;

        }


        string cardJson = JsonConvert.SerializeObject(card);
        adaptiveCard = AdaptiveCard.FromJson(cardJson).Card;

        var attachement = new Attachment()
        {
            Content = adaptiveCard,
            ContentType = AdaptiveCard.ContentType,
            Name = "Card"
        };
        attachements.Add(attachement);

        return attachements;
    }

}
            
        

Code de la carte FixtureAdaptiveCard (première image) :

            
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.0",
  "body": [
    {
      "type": "Container",
      "name": "header",
      "items": [
        {
          "type": "TextBlock",
          "text": "Result",
          "weight": "bolder",
          "wrap": true

        }
      ]
    },
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "text": "Date",
          "wrap": true
        }
      ]
    },
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "text": "Matchday",
          "wrap": true
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "Détails",
      "url": "https://www.google.fr/search?q=world+cup+2018"
    }
  ]
}
            
        

Code de la carte StandingAdaptiveCard (deuxième image) :

            
{
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [{
            "type": "TextBlock",
            "text": "Classement groupe ",
            "weight": "bolder",
            "size": "large"
        },
        {
            "type": "ColumnSet",
            "spacing": "medium",
            "columns": [{
                    "type": "Column",
                    "width": "auto",
                    "items": [{
                        "type": "Image",
                        "url": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg",
                        "size": "medium",
                        "style": "person"
                    }]
                },
                {
                    "type": "Column",
                    "width": 4,
                    "items": [{
                            "type": "TextBlock",
                            "text": "#1 Teamname",
                            "weight": "bolder",
                            "size": "medium"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "medium",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "M.J. : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Points : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Buts : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Concédés : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Différence : ",
                                        "isSubtle": true
                                    }]
                                }

                            ]
                        }
                    ]
                }
            ]
        },
        {
            "type": "ColumnSet",
            "spacing": "medium",
            "columns": [{
                    "type": "Column",
                    "width": "auto",
                    "items": [{
                        "type": "Image",
                        "url": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg",
                        "size": "medium",
                        "style": "person"
                    }]
                },
                {
                    "type": "Column",
                    "width": 4,
                    "items": [{
                            "type": "TextBlock",
                            "text": "#2 Teamname",
                            "weight": "bolder",
                            "size": "medium"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "medium",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "M.J. : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Points : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Buts : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Concédés : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Différence : ",
                                        "isSubtle": true
                                    }]
                                }

                            ]
                        }
                    ]
                }
            ]
        },
        {
            "type": "ColumnSet",
            "spacing": "medium",
            "columns": [{
                    "type": "Column",
                    "width": "auto",
                    "items": [{
                        "type": "Image",
                        "url": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg",
                        "size": "medium",
                        "style": "person"
                    }]
                },
                {
                    "type": "Column",
                    "width": 4,
                    "items": [{
                            "type": "TextBlock",
                            "text": "#3 Teamname",
                            "weight": "bolder",
                            "size": "medium"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "medium",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "M.J. : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Points : ",
                                        "isSubtle": true
                                    }]
                                },
                              {
                                "type": "Column",
                                "width": "auto",
                                "items": [
                                  {
                                    "type": "TextBlock",
                                    "text": "Buts : ",
                                    "isSubtle": true
                                  }
                                ]
                              },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Concédés : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Différence : ",
                                        "isSubtle": true
                                    }]
                                }

                            ]
                        }
                    ]
                }
            ]
        },
        {
            "type": "ColumnSet",
            "spacing": "medium",
            "columns": [{
                    "type": "Column",
                    "width": "auto",
                    "items": [{
                        "type": "Image",
                        "url": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg",
                        "size": "medium",
                        "style": "person"
                    }]
                },
                {
                    "type": "Column",
                    "width": 4,
                    "items": [{
                            "type": "TextBlock",
                            "text": "#4 Teamname",
                            "weight": "bolder",
                            "size": "medium"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "medium",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "M.J. : ",
                                        "isSubtle": true
                                    }]
                                },{
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Points : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Buts : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Concédés : ",
                                        "isSubtle": true
                                    }]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [{
                                        "type": "TextBlock",
                                        "text": "Différence : ",
                                        "isSubtle": true
                                    }]
                                }

                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
            
        

RootLuisDialog

Cette classe donne au chatbot la capacité de comprendre les messages des utilisateurs et d'y répondre de la bonne manière. Il faut dans un premier temps modifier le fichier MessagesController.cs, plus précisément la fonction Post par le code ci-dessous.

            
[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.RootLuisDialog());
        }
        else
        {
            HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

   ...
}
            
        

Une fois le fichier MessagesController modifier, il nous reste qu'à programmer le fichier RootLuisDialog avec le code suivant.

            
[LuisModel("LUIS_APPID", "LUIS_APPKEY")]
[Serializable]
public class RootLuisDialog : LuisDialog<object>
{
    private const string EntityTypes = "Group";

    [LuisIntent("")]
    [LuisIntent("None")]
    public async Task None(IDialogContext context, LuisResult result)
    {
        string message = $"Sorry, I did not understand '{result.Query}'. Type 'help' if you need assistance.";

        await context.PostAsync(message);

        context.Wait(this.MessageReceived);
    }

    [LuisIntent("Help")]
    public async Task Help(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {
        var act = context.Activity;

        await context.PostAsync("Bonjour! Je suis le chatbot Supinfo FIFA Coupe du Monde 2018.");

        var reply = context.MakeMessage();
        reply.Text = "Je peux vous aider?";
        reply.SuggestedActions = AdaptiveCardProvider.GetSuggestedActions();
        await context.PostAsync(reply);


        context.Wait(this.MessageReceived);
    }

    [LuisIntent("Fixture")]
    public async Task Fixture(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {
        var act = context.Activity;

        await context.PostAsync("Voici les résultats");

        var replyToConversation = context.MakeMessage();
        replyToConversation.Attachments = new List<Attachment>();
        
        replyToConversation.Attachments = await AdaptiveCardProvider.GetFixturesCard();

        await context.PostAsync(replyToConversation);


        context.Wait(this.MessageReceived);
    }

    [LuisIntent("Standing")]
    public async Task Standing(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {
        var act = context.Activity;

        EntityRecommendation typesEntityRecommendation;
        var resultMessage = context.MakeMessage();
        if (result.TryFindEntity(EntityTypes, out typesEntityRecommendation))
        {
            var group = result.Entities.FirstOrDefault().Entity;
            var cards = await AdaptiveCardProvider.GetStandingCard(group);
            resultMessage.Attachments = cards;
        }
        await context.PostAsync(resultMessage);
    }

}
            
        

Conclusion

C'est la fin de cette article, vous pouvez tester le chatbot via l'application botframework-emulator ou l'héberger sous Microsoft Azure et pourquoi pas le mettre en production comme un chatbot Facebook Messenger.

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Toeic, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels