
Aufbau von Textnachrichten-Gruppenchats mit der Nexmo SMS API und PHP
Um die neue PHP-Client-Bibliothek ein wenig zu üben, werden wir einen einfachen SMS-Gruppenchat erstellen, bei dem die eingehende Nachricht eines Benutzers an alle anderen Mitglieder des Chats gesendet wird. Sie können hier mitmachen und den Chat mit mir bauen, oder Sie klonen einfach das Nexmo SMS Gruppenchat Repository und es in Aktion sehen.
Ich werde es nicht verurteilen, wenn Sie einfach das Repo klonen, wirklich nicht. Viel.
Was wir bauen
Wir werden ein einfaches Skript erstellen, das:
Ermöglicht Benutzern die Eingabe von Text
JOINund ihren Namen (z. B.JOIN tlytle) an eine Telefonnummer zu senden, wobei die Telefonnummer eine "Gruppe" darstellt.Nach dem Beitritt wird jede Nachricht, die ein Benutzer sendet, an den Rest der Gruppe weitergeleitet. Und die Benutzer erhalten jede Nachricht, die jemand anderes sendet.
Wenn ein Nutzer beschließt, dass er nicht mehr Teil der Gruppe sein möchte, wird er
LEAVEwird er abgemeldet.
In einem späteren Beitrag werden wir auch eine einfache Webschnittstelle einrichten, über die sie ein Protokoll der Nachrichten der Gruppe einsehen können.
Einrichten
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.
Wir beginnen mit der Nexmo-Client-Bibliothekund einer Mongo-Datenbank. Die Einrichtung der Datenbank würde den Rahmen dieses Tutorials sprengen, aber es gibt einige Mongo-Hosts mit kostenlosen Tiers, und die Einrichtung einer Datenbank auf einem von ihnen sollte ziemlich einfach sein. Sie benötigen außerdem den Mongo-Treiber für PHP installiert haben.
Sie können diese und die Nexmo-Client-Bibliothek in Composer einbinden:
$ composer require nexmo/client 1.0.*@beta
$ composer require mongodb/mongodbDurch die Definition einer einfachen Konfigurationsdatei können wir unsere API-Anmeldedaten und die Datenbankverbindung in einer einzigen Datei speichern.
Hier ist ein Beispiel config.php zur Verwendung als Vorlage:
<?php
return [
'mongo' => [
'uri' => 'mongodb://user:password@host:port',
'database' => 'groupchat'
],
'nexmo' => [
'key' => 'key',
'secret' => 'secret'
]
];Eine sehr einfache Bootstrap (bootstrap.php) Datei kümmert sich um das Autoloading und gibt die Konfigurationsdatei weiter:
<?php
$autoloader = require __DIR__ . '/vendor/autoload.php';
$config = require __DIR__ . '/config.php';
$config['autoloader'] = $autoloader;
return $config;Mit diesen beiden Dateien sind wir bereit, unsere Gruppenchat-Anwendung zu erstellen.
Behandlung eingehender Textnachrichten
Wir brauchen ein Skript, das Folgendes annimmt eingehende Webhooks von Nexmo annimmt und die Nachricht verarbeitet. Erstellen Sie also ein public/inbound.php und darin ein Include ../bootstrap.phperstellen Sie einen Nexmo-Client und einen Mongo-Client.
<?php
$config = require __DIR__ . '/../bootstrap.php';
$nexmo = new \Nexmo\Client(new \Nexmo\Client\Credentials\Basic($config['nexmo']['key'], $config['nexmo']['secret']));
$mongo = new \MongoDB\Client($config['mongo']['uri']);
$db = $mongo->selectDatabase($config['mongo']['database']);Als Nächstes wollen wir eine eingehende Nachricht aus einer eingehenden Anfrage erstellen und prüfen, ob sie gültig ist. Die Client-Bibliothek bietet eine einfache Möglichkeit, dies zu tun.
$inbound = \Nexmo\Message\InboundMessage::createFromGlobals();
if(!$inbound->isValid()){
error_log('not an inbound message');
return;
}Jetzt, wo die Datei eingerichtet ist, können Sie das Nexmo Dashboardund eine Nummer auf das Skript zeigen in das Callback URL Feld.

Dadurch wird Ihr Nexmo Account so konfiguriert, dass er eine Webhook-Anfrage an das Skript stellt, sobald eine Nachricht an diese Nummer gesendet wird. Wenn Sie lokal entwickeln, müssen Sie etwas verwenden wie ngrok verwenden, um einen lokalen Tunnel mit einer öffentlichen URL zu erstellen, die die Nexmo-Plattform erreichen kann.
Sobald Sie die Nummer konfiguriert haben, können Sie ihr eine Nachricht senden - aber sie wird nichts tun. Lassen Sie uns also dem Absender mit einigen Anweisungen antworten. Das von der Client-Bibliothek erstellte Objekt für eingehende Nachrichten hat eine createReply Methode, die die eingehenden Webhook-Daten verwendet, um eine Antwort zu erstellen, indem sie die to und die from.
$nexmo->message()->send($inbound->createReply('Use JOIN [your name] to join this group.'));Schicken Sie nun eine Nachricht an Ihre Nexmo-Nummer, und Sie sollten eine schnelle Antwort erhalten.
Da wir die mit dem eingehenden Webhook gesendeten Parameter verwenden, muss unser Code nicht wissen, welche Nummer als Absender zu verwenden ist. Warum ist das wichtig? Jetzt haben wir ohne zusätzlichen Code oder Konfiguration einen einfachen Autoresponder, der so viele Nexmo Numbers - und damit auch Gruppenchats - unterstützt, wie wir ihm zuweisen.
Verarbeitung von Befehlen
Bevor wir wirklich Befehle verarbeiten können JOIN Befehle verarbeiten können, müssen wir wissen, ob der Benutzer bereits mit dem System interagiert hat. Es ist also an der Zeit, einige Abfragen zu schreiben. Wir richten die Dinge mit einer users Sammlung. Und wir erwarten, dass jedes Dokument die Eigenschaft group Eigenschaft auf die eingehende Nexmo-Nummer gesetzt ist, an die die Nachricht gesendet wurde. Die user wird auf die Nummer des Benutzers gesetzt (die Nummer, von der die Nachricht gesendet wurde).
$user = $db->selectCollection('users')->findOne([
'group' => $inbound->getTo(), // the group's number
'user' => $inbound->getFrom() //the user's number
]);Fügen wir ein einfaches Fehlerprotokoll hinzu, damit wir bei Bedarf eine Fehlersuche durchführen können:
if($user){
error_log('found user: ' . $user['name']);
} else {
error_log('no user found');
}Da sich keine Daten in der Datenbank befinden, sollte jede Meldung an dieser Stelle protokolliert werden no user found. Jetzt, wo wir den Code für die Verwendungsprüfung haben, können wir nach Befehlsschlüsselwörtern suchen.
Wir verwenden das erste Wort, um zu prüfen, ob der Benutzer einen Befehl sendet. Da der JOIN Befehl auch einen Namen erwartet, müssen wir die Nachricht in einen einzelnen Befehl als erstes Wort und ein optionales Argument danach zerlegen. Die Verwendung eines regulären Ausdrucks zur Aufteilung auf ein beliebiges Leerzeichen und die Begrenzung auf 2 Elemente gibt uns, was wir brauchen. Mit einem geparsten Befehl, einem switch lässt uns auf dieses erste Wort reagieren:
$command = preg_split('#\s+#', $inbound->getBody(), 2);
switch(strtolower(trim($command[0]))){Prüfen wir zunächst, ob das erwartete zweite Argument auch angegeben wurde - zumindest bei neuen Benutzern. Ist dies nicht der Fall, ist das Senden einer Antwort einfach, wir verschieben einfach die Antwort, die wir bereits haben, hierher:
case 'join';
error_log('got join command');
if(!$user && empty($command[1])){
$nexmo->message()->send($inbound->createReply('Use JOIN [your name] to join this group.'));
break;
}Wenn es sich um einen neuen Benutzer handelt (kein vorhandener Benutzer gefunden) und er einen Namen angegeben hat ($command['1'] wurde nicht empty()), sollten wir die grundlegenden Benutzerdaten einrichten:
if(!$user){
$user = [
'group' => $inbound->getTo(),
'user' => $inbound->getFrom(),
'actions' => []
];
}Und vergessen wir den Namen nicht. Warum machen wir es außerhalb von der Prüfung eines neuen Benutzers? Um einem bestehenden Benutzer die Möglichkeit zu geben, seinen Namen mit dem JOIN Befehl zu aktualisieren, wenn er einen neuen Namen angibt. Da wir sicherstellen, dass neue Benutzer das zweite Argument haben, wissen wir, dass jeder neue Benutzer auch den Namen gesetzt hat:
if(isset($command[1])){
$user['name'] = $command[1];
}Da es sich um einen JOIN Befehl ist, müssen wir auch den Status des Benutzers auf aktiv setzen und einen Protokolleintrag für die Aktion erstellen.
$user['status'] = 'active';
$user['actions'][] = [
'command' => 'join',
'date' => new \MongoDB\BSON\UTCDatetime(microtime(true))
];Jetzt müssen wir nur noch den Benutzer speichern (oder erstellen). Wir verwenden den Mongo replaceOne Befehl und lassen ihn das Dokument einfügen (upsert), falls erforderlich, und fügen break damit wir die Verarbeitung beenden, sobald die Aktion ausgeführt wurde:
$db->selectCollection('users')->replaceOne([
'group' => $inbound->getTo(), // the group's number
'user' => $inbound->getFrom() //the user's number
], $user, ['upsert' => true]);
error_log('added user');
break;JOINhalbwegs am Ziel, aber wir müssen immer noch Benutzer zu einer Gruppe zulassen. LEAVE eine Gruppe. Wie JOIN werden wir ein wenig protokollieren und überprüfen, ob der Benutzer tatsächlich angemeldet ist - er kann nicht wirklich gehen, wenn er es nicht ist. Wenn sie nicht angemeldet sind, antworten wir einfach mit einer Hilfestellung. Was, wie wir herausgefunden haben, ziemlich einfach zu machen ist:
case 'leave';
error_log('got leave command');
if(!$user){
$nexmo->message()->send($inbound->createReply('Use JOIN [your name] to join this group.'));
break;
}Wenn sie ein Abonnement abgeschlossen haben, müssen wir den Abonnementstatus aktualisieren und protokollieren, dass die Aktion durchgeführt wurde. Dies geschieht durch Ändern der status Eigenschaft und das Anhängen eines neuen Mitglieds an das actions Array. Natürlich ist es auch wichtig, diese Änderung in die Datenbank zu schreiben:
//update the user's status
$user['status'] = 'inactive';
$user['actions'][] = [
'command' => 'leave',
'date' => new \MongoDB\BSON\UTCDatetime(microtime(true))
];
//update the database
$db->selectCollection('users')->replaceOne([
'group' => $inbound->getTo(), // the group's number
'user' => $inbound->getFrom() //the user's number
], $user);Nachdem wir den Benutzer aus der Gruppe entfernt haben, sollten wir ihm mitteilen, dass er die Gruppe verlassen hat und wie er ihr in Zukunft wieder beitreten kann:
//let them know they've left
$nexmo->message()->send($inbound->createReply('You have left. Use JOIN to join this group again.'));
error_log('removed user');
break; SMS-Gruppenchat durch Weiterleitung von Nachrichten
Nach dem Beitritt zur Gruppe und dem Verlassen der Gruppe müssen wir uns nun darum kümmern, dass ein Benutzer eine Nachricht und keinen Befehl sendet. Jede Nachricht, die kein Befehl ist, ist eine Nachricht an die Gruppe. Die Logik hier ist einfach: Wenn der Benutzer angemeldet und aktiv ist, sollte seine Nachricht an alle anderen Mitglieder gesendet werden.
Wir müssen prüfen, ob der Benutzer in der Lage ist, eine Nachricht an die Gruppe zu senden. Wenn wir einen Benutzer in der Datenbank gefunden haben, bedeutet das, dass er irgendwann einmal die Gruppe abonniert hat, aber wir müssen überprüfen, ob er sie verlassen hat. Wenn einer der beiden Fälle nicht zutrifft - sie werden nicht in der Datenbank gefunden oder sind nicht in der Gruppe aktiv -, senden wir eine kurze hilfreiche Antwort:
default:
error_log('no command found');
if(!$user || 'active' != $user['status']){
$nexmo->message()->send($inbound->createReply('Use JOIN [your name] to join this group.'));
break;
}Wenn sie abonniert und aktiv sind, erstellen wir ein Archiv ihrer Nachricht. Dieses enthält den Text, die Gruppe, an die die Nachricht gesendet wurde, den Benutzer selbst (sowie seinen Namen, um zu vermeiden, dass der Benutzer jedes Mal gesucht werden muss, wenn der Name benötigt wird) und andere Metadaten.
Wir erstellen auch ein leeres sends Array, um die an die anderen Benutzer der Gruppe gesendeten Nachrichten zu protokollieren:
error_log('user is active');
$log = [
'_id' => $inbound->getMessageId(),
'text' => $inbound->getBody(),
'date' => new \MongoDB\BSON\UTCDatetime(microtime(true)),
'group' => $inbound->getTo(),
'user' => $inbound->getFrom(),
'name' => $user['name'],
'sends' => []
];Um alle Mitglieder zu finden, die die Nachricht weiterleiten müssen, fragen wir die users nach allen Benutzern in dieser Gruppe ab, die als aktiv markiert sind. Wir müssen daran denken, den aktuellen Benutzer auszuschließen (das ist, was das $ne bedeutet "nicht gleich"), aber es kann praktisch sein, dies zu Testzwecken zu entfernen:
$members = $db->selectCollection('users')->find([
'group' => $inbound->getTo(),
'user' => ['$ne' => $inbound->getFrom()],
'status' => 'active'
]);Sobald wir diese Liste haben, können wir sie durchgehen und jedem Mitglied eine Nachricht schicken. Wir können ein einfaches Array an die send() Methode übergeben (ebenso wie ein Message Objekt). Dieses Array verwendet die Nummer des Mitglieds als to, die Nummer der Gruppe als from, und wir fügen den Namen des Benutzers, der die Nachricht gepostet hat, in das Feld text bevor die Nachricht gesendet wird.
Das gibt ein vollständiges Nachrichtenobjekt zurück. Wir könnten es als Array behandeln, aber es ist einfacher, nur die Getter-Methoden zu verwenden, um die Nachrichten-ID und die Nummer des Mitglieds zum Sendeprotokoll hinzuzufügen.
foreach($members as $member) {
$sent = $nexmo->message()->send([
'to' => $member['user'],
'from' => $inbound->getTo(),
'text' => $user['name'] . ': ' . $inbound->getBody()
]);
$log['sends'][] = [
'user' => $sent->getTo(),
'id' => $sent->getMessageId()
];
}Sobald alle Nachrichten gesendet wurden, fügen wir die neue Nachricht der Protokollsammlung in der Datenbank hinzu, und die Verarbeitung der eingehenden Nachrichten ist abgeschlossen.
$db->selectCollection('logs')->insertOne($log);
error_log('relayed message');
break;
} // end of switch Nächste Schritte
Und damit haben wir ein einfaches Skript eingerichtet, das eingehende Nachrichten annimmt, einige davon beantwortet und andere an eine Gruppe weiterleitet. Zentral könnte das Befehlskonzept zu komplexeren und interaktiven Auto-Responder-Bots erweitert werden, das Gruppenrelais könnte in zwei Benutzer-Proxys umgewandelt werden, die nur die Nummern der Benutzer maskieren, oder es könnte als SMS-Verteilerliste umfunktioniert werden, die es jedem erlaubt, eine eingehende Nachricht an eine Gruppe von Personen zu senden.

Die Verarbeitung eingehender Nachrichten und der Versand ausgehender Nachrichten ist mit der PHP-Client-Bibliothek und der Nexmo-API ein Kinderspiel.
Zu dieser Demo (die Sie einfach klonen und ausführen können, wenn Sie wollen) gibt es noch ein bisschen mehr, und wir werden im zweiten Teil dieses Tutorials eine Webschnittstelle zu unserem Gruppenchat erstellen.
