https://d226lax1qjow5r.cloudfront.net/blog/blogposts/santas-nexmo-helper-c-advent-series-dr/E_Santas-Helper_1200x600.jpg

Der Nexmo-Helfer des Weihnachtsmanns C#-Adventsserie

Zuletzt aktualisiert am May 18, 2021

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

Build QnAMaker gif

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:

QnA Maker Knowledgebase edit screen

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!

Test Question

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.

Inspect Drill down

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:

Debug settings

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:

ngrok http --host-header="localhost:PORT_NUMBER" http://localhost:PORT_NUMBER

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:

ngrok output

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:

messages urls

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:

Facebook Example

Und eine von SMS:

SMS Example

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

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/73d57fd8eb/stevelorello.png
Steve LorelloVonage Ehemalige

Ehemaliger .NET Developer Advocate @Vonage, polyglotter Software-Ingenieur, AI/ML