
Partager:
Ancien développeur .NET Advocate @Vonage, ingénieur logiciel polyglotte full-stack, AI/ML
Série de l'Avent du Père Noël Nexmo Helper C#
Temps de lecture : 9 minutes
C'est la saison de l'Avent, une période si chargée pour beaucoup, mais surtout pour le vieux Saint-Nicolas, dont le rite oriental célèbre aujourd'hui la fête. En l'honneur de la saison de l'Avent Avent C# nous allons aider le Père Noël en automatisant une partie de sa correspondance et en lui fournissant des moyens plus modernes que la poste pour répondre à ses questions.
Pour cristalliser notre objectif, nous allons créer un robot FAQ pour le Père Noël qui peut être joint et répondre via Facebook Messenger, WhatsApp et SMS.
Nous allons construire ceci avec l'aide de QnAMaker et bien sûr de l'API Nexmo Messages API
Conditions préalables
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Visual Studio 2019 version 16.3 ou supérieure
Un Account Azure
Optionnel : Ngrok pour le déploiement de tests
Facultatif : Pour Facebook Messenger, nous devrons lier une page Facebook à notre Account Nexmo - vous pouvez consulter les instructions étape par étape sur le site suivant Nexmo Developer. Compléter partie 2 du guide créera une Applications Nexmo avec une page Facebook liée ; assurez-vous de sauvegarder le fichier de clé privée qui est généré pour cette application.
Remarque : le code de cette démo fonctionnera avec WhatsApp Messages une fois que WhatsApp Business sera configuré. Cela dit, WhatsApp est plutôt destiné aux entreprises - pour plus de détails sur la configuration d'une application pour WhatsApp, vous pouvez consulter le guide sur le site de Nexmo Developer
Construire notre robot
Mise en place
Pour construire notre bot, nous allons nous rendre à QnAMaker et nous connecter à l'aide d'un compte Azure.
Cliquez sur "Créer une base de connaissances".
Suivez les instructions de l'étape 1 pour créer un service QnA dans Azure.
Pour l'étape 2, définissez les éléments suivants :
ID de l'annuaire Microsoft Azure
Le nom de l'abonnement à partir du compte Azure.
Le service QnA que nous allons utiliser (il correspondra au nom de service que nous venons de créer dans le portail Azure).
La langue de notre robot

Pour l'étape 3, nous allons nommer notre base de connaissances "Santa's Nexmo Helper".
C'est à l'étape 4 que QnA Maker devient intéressant : il suffit de lier la base de connaissances de ce robot à une page FAQ ou de télécharger un fichier FAQ pour qu'elle soit alimentée. Bien entendu, pour cette démo, nous allons utiliser la page page FAQ du site officiel du Père Noël.
Nous allons également ajouter un peu de bavardage à notre robot - puisque notre robot sera un elfe honoraire, nous allons utiliser la sélection de bavardage plein d'esprit.
Une fois tous les paramètres définis, cliquez sur Créer une base de connaissances.
Cela permettra d'ingérer les FAQ que nous avons indiquées à QnA Maker et nous amènera à une page qui ressemble à celle-ci :

Édition, publication et test de notre base de connaissances
Voici l'écran d'édition de notre base de connaissances. D'ici, nous pouvons voir à quoi ressemble notre base de connaissances. Nous pouvons également l'éditer librement si nous voulons changer certaines réponses ; par exemple, nous pouvons raccourcir la réponse "Qui est le Père Noël ?
Après avoir suffisamment modifié la base de connaissances, cliquer sur Save and Train pour enregistrer et former le bot.
Pour tester, cliquez sur le bouton Test dans le coin supérieur droit. Cela ouvrira la boîte de dialogue de test, vous pouvez lui envoyer une question, par exemple how do reindeer fly? et le robot vous répondra !

Il est même possible d'inspecter la façon dont le robot a pris sa décision. Cliquez sur le lien inspecter et la boîte de dialogue d'inspection s'ouvrira. Elle indique le degré de confiance du robot dans sa réponse et les alternatives qu'il a proposées.

Lorsque le bot est prêt à fonctionner, cliquez sur publier en haut de la page, puis sur publier dans la boîte de dialogue qui s'affiche. Une fois cette opération terminée, un écran s'affiche avec des structures de demande utiles qui peuvent être utilisées pour générer une réponse de la part du robot. Cela ressemblera à quelque chose comme :
POST /knowledgebases/YOUR_KNOWLEDGE_BASE_ID/generateAnswer
Host: https://nexmofaqbot.azurewebsites.net/qnamaker
Authorization: EndpointKey YOUR_KNOWLEDGE_BASE_ENDPOINT_KEY
Content-Type: application/json
{"question":"YOUR_QUESTION"}Enregistrez cette chaîne - elle sera utilisée pour créer le WebService qui pilotera notre robot via l'API Messages.
Construire notre application
Commencez par ouvrir Visual Studio et sélectionnez " Créer un nouveau projet " Dans la boîte de dialogue qui s'ouvre, sélectionnez une application Web ASP.NET Core, nommez-la quelque chose comme " QnAMakerMessagesDemo ". Nommez-la quelque chose comme "QnAMakerMessagesDemo" Sélectionnez ASP.NET Core 3.0, "Application Web (Model-View-Control)" comme type et cliquez sur créer.
Installer les paquets NuGet
Dans Visual Studio, allez dans Outils -> NuGet Package Manager -> Gérer les paquets NuGet pour la solution.
Installez les paquets NuGet suivants :
Newtonsoft.Json
Nexmo.Csharp.Client
Château gonflable
jose-jwt
Construction de notre générateur de jetons
Créez une classe appelée TokenGenerator et ajoutez-y le code suivant :
public static string GenerateToken(IConfiguration config)
{
// retrieve appID and privateKey from configuration
var appId = config["Authentication:appId"];
var priavteKeyPath = config["Authentication:privateKey"];
string privateKey = "";
using (var reader = File.OpenText(priavteKeyPath)) // file containing RSA PKCS1 private key
privateKey = reader.ReadToEnd();
//generate claims list
const int SECONDS_EXPIRY = 3600;
var t = DateTime.UtcNow - new DateTime(1970, 1, 1);
var iat = new Claim("iat", ((Int32)t.TotalSeconds).ToString(), ClaimValueTypes.Integer32); // Unix Timestamp for right now
var application_id = new Claim("application_id", appId); // Current app ID
var exp = new Claim("exp", ((Int32)(t.TotalSeconds + SECONDS_EXPIRY)).ToString(), ClaimValueTypes.Integer32); // Unix timestamp for when the token expires
var jti = new Claim("jti", Guid.NewGuid().ToString()); // Unique Token ID
var claims = new List<Claim>() { iat, application_id, exp, jti };
//create rsa parameters
RSAParameters rsaParams;
using (var tr = new StringReader(privateKey))
{
var pemReader = new PemReader(tr);
var kp = pemReader.ReadObject();
var privateRsaParams = kp as RsaPrivateCrtKeyParameters;
rsaParams = DotNetUtilities.ToRSAParameters(privateRsaParams);
}
//generate and return JWT
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
Dictionary<string, object> payload = claims.ToDictionary(k => k.Type, v => (object)v.Value);
return Jose.JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256);
}
}Cela générera le JWT nécessaire pour s'authentifier auprès de l'In-App Messaging créée dans le cadre des prérequis.
Ajouter appId et le chemin de la clé privée à la configuration
Le générateur de JWT fait appel à un appId et à un chemin d'accès à la clé privée sauvegardée précédemment dans le fichier appsettings.json.
Ouvrez ce fichier et ajoutez les éléments suivants à l'objet de configuration :
"Authentication": {
"appId": "NEXMO_APPLICATION_ID",
"privateKey": "C:\\Path\\to\\Private\\key.key"
} Construire des structures de données pour recevoir et envoyer des données
Quelques POCO sont nécessaires pour cette démonstration ; ils sont un peu verbeux et ne font rien d'autre que de définir les objets de messages conformément à la directive specla structure complète est donc omise dans ce billet. Il suffit d'ajouter les classes suivantes au projet :
InboundMessage.cs MessageRequest.cs
Envoyer des messages
Une fois les structures établies, l'étape suivante consiste à envoyer des messages via l'API Messages de Nexmo. Créez une classe appelée MessageSenderqui aura une seule méthode statique SendMessage qui créera simplement une demande de message, créera un JWT, créera une demande et enverra la demande à l'API Messages - elle devrait ressembler à ceci :
public static void SendMessage(string message, string fromId, string toId, IConfiguration config, string type)
{
const string MESSAGING_URL = @"https://api.nexmo.com/v0.1/messages";
try
{
var jwt = TokenGenerator.GenerateToken(config);
//construct message Request
var requestObject = new MessageRequest()
{
to = new MessageRequest.To()
{
type = type
},
from = new MessageRequest.From()
{
type = type
},
message = new MessageRequest.Message()
{
content = new MessageRequest.Message.Content()
{
type = "text",
text = message
}
}
};
//special messenger request formatting (use to/from id rather than number, set category to RESPONSE)
if (type == "messenger")
{
requestObject.message.messenger = new MessageRequest.Message.Messenger()
{
category = "RESPONSE"
};
requestObject.to.id = toId;
requestObject.from.id = fromId;
}
else
{
requestObject.to.number = toId;
requestObject.from.number = fromId;
}
//Generate Request payload from requestObject
var requestPayload = JsonConvert.SerializeObject(requestObject, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore });
//build request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(MESSAGING_URL);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Accept = "application/json";
httpWebRequest.Method = "POST";
httpWebRequest.PreAuthenticate = true;
httpWebRequest.Headers.Add("Authorization", "Bearer " + jwt);
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(requestPayload);
}
//handle response
using (var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
Console.WriteLine(result);
Console.WriteLine("Message Sent");
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
} Poser une question au robot et envoyer une réponse
Il est maintenant temps de parler au robot FAQ depuis l'application. C'est là que les exemples d'appels REST que QnAMaker a présentés plus tôt vont entrer en jeu. Rappelez-vous cette chaîne de caractères.
POST /knowledgebases/YOUR_KNOWLEDGE_BASE_ID/generateAnswer
Host: https://AZURE_APP_NAME.azurewebsites.net/qnamaker
Authorization: EndpointKey YOUR_KNOWLEDGE_BASE_ENDPOINT_KEY
Content-Type: application/json
{"question":"YOUR_QUESTION"}Utilisez cette chaîne pour créer des constantes et des listes de lecture utiles pour le questionneur - remplissez-les avec les valeurs appropriées de la chaîne ci-dessus :
//TODO: fill in with Knowledgebase ID
const string kb_id = "YOUR_KNOWLEDGE_BASE_ID";
//TODO: fill in with Knowledgebase Endpoint key
const string ENDPOINT_KEY = "YOUR_KNOWLEDGE_BASE_ENDPOINT_KEY";
const string QUESTION_FORMAT = @"{{'question': '{0}'}}";
//TODO fill in base url
static readonly string URI = $"https://AZURE_APP_NAME.azurewebsites.net/qnamaker/knowledgebases/{kb_id}/generateAnswer";Ensuite, créez une tâche pour poser la question :
public static async Task<string> RequestAnswer(string question)
{
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(URI);
var formatted_question = string.Format(QUESTION_FORMAT, question);
request.Content = new StringContent(formatted_question, Encoding.UTF8, "application/json");
request.Headers.Add("Authorization", "EndpointKey " + ENDPOINT_KEY);
var response = await client.SendAsync(request);
var jsonResponse = await response.Content.ReadAsStringAsync();
JObject obj = JObject.Parse(jsonResponse);
var answer = ((JArray)obj["answers"])[0]["answer"];
return answer.ToString();
}
}
Il s'agit simplement de formater la question à partir de l'API des messages et d'envoyer la demande au robot QnAMaker sous la forme d'une demande de message generateAnswer.
Enfin, créez une méthode pour gérer la demande et y répondre.
public static async Task AskQuestion(string to, string from, string type, string question, IConfiguration config)
{
question = HttpUtility.JavaScriptStringEncode(question);
var response = await RequestAnswer(question);
MessageSender.SendMessage(response, from, to, config, type);
} Construire un contrôleur pour recevoir les messages entrants
La dernière pièce du puzzle est le contrôleur qui gérera l'afflux de messages provenant de l'API Messages. Créez un contrôleur MVC vide appelé MessagesController.
Injection de dépendance et configuration
Ajoutez-y un champ IConfiguration appelé _config et mettez en place l'injection de dépendance de la configuration en créant un constructeur de contrôleur prenant un objet IConfiguration :
private IConfiguration _config;
public MessagesController(IConfiguration config)
{
_config = config;
} Demande de statut
Créez ensuite une requête "Status Post" qui ne renvoie aucun contenu :
[HttpPost]
public HttpStatusCode Status()
{
return HttpStatusCode.NoContent;
} Messages entrants
Ensuite, ajoutez une demande de courrier pour les messages entrants provenant de l'API Messages.
Cette méthode extrait le message entrant du corps de la requête, puis transmet les tâches du questionneur à partir du contenu du corps de la requête.
[HttpPost]
public HttpStatusCode Inbound([FromBody]InboundMessage message)
{
Debug.WriteLine(JsonConvert.SerializeObject(message));
if (message.from.type == "messenger")
{
_ = Questioner.AskQuestion(message.from.id, message.to.id, message.from.type, message.message.content.text, _config);
}
else
{
_ = Questioner.AskQuestion(message.from.number, message.to.number, message.from.type, message.message.content.text, _config);
}
return HttpStatusCode.NoContent;
} SMS entrants
Enfin, ajoutez une requête HttpGet pour gérer les SMS entrants. Cette requête extraira les informations nécessaires du message entrant et posera la question à l'auteur de la question.
[HttpGet]
public HttpStatusCode InboundSms([FromQuery] SMS.SMSInbound inboundMessage)
{
_ = Questioner.AskQuestion(inboundMessage.msisdn, inboundMessage.to, "sms", inboundMessage.text, _config);
return HttpStatusCode.NoContent;
}Une fois ce tri effectué, le service est prêt à être déployé.
Essais
La dernière chose à faire est d'activer le service, de l'exposer à l'internet et de câbler l'application In-App Messaging pour qu'elle envoie des webhooks au service.
Configuration de IIS Express
Pour des raisons de simplicité, cette démo utilise IIS. Pour faciliter l'installation de ngrok, désactivez SSL pour IIS Express en allant dans les propriétés de débogage du projet et en décochant le paramètre "Enable SSL" :

Prenez note du numéro de port dans le champ de l'URL de l'app, il sera utilisé à l'étape suivante.
Mise en place de Ngrok
L'étape suivante consiste à exposer ce point final à l'internet. Pour cette démo, quelque chose comme ngrok peut être utilisé pour créer un tunnel vers le port IIS Express. Après avoir installé ngrok, utilisez une commande comme :
Pour mettre en place le tunnel, remplacez 'PORT_NUMBER' par le numéro de port IIS Express mentionné plus haut. Cela créera une sortie qui ressemblera à ceci :

Prenez note de l'URL de base http - dans l'image ci-dessus, l'URL de base est http://dc0feb1d.ngrok.io.
Configuration des webhooks
La dernière étape avant d'activer le service consiste à configurer les webhooks pour qu'ils rappellent le service.
SMS entrants
Allez sur le Tableau de bord Nexmo et allez à Settings. Définissez l'URL des messages entrants pour les SMS à ngrok_baseurl/messages/InboundSms, selon l'exemple ci-dessus.
http://dc0feb1d.ngroke.io/messages/InboundSms Autres messages entrants
Dans le tableau de bord Nexmo ouvrez Messages et Dispatch -> Vos Applications. Ouvrez l'application associée aux comptes liés et cliquez sur "Modifier". Sous Capacités dans la section Messages, définissez l'URL entrante et l'URL d'état pour qu'elles correspondent à la baseurl ngrok /Messages/Inbound et /Messages/Status respectivement et cliquez sur "Enregistrer".
Selon l'exemple de tunnel ngrok, il ressemblera à quelque chose comme :

NOTE : Les 8 caractères précédant ngrok.io ne sont pas fixes dans la version gratuite. Cela signifie qu'à chaque fois que la commande ngrok est lancée, il sera nécessaire de changer l'emplacement des webhooks. Il est possible de créer un nom d'hôte statique en passant à une version payante de ngrok.
Mise en route et test
Et voilà ! Le Nexmo Helper du Père Noël est prêt à être déployé. Lancez IIS Express et envoyez des messages. Ce dernier peut être joint sur n'importe quel canal configuré pour atteindre l'application de messages.
Voici un exemple tiré de Facebook :

Et un de SMS :

Et voilà, le Nexmo Helper du Père Noël est opérationnel.
Pour en savoir plus
Le code source complet de cette démo est disponible sur GitHub
Pour plus d'informations sur QnAMaker, consultez leur site web ici
Pour des robots entièrement interactifs, voir Luis Ai
Pour plus d'informations sur l'API Messages Nexmo, consultez la documentation sur Nexmo Developer
Pour plus d'API de Nexmo, consultez notre site des développeurs
Pour vérifier le SDK Nexmo .NET, vous pouvez jeter un coup d'œil à notre GitHub Repo
