https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-customer-service-chat-rails-5-action-cable-sms-dr/ruby-on-rails-actioncable.png

Aufbau einer Kundendienst-Chat-App mit Rails 5 Action Cable und SMS

Zuletzt aktualisiert am May 13, 2021

Lesedauer: 9 Minuten

Rails 5 wurde mit einigen erstaunlichen neuen Funktionen ausgeliefert wie Turbolinks 5 und den API-Modus, aber die Funktion, die uns ins Auge fiel, ist die neue integrierte WebSocket-Integration mit Action Kabel. Diese neue Abstraktion um WebSocket ist direkt in Rails integriert und eignet sich perfekt für Echtzeit-Ereignisse und Zwei-Wege-Kommunikation. Heute werden wir einen Blick darauf werfen, wie man einen Kundenservice darauf aufbaut und die Nexmo SMS API.

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.

Ein Kundendienst-Chat in Ruby on Rails 5

In diesem Tutorial erstellen wir ein Zwei-Wege-Chat-System für die Kundenbetreuung für ein Unternehmen, das nicht nur über seine Website, sondern auch per SMS direkt mit seinen Kunden kommunizieren möchte. Die Web-App wird die Schnittstelle für den "Kundensupport-Agenten" sein und von Action Cable betrieben, während die Kunden ihr Telefon benutzen können, um Textnachrichten direkt an den Support-Agenten zu senden.

Customer Service Chat with Rails 5 Action Cable and SMS

Um die Sache zu vereinfachen, erstellen wir einen Startpunkt für unsere Anwendung und stellen sie auf Github. Es ist eine einfache Rails 5 Anwendung mit einem MessagesController zum Erstellen neuer Nachrichten in einem Thread. Wir haben einige Fake-Daten über die Seed-Datei und eine hübsche UI mit Hilfe von Semantischen UI.

Stellen Sie sicher, dass Sie Ruby 2.2.2 oder höher installiert haben. Sie können dann den Ausgangspunkt dieses Tutorials von GitHub herunterladen, wenn Sie mitprogrammieren möchten.

git clone git@github.com:nexmo-community/nexmo-customer-service-chat-demo.git cd nexmo-customer-service-chat-demo bundle install rails db:create db:migrate db:seed bundle exec rails server

Dann besuchen Sie localhost:3000 in Ihrem Browser und Sie sollten in der Lage sein, die bestehenden Themen zu kommentieren, aber es wird noch nichts per SMS verschickt.

Der gesamte Code für diesen Ausgangspunkt befindet sich auf der Seite vor Zweig auf Github. Der gesamte Code, den wir im Folgenden hinzufügen werden, befindet sich im nach Zweig. Zu Ihrer Bequemlichkeit können Sie alle Änderungen zwischen unserem Start- und Endpunkt auch auf Github einsehen.

Nexmo SMS und Schienen 5 Action Kabel

Die Nexmo SMS API bietet Ihnen eine niedrige Latenzzeit und eine hohe Zustellbarkeit von Nachrichten, die sich perfekt für den Echtzeit-Chat im Kundenservice eignen. Wir werden uns zwei Kernelemente ansehen: das Senden und Empfangen von Textnachrichten und deren Live-Darstellung in der Web-App mit Action Cable.

Zu diesem Zweck werden wir die folgenden Änderungen an unserer Anwendung vornehmen:

  • Anhören eingehender Textnachrichten

  • Neue Nachricht in unserer UI mit Action Cable anzeigen

  • Nexmo zu unserer App hinzufügen

  • Senden neuer Nachrichten per SMS vom Service Agent (über Nexmo)

Eingehende SMS-Nachrichten mit Action Cable anzeigen

Unser erster Schritt besteht darin, unserer Anwendung eingehende SMS-Nachrichten hinzuzufügen. Dazu benötigen wir eine Nexmo-Telefonnummer, die SMS-Nachrichten senden und empfangen kann.

Incoming Messages

Sie können eine Number über das Nexmo Dashboardkaufen, oder Sie können die nexmo-cli Bibliothek verwenden und eine Nummer direkt über die Kommandozeile kaufen. Ihre API-Zugangsdaten finden Sie auf der der Einstellungsseite deines Nexmo Accounts

Zum Beispiel, um eine britische Telefonnummer zu kaufen, die mit 07 beginnt:

> npm install -g nexmo-cli
> nexmo setup <your_api_key> <your_api_secret>
> nexmo number:buy GB 447* --confirm
Number purchased
> nexmo number:list
4475555555555

Wenn eine SMS von dieser neuen Nummer empfangen wird, ruft Nexmo die von uns angegebene Webhook-URL auf. Wir erstellen zunächst einen Dummy-Webhook, der nur eine einfache JSON-Antwort zurückgibt.

# app/controllers/text_messages_controller.rb
class TextMessagesController < ApplicationController
  include ApplicationHelper
  
  skip_before_action :verify_authenticity_token, :only => [:create]
  
  # If webhooks are set up as GET requests
  def index
    render json: { state: 200 }
  end
  
  # If webhooks are set up as POST requests
  def create
    render json: { state: 200 }
  end
end

Fügen wir auch dies zu unserem routes.rb.

# config/routes.rb
Rails.application.routes.draw do
  resources :text_messages
  ...
end

Wenn Sie nun laden localhost:3000/text_messages aufrufen, sollten Sie eine JSON-Antwort sehen.

Um Ihre App über die Nexmo-Webhooks öffentlich erreichbar zu machen, haben Sie mehrere Möglichkeiten. Wenn Sie das Glück haben, eine öffentliche IP auf Ihrem Rechner zu haben, sollten Sie bereit sein, loszulegen. Für den Rest von uns könnten wir entweder die App bereitstellen, einen SSH-Tunnel verwenden oder meine Lieblingslösung: das erstaunliche ngrok Werkzeug.

Nach der Installation können Sie eine öffentliche URL abrufen:

ngrok http 3000

Sobald Ihre App öffentlich zugänglich ist, können wir unsere Nummer mit einer Webhook-URL verknüpfen. Nun wird jedes Mal, wenn eine SMS empfangen wird, ein Anruf an diese URL getätigt. Hierfür verwenden wir wieder die nexmo-cli.

nexmo link:sms 44755555555 http://.ngrok.io/text_messages Number updated

Wenn Sie dabei Fehler erhalten, vergewissern Sie sich bitte, dass Sie die Nexmo-Telefonnummer in Ihrem Account verwenden und dass die Webhook-URL öffentlich zugänglich ist.

Der nächste Schritt besteht darin, unsere Aktion so zu erweitern, dass sie die eingehende Nachricht entgegennimmt, die Antwort analysiert und in unserer Datenbank speichert.

# app/controllers/text_messages_controller.rb

# If webhooks are set up as GET requests
def index
  create_message(params)
end

# If webhooks are set up as POST requests
def create
  create_message(params)
end

def create_message(params)
  message = Message.create!(
    number: params[:msisdn],
    text: params[:text],
    inbound: true
  )
  render json: { state: 200 }
end

Probieren Sie es aus! Starten Sie Ihren Server (und ngrok, falls nötig) und senden Sie eine Nachricht an Ihre Nexmo-Nummer. Innerhalb weniger Sekunden sollte die Nachricht von Ihrer App geparst werden. Aktualisieren Sie die Website, um Ihre neue Nachricht zu sehen.

Anzeigen von Nachrichten mit Action Cable

Natürlich wollen wir unsere Seite nicht jedes Mal aktualisieren müssen, wenn eine neue Nachricht eintrifft, und genau hier kommt Action Cable ins Spiel.

Action Cable verwendet Kanäle für die Kommunikation zwischen Herausgebern und Abonnenten. In unserem Fall werden wir die number und die html unserer neuen Nachricht an den messages Kanal.

Wir beginnen damit, dass wir eine neue Zeile in unsere TextMessagesController:

# app/controllers/text_messages_controller.rb
def create_message(params)
  ...
  send_cable(message)
  render json: { state: 200 }
end

Wie Sie sehen können, haben wir unsere Veröffentlichung in eine Hilfsmethode extrahiert, da wir diese später wiederverwenden wollen.

Unser Helfer ist ziemlich einfach - er rendert das HTML und übergibt dann das HTML und die Nummer an die ActionCable.server.broadcast Methode.

# app/helpers/application_helper.rb
def send_cable message
  html = render_message(message)
  ActionCable.server.broadcast 'messages',
    number: message.number,
    html: html
end

def render_message message
  ApplicationController.render({
    partial: 'messages/message',
    locals: { message: message }
  })
end

Um die Nachricht auf dem Front-End zu empfangen, müssen wir eine Verbindung zu unserem Server über einen neuen WebSocket herstellen und den messages Kanal auf neu eintreffende Nachrichten.

Wir beginnen mit dem Einbau von ActionCable in unseren Routen.

# config/routes.rb
Rails.application.routes.draw do
  mount ActionCable.server => '/cable'
  ...
end

Dadurch wird ein WebSocket-Endpunkt auf http://localhost:3000/cable mit dem wir uns mit unserem Javascript wie folgt verbinden können.

// app/assets/javascripts/channels/messages.js
App.cable.subscriptions.create('MessagesChannel', {
  received: function(data) {
    // process data
  }
});

Woher weiß Rails also, dass die MessagesChannel auf den messages Stream gehört? Es weiß es nicht. Wir müssen dies selbst festlegen.

# app/channels/messages_channel.rb
class MessagesChannel < ApplicationCable::Channel
  def subscribed
    stream_from "messages"
  end
end

In komplizierteren Beispielen könnten Sie den Namen des Kanals dynamisch auf der Grundlage des authentifizierten Benutzers, zusätzlicher Parameter und vieles mehr erstellen. In unserem Beispiel halten wir es einfach und kodieren lediglich ein Abonnement für den messages Stream.

Schließlich müssen wir unser JS aktualisieren, um den HTML-Code, den wir über das Kabel erhalten haben, in unsere Benutzeroberfläche einzufügen.

// app/assets/javascripts/channels/messages.js
App.cable.subscriptions.create('MessagesChannel', {
  received: function(data) {
    var list      = $('.numbers');
    var thread    = $('.thread');
    var number    = thread.data('number');
    var latest    = $('.message[data-number="'+data.number+'"]');

    // prepend to message thread
    if (thread.length &&
        data.number == number) thread.prepend(data.html);

    // prepend to list of ongoing threads
    if (list.length) {
      latest.remove();
      list.prepend(data.html);
    }

    $('.message:first').transition('flash');
  }
});

Dieser Code erfüllt mehrere Aufgaben. Zunächst einmal findet er die .thread und .numbers Elemente innerhalb des DOMs. Wenn wir Nachrichten anzeigen (in der views/messages/show.html.erb Ansicht) für die Nummer, von der die eingehende Nachricht empfangen wurde, stellen wir die Nachricht voran. Wenn wir uns in der index Ansicht (views/messages/index.html.erb), die die letzte Nachricht von allen Nummern anzeigt, ersetzen wir die vorhandenen Nachrichtendetails durch das neue HTML.

Damit das alles funktioniert, müssen wir ein data-number Attribut zu dem .thread Element und jedem .message Element ein Attribut hinzufügen, damit wir wissen, auf welche Nummer sie sich beziehen.

<!-- app/views/messages/show.html.erb -->
<div class="ui one cards thread" data-number="<%= params[:id] %>">     

<!-- app/views/messages/_message.html.erb -->
<%= link_to "/messages/#{message.number}",
      class: 'ui card message',
      data: { number: message.number } do %>

Das war's für Action Cable! Starten Sie Ihren Server (und ngrok, falls nötig) und senden Sie eine Nachricht an Ihre Nexmo-Nummer. Innerhalb weniger Sekunden sollte die Nachricht von Ihrer App geparst werden und diesmal müssen Sie die Website nicht aktualisieren, um die neue Nachricht zu sehen. Stattdessen wird sie angezeigt, sobald Action Cable sie im messages Stream veröffentlicht.

Beantwortung von Nachrichten

Jetzt, wo die eingehenden SMS funktionieren, können wir unseren bestehenden Code aktualisieren, um neue Nachrichten vom Serviceagenten direkt an die Telefonnummer des Kunden zu senden.

Incoming Messages

Um eine SMS-Nachricht über Nexmo zu versenden, müssen wir das nexmo gem zum Projekt hinzufügen.

# Gemfile
gem 'nexmo'

group :development, :test do
  gem 'dotenv-rails'
end

Wie Sie sehen können, haben wir auch die dotenv-rails gem. Dies dient nur der Vereinfachung, da es der App erlaubt, unsere API-Anmeldedaten aus einer .env Datei zu laden. Das nexmo holt sich automatisch diese Umgebungsvariablen und verwendet sie, um den Client zu initialisieren. Sie finden Ihre Anmeldedaten auf der der Einstellungsseite deines Nexmo Accounts.

# .env
NEXMO_API_KEY=<your_api_key>
NEXMO_API_SECRET=<your_api_secret>
NEXMO_NUMBER=<your_nexmo_number>

Wir haben auch unsere NEXMO_NUMBER in die .env Datei auch hier hinzugefügt.

Als nächstes verwandeln wir unser neues Nachrichtenformular in ein remote Formular und verwenden ActionCable um dem Kunden neue Eingaben zu zeigen, anstatt die Seite weiterzuleiten.

<!-- app/views/messages/_form.html.erb -->
<%= form_for(@new_message, remote: true, html: { class: 'ui form error' }) do |f| %>

In unserem Controller werden wir den Redirect durch etwas Bekanntes ersetzen.

# app/controllers/messages_controller.rb
if message.save
  send_cable(message)
  send_sms(message)
end

Die send_cable ist unser Action Cable Publisher von vorhin, und die send_sms wird als nächstes implementiert werden.

Bevor wir jedoch fortfahren, sollten wir eine create.js.erb erstellen, damit sich unsere Aktion nicht über eine fehlende Ansicht beschwert. Wir werden diese Ansicht verwenden, um auch unsere textarea wenn das Formular abgeschickt wird.

# app/views/messages/create.js.erb
$('textarea').val('');

Zum Schluss senden wir die SMS an die richtige Nummer mit der Nachricht des Kundendienstmitarbeiters.

# app/helpers/application_helper.rb
def send_sms message
  Nexmo::Client.new.send_message(
    from: ENV['NEXMO_NUMBER'],
    to: message.number,
    text: message.text
  )
end

Das war's. Sie sollten nun mit Hilfe von Nexmo und dem Action Cable von Rails 5 eine vollständige 2-Wege-SMS an Rails senden können. Starten Sie Ihren Server bei Bedarf neu und senden Sie sich selbst einige Nachrichten, um das Ganze in Aktion zu sehen.

Nächste Schritte

Offensichtlich haben wir eine Menge cooler Dinge übersprungen, sowohl bei Action Cable als auch bei der Nexmo SMS API. Wir haben den Kundenbetreuer nicht authentifiziert, und wir haben keine Web-UI für den Kunden bereitgestellt - was interessant wäre, um auf der Nexmo Verify API wie wir es mit dem Beitrag über Zwei-Faktor-Authentifizierung (2FA) in Ruby on Rails mit Devise und Nexmo Verify.

Ich werde spielen mit Turbolinks 5 spielen, um zu sehen, ob ich dies in ein natives mobiles Erlebnis einbauen kann. Ich würde gerne wissen, was Sie als nächstes hinzufügen würden? Bitte schreiben Sie mir einen Tweet (ich bin @cbetta) mit Ihren Gedanken und Ideen.

Teilen Sie:

https://a.storyblok.com/f/270183/400x400/73e68604be/phil-leggetter.jpg
Phil Leggetter

Phil is Head of Developer Relations at Hookdeck, an asynchronous messaging platform, and a proud Vonage alumni.