
Teilen Sie:
Ehemaliger .NET Developer Advocate @Vonage, polyglotter Software-Ingenieur, AI/ML
Der Nexmo-Helfer des Weihnachtsmanns C#-Adventsserie
Lesedauer: 8 Minuten
Die Adventszeit ist für viele eine arbeitsreiche Zeit, aber für niemanden mehr als für den heiligen Nikolaus, dessen Festtag der östliche Ritus heute begeht. Zu Ehren der Saison des C#-Advent werden wir dem Weihnachtsmann helfen, indem wir einen Teil seiner Korrespondenz automatisieren und ihm modernere Mittel zur Beantwortung von Fragen zur Verfügung stellen als den Postweg.
Um unser Ziel zu verdeutlichen, werden wir einen FAQ-Bot für den Weihnachtsmann erstellen, der über Facebook Messenger, WhatsApp und SMS erreichbar ist und antworten kann.
Wir werden dies mit Hilfe von QnAMaker und natürlich der Nexmo Messages API
Voraussetzungen
Vonage API-Konto
Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.
In diesem Lernprogramm wird auch eine virtuelle Telefonnummer verwendet. Um eine zu erwerben, gehen Sie zu Rufnummern > Rufnummern kaufen und suchen Sie nach einer Nummer, die Ihren Anforderungen entspricht.
Visual Studio 2019 Version 16.3 oder höher
Ein Azure Account
Fakultativ: Ngrok für den Testeinsatz
Optional: Für Facebook Messenger müssen wir eine Facebook-Seite mit unserem Nexmo Account verknüpfen - eine Schritt-für-Schritt-Anleitung finden Sie unter Nexmo Entwickler. Ausfüllen von Teil 2 der Anleitung wird eine Nexmo-App mit einer verknüpften Facebook-Seite erstellt; stellen Sie sicher, dass Sie die private Schlüsseldatei, die für diese Anwendung erstellt wird, speichern.
Hinweis: Der Code in dieser Demo funktioniert mit WhatsApp-Nachrichten, sobald WhatsApp Business konfiguriert ist. Das heißt, WhatsApp ist eher für den geschäftlichen Einsatz gedacht - weitere Details zur Konfiguration einer App für WhatsApp finden Sie in der Anleitung auf Nexmo Entwickler
Bau unseres Bot
Einrichtung
Um unseren Bot zu erstellen, gehen wir hinüber zu QnAMaker und melden uns mit einem Azure Account an.
Klicken Sie auf "Wissensdatenbank erstellen".
Folgen Sie den Anweisungen in Schritt 1 zur Erstellung eines QnA-Dienstes in Azure.
Für Schritt 2 stellen Sie Folgendes ein:
Microsoft Azure-Verzeichnis-ID
Der Name des Abonnements aus dem Azure Account
Der QnA-Dienst, den wir verwenden werden (dies entspricht dem Dienstnamen, den wir gerade im Azure-Portal erstellt haben)
Die Sprache unseres Bots

Für Schritt 3 werden wir unsere Wissensdatenbank "Santa's Nexmo Helper" nennen.
Schritt 4 ist der Punkt, an dem QnA Maker cool wird - die Wissensdatenbank des Bots zu füllen ist so einfach wie das Verlinken mit einer FAQ-Seite oder das Hochladen einer FAQ-Datei. Für diese Demo verwenden wir natürlich die offizielle Website des Weihnachtsmanns FAQ-Seite.
Wir werden unserem Bot auch ein paar Plaudereien hinzufügen - da unser Bot ein Ehrenelf sein wird, werden wir die witzige Plaudereien-Auswahl verwenden
Klicken Sie anschließend auf Wissensdatenbank erstellen.
Dadurch werden die FAQ, auf die wir den QnA Maker hingewiesen haben, eingelesen und wir gelangen auf eine Seite, die wie folgt aussieht:

Bearbeiten, Veröffentlichen und Testen unserer Knowledgebase
Dies ist der Bearbeitungsbildschirm unserer Wissensdatenbank. Von hier aus können wir sehen, wie unsere Wissensdatenbank aussieht. Wir können sie auch frei bearbeiten, wenn wir einige Antworten ändern wollen; z. B. können wir die Antwort "Wer ist der Weihnachtsmann" kürzen.
Nachdem Sie die Wissensdatenbank ausreichend bearbeitet haben, klicken Sie auf Save and Train wird der Bot gespeichert und trainiert.
Zum Testen klicken Sie auf die Schaltfläche Test Schaltfläche in der oberen rechten Ecke. Dies öffnet den Testdialog, Sie können id eine Frage schicken, z.B. how do reindeer fly? und der Bot wird antworten!

Es ist sogar möglich, zu überprüfen, wie der Bot seine Entscheidung getroffen hat. Klicken Sie auf den Link "Inspektion" und der Inspektionsdialog wird angezeigt. Hier wird angezeigt, wie sehr der Bot auf seine Antwort vertraut und welche Alternativen er vorgeschlagen hat.

Wenn der Bot einsatzbereit ist, klicken Sie oben auf der Seite auf "Veröffentlichen" und dann im angezeigten Dialogfeld auf "Veröffentlichen". Wenn dies abgeschlossen ist, wird ein Bildschirm mit einigen hilfreichen Anfragestrukturen angezeigt, die verwendet werden können, um eine Antwort vom Bot zu generieren. Es wird ungefähr so aussehen:
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"}Speichern Sie diese Zeichenfolge - sie wird zur Erstellung des WebService verwendet, der unseren Bot über die Messages API ansteuert.
Aufbau unserer App
Öffnen Sie zunächst Visual Studio und wählen Sie "Neues Projekt erstellen". Wählen Sie in dem sich öffnenden Dialogfeld eine ASP.NET Core-Webanwendung aus. Nennen Sie sie z. B. "QnAMakerMessagesDemo", wählen Sie als Typ ASP.NET Core 3.0, "Web Application (Model-View-Control)" und klicken Sie auf "Erstellen".
NuGet-Pakete installieren
Gehen Sie in Visual Studio zu Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution.
Installieren Sie die folgenden NuGet-Pakete:
Newtonsoft.Json
Nexmo.Csharp.Client
BouncyCastle
jose-jwt
Aufbau unseres Token-Generators
Erstellen Sie eine Klasse namens TokenGenerator und fügen Sie den folgenden Code hinzu:
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);
}
}Dadurch wird das JWT generiert, das für die Authentifizierung gegenüber der Nachrichten-App benötigt wird, die als Teil der Voraussetzungen erstellt wurde.
appId und den Pfad des privaten Schlüssels zur Konfiguration hinzufügen
Der JWT-Generator benötigt eine appId und einen Pfad zum privaten Schlüssel, der zuvor in der Datei appsettings.json gespeichert wurde.
Öffnen Sie diese Datei und fügen Sie dem Konfigurationsobjekt Folgendes hinzu:
"Authentication": {
"appId": "NEXMO_APPLICATION_ID",
"privateKey": "C:\\Path\\to\\Private\\key.key"
} Aufbau von Datenstrukturen zum Empfangen und Senden von Daten
Für diese Demo werden ein paar POCOs benötigt; sie sind ein bisschen langatmig und tun nichts anderes, als die Nachrichtenobjekte gemäß der Spezifikationzu definieren, daher wird die vollständige Struktur in diesem Beitrag weggelassen. Fügen Sie einfach die folgenden Klassen in das Projekt ein:
InboundMessage.cs MessageRequest.cs
Nachrichten senden
Nachdem die Strukturen geordnet sind, geht es im nächsten Schritt darum, Nachrichten über die Nexmo Messages API zu versenden. Erstellen Sie eine Klasse namens MessageSenderDiese wird eine einzige statische Methode haben SendMessage die einfach eine Nachrichtenanforderung erstellt, ein JWT erstellt, eine Anforderung erstellt und die Anforderung an die Messages API sendet - sie sollte etwa so aussehen:
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());
}
} Dem Bot eine Frage stellen und eine Antwort senden
Jetzt ist es an der Zeit, von der App aus mit dem FAQ-Bot zu kommunizieren. Hier kommen die REST-Beispielaufrufe ins Spiel, die QnAMaker zuvor vorgestellt hat. Erinnern Sie sich an diese Zeichenfolge von vorhin.
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"}Verwenden Sie diese Zeichenfolge, um einige nützliche Konstanten/Lesezeichen für den Fragesteller zu erstellen - geben Sie die entsprechenden Werte aus der obigen Zeichenfolge ein:
//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";Erstellen Sie dann eine Aufgabe, um die Frage zu stellen:
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();
}
}
Dies formatiert einfach die Frage aus der Messages API und sendet die Anfrage als generateAnswer Post-Anfrage an den QnAMaker Bot.
Erstellen Sie schließlich eine Methode, um die Anfrage zu steuern und mit einer Antwort zu versehen.
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);
} Controller für den Empfang von eingehenden Nachrichten erstellen
Das letzte Teil des Puzzles ist der Controller, der den Zustrom von Nachrichten von der Messages API verarbeiten wird. Erstellen Sie einen leeren MVC Controller namens MessagesController.
Dependency Injection und Konfiguration
Fügen Sie ein IConfiguration-Feld namens _config hinzu und richten Sie eine Dependency Injection der Konfiguration ein, indem Sie einen Controller-Konstruktor erstellen, der ein IConfiguration-Objekt annimmt:
private IConfiguration _config;
public MessagesController(IConfiguration config)
{
_config = config;
} Status-Anfrage
Als Nächstes erstellen Sie eine Status Post-Anfrage, die einfach keinen Inhalt zurückgibt:
[HttpPost]
public HttpStatusCode Status()
{
return HttpStatusCode.NoContent;
} Eingehende Nachrichten
Als Nächstes fügen Sie eine Postanforderung für eingehende Nachrichten von der Messages API hinzu.
Diese Methode extrahiert die eingehende Nachricht aus dem Body und leitet dann die Aufgaben des Questioners aus dem Inhalt des Anfragekörpers weiter.
[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;
} Eingehende SMS
Fügen Sie schließlich eine HttpGet-Anfrage hinzu, um die eingehenden SMS-Nachrichten zu verwalten. Diese extrahiert ebenfalls die benötigten Informationen aus der eingehenden Nachricht und stellt die Frage an den Fragesteller.
[HttpGet]
public HttpStatusCode InboundSms([FromQuery] SMS.SMSInbound inboundMessage)
{
_ = Questioner.AskQuestion(inboundMessage.msisdn, inboundMessage.to, "sms", inboundMessage.text, _config);
return HttpStatusCode.NoContent;
}Mit dieser Sortierung ist der Dienst einsatzbereit.
Prüfung
Als Letztes müssen Sie den Dienst starten, ihn dem Internet aussetzen und die Nexmo Messages App verkabeln, um Webhooks an den Dienst zu senden.
IIS Express-Konfiguration
Der Einfachheit halber verwendet diese Demo den IIS. Um die Einrichtung von ngrok zu vereinfachen, deaktivieren Sie SSL für IIS Express, indem Sie in den Debug-Eigenschaften des Projekts das Häkchen bei "SSL aktivieren" entfernen:

Notieren Sie sich die Portnummer im Feld für die URL der Anwendung, sie wird im nächsten Schritt verwendet.
Ngrok einrichten
Der nächste Schritt besteht darin, diesen Endpunkt für das Internet freizugeben. Für diese Demo wird etwas wie ngrok verwendet werden, um einen Tunnel zurück zum IIS Express-Port zu erstellen. Nach der Installation von ngrok verwenden Sie einen Befehl wie:
Um den Tunnel einzurichten, ersetzen Sie "PORT_NUMBER" durch die zuvor notierte IIS Express-Portnummer. Dadurch wird eine Ausgabe erzeugt, die in etwa so aussieht:

Beachten Sie die http-Basis-URL - im obigen Bild ist die Basis-URL http://dc0feb1d.ngrok.io.
Konfigurieren von Webhooks
Der letzte Schritt vor dem Einschalten des Dienstes besteht darin, die Webhooks so zu konfigurieren, dass sie einen Rückruf an den Dienst senden.
Eingehende SMS
Besuchen Sie das Nexmo Dashboard und gehen Sie zu Settings. Setzen Sie die URL für eingehende Nachrichten für SMS auf ngrok_baseurl/messages/InboundSms, wie im obigen Beispiel.
http://dc0feb1d.ngroke.io/messages/InboundSms Andere eingehende Nachrichten
Im Nexmo Dashboard öffnen Sie Nachrichten und Versand -> Ihre Applications. Öffnen Sie die Anwendung, die mit den verknüpften Accounts verbunden ist, und klicken Sie auf "Bearbeiten". Setzen Sie unter Capabilities im Abschnitt Messages die Inbound URL und Status URL auf die ngrok baseurl /Messages/Inbound bzw. /Messages/Status und klicken Sie auf Save.
Im Beispiel des ngrok-Tunnels sieht das etwa so aus:

HINWEIS: Die 8 Zeichen vor ngrok.io sind bei der kostenlosen Version nicht festgelegt. Das bedeutet, dass jedes Mal, wenn der ngrok-Befehl ausgeführt wird, geändert werden muss, wohin die Webhooks zielen. Es ist möglich, einen statischen Hostnamen zu erstellen, indem Sie auf ein kostenpflichtiges ngrok-Tier upgraden.
Einschalten und testen
Und das war's! Der Nexmo-Helfer des Weihnachtsmanns ist bereit für den Einsatz. Starten Sie IIS Express und verschicken Sie Ihre Nachrichten. Diese kann über jeden Kanal erreicht werden, der für die Nachrichten-App konfiguriert ist.
Hier ist ein Beispiel von Facebook:

Und eine von SMS:

Der Nexmo-Helfer des Weihnachtsmanns ist jetzt einsatzbereit.
Weitere Lektüre
Den vollständigen Quellcode für diese Demo finden Sie auf GitHub
Weitere Informationen über QnAMaker finden Sie auf deren Website hier
Für vollständig interaktive Bots siehe Luis Ai
Weitere Informationen über die Nexmo Messages API finden Sie in der Dokumentation auf Nexmo Entwickler
Für weitere APIs von Nexmo besuchen Sie unsere Entwickler-Seite
Um das Nexmo .NET SDK zu testen, können Sie einen Blick auf unser GitHub Repo
