Erstellen eines Foto-Gästebuchs mit WhatsApp, Node.js und Twilio

April 23, 2023
Autor:in:
Prüfer:in:
Diane Phan
Twilion


Hallo und danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von Build a Photo Guest Book With WhatsApp, Node.js and Twilio.

Geburtstagsfeiern, Hochzeiten, Jubiläen und andere gesellschaftliche Veranstaltungen sind oft wunderbare Ereignisse, an dir wir uns ein Leben lang erinnern möchten. Bei solchen Ereignissen werden wir alle zu Fotograf:innen, die versuchen, die schönsten Momente auf ihrem Handy festzuhalten und anschließend mit den Gastgeber:innen zu teilen. Aber die meisten Partygästen bekommen die Fotos normalerweise nie zu Gesicht, weil sie nicht geteilt werden. Eine gängige Lösung hierfür sind Apps zum Teilen von Fotos, auf denen die aufgenommenen Fotos hochladen können.

Das Problem bei solchen Apps besteht darin, dass sie zunächst von allen installiert werden müssen und oft mit einem komplizierten Setup-Prozess verbunden sind. Und am Ende sieht nur ein Bruchteil der Gäste diese Erinnerungen.

Um dagegen etwas zu unternehmen, beschloss ich, auf einem Messenger aufzubauen, den fast alle bereits nutzen: WhatsApp. WhatsApp-Nutzer:innen wissen bereits, wie sie mit der App Bilder senden und Nachrichten empfangen können.

In diesem Artikel zeige ich Ihnen, wie ich Twilio und die WhatsApp Business API verwendet habe, um ein Foto-Gästebuch für eine Hochzeit zu erstellen, bei der ich neulich anwesend sein durfte.

Voraussetzungen

Sie können den Bot auch mit der Twilio Sandbox für WhatsApp erstellen. Das Onboarding-Erlebnis wird etwas komplizierter, da Sie zuerst der Sandbox beitreten müssen, aber das ist für Entwicklungszwecke akzeptabel.

Leistungsmerkmale und Funktionen

Die größte Herausforderung bei einer kollaborativen Fotogalerie besteht darin, die Gäste zur Teilnahme zu motivieren. Deshalb muss der Onboarding-Prozess so bequem wie möglich sein. Eine Website, die den Ablauf erläutert, ist einigen Gästen, die einfach nur die Party genießen möchten, möglicherweise schon zu kompliziert.

Um die erforderlichen Interaktionen für die Nutzer:innen zu minimieren, ermöglichen wir es ihnen, den WhatsApp-Bot direkt anzuschreiben. Hierfür finde ich QR-Codes, die auf WhatsApp-URLs verweisen, sehr praktisch. Die Nutzer:innen müssen den Code nur scannen, und WhatsApp öffnet sich automatisch mit einem Vorschlag für die erste Nachricht. In unserem Fall machen wir uns keine Gedanken über die Nachricht selbst und antworten mit den Anleitungen und einem Link zu der Website, die alle Bilder enthält. Parallel dazu planen wir eine Nachricht, die nach 23 Stunden gesendet wird, um die Nutzer:innen nochmals zu fragen, ob sie weitere Fotos an den Bot senden möchten.

Wenn die Nutzer:innen ihre erste Mediendatei an unseren Bot senden, bestätigen wir, dass wir sie erhalten haben, und warten auf weitere Bilder. Für alle nachfolgenden Mediendateien senden wir keine Bestätigung, da die Nutzer:innen sie beim gleichzeitigen Senden mehrerer Bilder als lästig empfinden könnten. Weiterhin prüfen wir, ob in der Nachricht eine Mediendatei enthalten ist, um sicherzustellen, dass der Bot korrekt verwendet wird. Wenn sie nur Text enthält, erinnern wir die Nutzer:innen daran, wie der Bot funktioniert.

Ich lebe in Europa, wo WhatsApp der am häufigsten verwendete Messenger ist und Menschen nur selten Mediendateien per "SMS" (bezeichnet als MMS) senden. Einer der vielen großen Vorteile der Twilio-Plattform ist, dass sie für diese beiden Kanäle praktisch gleich funktionieren. Sie müssten nur geringfügige Änderungen vornehmen, um MMS anstelle von WhatsApp zu verwenden, wenn Sie beide Kanäle nutzen möchten.

Erstellen des Back-Ends

Um diese Funktionen zu erstellen, müssen wir zwei API-Endpunkte bereitstellen. Der erste ist die Webhook, der für jede eingehende WhatsApp-Nachricht aufgerufen wird. Der zweite gibt dann eine Liste aller Bilder zurück, um sie auf einer Website zu visualisieren. Für diesen Blogbeitrag implementieren wir diese Endpunkte in Node.js und führen sie auf der Twilio Serverless-Plattform aus.

Der erste Schritt besteht darin, ein Projekt anzulegen:

twilio serverless:init --template=blank twilio-whatsapp-photo-guest-book
cd twilio-whatsapp-photo-guest-book

Beginnen wir mit der Funktion, die eingehende Nachrichten verarbeitet. Zuerst initialisieren wir das Antwortobjekt und laden die vorherige Unterhaltung von diesem Absender. Wir prüfen, ob dies die erste Nachricht ist und ob ein Bild an die Nachricht angehängt ist oder nicht.

Je nach Ergebnis dieser Prüfungen reagiert der Code unterschiedlich. Wie oben beschrieben, ist die Antwort entweder eine Einführungsnachricht für Erstnutzer:innen mit einer URL zum Auffinden der Bilder, eine Bestätigung des Empfangs des ersten Bildes oder eine Nachricht, dass kein Bild in der Nachricht gefunden wurde.

Benennen Sie die Datei functions/blank.js in functions/incoming.js um und fügen Sie den folgenden Code hinzu.

exports.handler = async function (context, event, callback) {
   const client = context.getTwilioClient();
   const twiml = new Twilio.twiml.MessagingResponse();

   const firstMediaResponse = "Danke für das Senden des ersten Bildes, du bist großartig!\nPS: Ich werde keine weiteren Bestätigungen senden";

   const prevCorrespondence = await client.messages.list({
       to: event.From,
       from: event.To,
   });

   const isFirstMessage = prevCorrespondence.length === 0,
       hasReceivedImagesBefore = prevCorrespondence.some(
           (m) => m.body.indexOf(firstMediaResponse) >= 0
       );
   if (isFirstMessage) {
       twiml.message(`Danke, dass Sie sich gemeldet haben. Das Teilen von Bildern ist super einfach, schicke sie mir einfach hier und dann kannst du sie auf dieser Website finden:\nhttps://${context.DOMAIN_NAME}/index.html`);
   } else if (event.MediaUrl0) {
       if (!hasReceivedImagesBefore) {
           twiml.message(firstMediaResponse);
       }
   } else if (!event.MediaUrl0) {
       twiml.message("Ich konnte in dieser Nachricht leider kein Bild finden :(");
   }
   callback(null, twiml);
};

Wir möchten für 23 Stunden nach Erhalt der ersten Nachricht eine Erinnerung einplanen. Sie können auch eine Nachricht planen, die zwei oder mehr Tage später gesendet wird, aber dann ist die 24-Stunden-Messaging-Sitzung wahrscheinlich vorbei, und Sie müssen eine vorab genehmigte Nachrichtenvorlage verwenden.

Für dieses Feature ist ein Messaging-Dienst erforderlich, den wir später erstellen. Um diesen Teil optional zu halten, planen wir die Nachricht nur für den Fall, dass es einen Messaging-Dienst gibt. Fügen Sie den folgenden Code nach der Zeile twiml.message(firstMediaResponse); hinzu.

   const tomorrow = new Date();
   tomorrow.setHours(tomorrow.getHours() + 23);

   if(context.MESSAGING_SERVICE_SID){
      client.messages.create({
        messagingServiceSid: context.MESSAGING_SERVICE_SID,
        body: "Wie war die Party gestern? Gibt es noch andere Bilder, die Sie teilen möchten?",
        sendAt: tomorrow,
        scheduleType: "fixed",
        to: event.From,
      });
   }

Praktischerweise speichert Twilio die Dateien für uns, sodass wir uns keine Gedanken darüber machen müssen, wie wir die eingehenden Mediendateien auf einem Cloud-Speicher von Drittanbietern wie AWS S3 Buckets speichern können.

Die zweite Funktion extrahiert alle Mediendateien aus dem Protokoll und stellt sie für die Nutzung durch das Front-End zur Verfügung. Je nach Größe des Nachrichtenprotokolls kann es eine Weile dauern, bis alle gesendeten Dateien empfangen werden, und die Nutzer:innen müssen warten. Um diese Leerlaufzeit zu reduzieren, rufen wir mit Paging nur 15 Nachrichten pro Seite ab und warten, bis die Nutzer:innen die nächste Seite anfordern. Unser Code ruft eine Seite ab und fordert für jede Nachricht die Twilio-API auf, die zugehörigen Medien abzurufen. Anschließend werden die Mediendateien gefiltert und zusammen mit den Metadaten zurückgegeben, einschließlich Telefonnummer, Titel, Versanddatum und Medientyp in einer Rückruffunktion. Der Medientyp unterstützt nicht nur Bilder, sondern auch Videos. Außerdem maskieren wir die Telefonnummer des Absenders, um ein Offenlegen gegenüber anderen Teilnehmenden der Veranstaltung zu vermeiden.

Erstellen Sie eine zweite Datei media.js mit dem folgenden Inhalt, um diesen Endpunkt zu implementieren:

const MaskData = require("maskdata");
const axios = require("axios");

const maskPhoneOptions = {
 maskWith: "*",
 unmaskedStartDigits: 5,
 unmaskedEndDigits: 4,
};
const PAGE_SIZE = 15;

exports.handler = async function (context, event, callback) {
 const client = context.getTwilioClient();
 const page = await client.messages.page({
   to: `whatsapp:${context.NUMBER}`,
   pageSize: PAGE_SIZE,
   pageNumber: event.page || 0,
   pageToken: event.pageToken,
 });

 let files = await Promise.all(
   page.instances.map(async (message) => {
     const phone = message.from.replace("whatsapp:", "");
     const mediaURL = message.subresourceUris.media;
     const mediaRes = await axios({
       url: `https://api.twilio.com${mediaURL}`,
       method: "GET",
       auth: {
         username: process.env.ACCOUNT_SID,
         password: process.env.AUTH_TOKEN,
       },
     });
     const media = mediaRes.data.media_list[0];
     if (!media) {
       return undefined;
     }
     const mediaFileRequest = await axios({
       url: `https://api.twilio.com${media.uri.replace(/\.json$/, "")}`,
       method: "GET",
       headers: { "Content-Type": media.content_type },
       auth: {
         username: process.env.ACCOUNT_SID,
         password: process.env.AUTH_TOKEN,
       },
     });

     const url = mediaFileRequest.request.res.responseUrl;
     const tags = message.body ? [{ value: message.body, title: "caption" }] : [];

     return {
       src: url,
       tags,
       dateSent: message.dateSent,
       sender: MaskData.maskPhone(phone, maskPhoneOptions),
       caption: message.body,
       contentType: media.content_type,
     };
   })
 );

 callback(null, {
   files: files.filter((files) => !!files),
   pageToken: page.nextPageUrl && page.nextPageUrl.match(/PageToken=(.*)/)[1], // no page token means no more pages
 });
};

Da wir zwei zusätzliche Pakete in diesem Code verwenden, müssen wir sie dem Projektdeskriptor package.json hinzufügen. Führen Sie dazu den folgenden Befehl im Projekt-Stammverzeichnis aus.

npm add maskdata axios

Bereitstellen des Back-Ends

Füllen Sie die Platzhalter in der .env-Datei und eine neue Eigenschaft aus, sodass die Datei wie folgt aussieht.

ACCOUNT_SID=<INSERT ACCOUNT SID>
AUTH_TOKEN=<INSERT AUTH TOKEN>
NUMBER=+<INSERT PHONE NUMBER>

Damit können wir das Back-End bereitstellen und einige Beispielbilder in der Galerie abrufen. Führen Sie den Befehl „deploy“ vom Terminal aus, um den Flow auszulösen.

npm run deploy  

Wenn der Prozess abgeschlossen ist, sollten Sie den Abschnitt Deployment Details (Bereitstellungsdetails) im Protokoll sehen. Unter anderem gibt es einen Unterabschnitt mit den Namen Functions (Funktionen), der die URL des „eingehenden“ Endpunkts enthält. Kopieren Sie die URL, die wie folgt aussieht:


Deployment Details
Domain: twilio-whatsapp-photo-guest-book-1860-dev.twil.io
Service:
   twilio-whatsapp-photo-guest-book (ZSabcdef1234)
Environment:
   dev (ZEabcdef1234)
Build SID:
   ZBabcdef1234
Runtime:
   node14
View Live Logs:
   https://www.twilio.com/console/functions/editor/ZSabcdef1234/environment/ZEabcdef1234
Functions:
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/incoming
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/media

Rufen Sie die Twilio Konsole auf, besuchen Sie die Seite Messaging Services (Messaging-Dienste) und klicken Sie auf die Schaltfläche Create Messaging Service (Messaging-Dienst erstellen). Wählen Sie in Schritt 1 einen Anzeigenamen aus und wechseln Sie über die Schaltfläche Create Messaging Service (Messaging-Dienst erstellen) zur nächsten Seite. Wenn Sie nach Absendern gefragt werden, klicken Sie auf die Schaltfläche Add Senders (Absender hinzufügen) und wählen Sie als „Type“ (Typ) die Option „WhatsApp Number“ (WhatsApp-Nummer) und dann die Nummer aus, die Sie mit Ihrem WhatsApp Business-Profil verbunden haben.

Wenn die Nummer hinzugefügt wurde, klicken Sie auf Schritt 3: Integration einrichten. Markieren Sie auf der Seite die Optionsschaltfläche „Send a Webhook“ (Webhook senden) und geben Sie die oben kopierte URL in das Feld „Request URL“ (Anfrage-URL) ein. Schließen Sie die Erstellung des Messaging-Dienstes ab, indem Sie auf Skip Setup (Setup überspringen) klicken.

Screenshot of the Twilio console showing the creation of a messaging service

Auf der nächsten Seite sehen Sie die „Messaging Service SID“ (Messaging-Dienst-SID). Kopieren Sie diese Angabe und hängen Sie eine neue Zeile an die .env-Datei an.

MESSAGING_SERVICE_SID=<INSERT SID HERE>

Wenn Sie stattdessen die WhatsApp-Sandbox verwenden möchten, befolgen Sie die Anweisungen in der Dokumentation, um die Request URL zu konfigurieren. Beachten Sie, dass die erste Nachricht der Join-Code der Sandbox sein muss und dass Sie einer Website keine Kontaktdaten wie ein Profilbild, eine Beschreibung oder einen Link hinzufügen können.

Ausführen der App für das Foto-Gästebuch

Es ist an der Zeit zu testen, ob der Bot eingehende Nachrichten verarbeiten kann. Öffnen Sie WhatsApp auf Ihrem Mobilgerät und senden Sie die erste Nachricht. Senden Sie einige Testbilder an die Nummer, um über Beispieldaten zu verfügen, die Sie anzeigen können.

WhatsApp chat history with submitted images

Wenn Sie zuvor schon mit dieser WhatsApp-Nummer kommuniziert haben, erhalten Sie vielleicht die erste Nachricht (Vielen Dank für die Kontaktaufnahme...). Wenn das passiert, springen Sie zum Download und leeren Sie den Galerieabschnitt, um das Skript clearGallery.js auszuführen.

Testen Sie nun, ob die Bilder in Ihrem Browser von dem oben genannten „Deployment Details“ (Bereitstellungsdetails) Medienendpunkt zugänglich sind. Es sollte eine Antwort wie diese gezeigt werden:

// https://twilio-whatsapp-photo-guest-book-XXXX-dev.twil.io/media
{
  "files": [
    {
      "src": "https://s3-external-1.amazonaws.com/media.twiliocdn.com/AC/a0",
      "tags": [
        
      ],
      "dateSent": "2023-02-01T18:60:00.000Z",
      "sender": "+49*****2",
      "caption": "",
      "contentType": "image/jpeg"
    },
    {
      "src": "https://s3-external-1.amazonaws.com/media.twiliocdn.com/AC/b1",
      "tags": [
        
      ],
      "dateSent": "2023-02-01T18:60:00.000Z",
      "sender": "+49*****2",
      "caption": "",
      "contentType": "image/jpeg"
    }
}]}

Erstellen des Front-Ends

Wir werden diesen Teil über das Front-End kurz halten und uns auf die Entwicklung eines Minimal Viable Product (MVP; „minimal funktionsfähiges Produkt“) konzentrieren. Um es so klein wie möglich zu halten, verwenden wir nur Funktionen und Hooks aus dem Browser und der React-Bibliothek. Die Web-App ruft die Medien-Metadaten von unserem Endpunkt ab und zeigt sie auf einer Seite an.

Erstellen Sie zunächst eine neue React-App innerhalb des Projekts.

npm init react-app frontend

Wir verwenden zwei Hooks aus der React-Bibliothek: useState und useEffect.
Die useState-Hook verwaltet den Status der Komponente, in diesem Fall ein Array von Mediendateien. Die Hook erstellt eine neue Statusvariable mit dem Namen media und eine Funktion mit dem Namen setMedia zum Aktualisieren der Variable.
Die useEffect-Hook wird verwendet, um auf unseren Medienendpunkt zuzugreifen. Die Hook wird mit einem leeren Array als zweites Argument aufgerufen. So weiß React, dass der Effekt nur einmal ausgeführt werden muss, nachdem die Komponente an das DOM angehängt wurde.

Nachdem die Medieninhalte empfangen wurden, ist es Sache der Komponente, die Länge des Arrays zu prüfen. Wenn es leer ist, gibt die Komponente eine Nachricht aus, dass die Mediendateien noch nicht vorhanden sind. Wenn Medienobjekte vorhanden sind, iteriert die Komponente durch das Array, prüft, ob es sich um ein Bild oder ein Video handelt und rendert das richtige HTML-Element. Eine fortschrittlichere Anwendung sollte zwischen einem Lade- und Fehlerstatus unterscheiden, aber das ist nicht Teil dieses MVP (Minimum Viable Product).

Das gesamte MVP wird in der App-Komponente implementiert. Ersetzen Sie die Datei frontend/src/App.js durch den folgenden Inhalt.

import './App.css';
import { useState, useEffect } from "react";


function App() {
  const [media, setMedia] = useState([])
  useEffect(() => {
    fetch("/media").then(
      res => res.json().then(
        payload => setMedia(payload.files)
      )
    )
  }, [])


  if (media.length === 0) {
    return (
      <div className="App">
        <p>Hier sind noch keine Mediendateien vorhanden</p>
      </div>
    )
  }


  return (
    <div className="App">
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(200px, auto))', gap: '0.5rem', margin: '1rem' }}>
        {media.map((media, mediaIdx) => {
          return media.contentType.indexOf("video") ? (
            <img key={mediaIdx} src={media.src} alt={media.caption} loading="lazy" style={{ objectFit: 'cover', width: '100%', height: '200px' }} />
          ) : (
            <video key={mediaIdx} autoPlay controls muted loop style={{ objectFit: 'cover', width: '100%', height: '200px' }} >
              <source src={media.src} type={media.contentType} />
              Ihr Browser unterstützt das Video-Tag nicht.
            </video>
          )
        }
        )}
      </div>
    </div>
  );
}
export default App;

Kennen Sie die hier verwendete object-fit CSS-Eigenschaft bereits? Mit ihr kann man hervorragend sicherstellen, dass die Bilder so zugeschnitten sind, dass sie den zugewiesenen Rahmen füllen, ohne gestreckt werden zu müssen.

Testen wir das lokal: Fügen Sie die folgende Zeile in die Datei package.json des Front-End-Ordners ein, um während der Entwicklung vom lokalen Host aus auf unseren Endpunkt zugreifen zu können.

 "proxy": "https://twilio-whatsapp-photo-guest-book-<INSERT HERE>-dev.twil.io",

Lassen Sie uns jetzt die React-App ausführen, um unser MVP in Aktion zu sehen.

cd frontend
npm start

The MVP showing the sample images on desktop

Dieses MVP zeigt im Augenblick nur 15 Mediendateien der ersten Seite an und greift nicht auf die anderen zu. Um diesen Beitrag kurz zu halten, gehe ich nicht mehr ins Detail über den Front-End-Teil, sondern gebe nur einige Empfehlungen, wie er von hier aus erweitert werden kann. Das bedeutet aber nicht, dass der Code geschlossen bleibt. Den vollständigen Quellcode des Projekts finden Sie auf GitHub.

Die Vollversion verwendet SWR, um bei Bedarf die übrigen Seiten abzurufen. Sie geht noch einen Schritt weiter und fügt einen Service-Worker hinzu. So wird die App auf eine Progressive Web-App hochgestuft, die sich wie eine native App installieren lässt. Um die Nutzererfahrung zu verbessern, verwendet es React-UI-Bibliotheken wie Material UI für das Layout und React Viewer, um eine einzelne Mediendatei anzuzeigen. Die Sync-API hilft dabei mit der like-Funktion, die Nutzerinteraktion zu fördern. Damit erhält das Front-End das Erscheinungsbild einer modernen App.

Bereitstellen der App mit Twilio Serverless-Funktionen

Bis jetzt stellt das Skript „deploy“ nur die serverlosen Funktionen bereit, nicht aber die Web-App. Twilio Serverless-Funktionen können über ihren assets/-Ordner statische Assets hosten, wie z. B. Webanwendungen. Das bedeutet, dass wir die erstellte Web-App aus dem frontend/build-Ordner vor der Bereitstellung nach assets/ verschieben müssen. Dies kann mit einem einzigen Befehl erfolgen, da npm-Skripte sehr flexibel sind. Fügen Sie die folgende Zeile zu package.json im Projekt-Stammverzeichnis hinzu.


{
 ...,
 "scripts": {
   ...,
   "full-deploy": "cd frontend && npm run build && cp -a ./build/. ../assets && cd .. && twilio-run deploy"
 }
}

Führen Sie dann das Skript aus, um alles in der serverlosen Laufzeit bereitzustellen.

cd .. # to make sure you are in the project root
npm run full-deploy

Der Abschnitt „Deployment Details“ (Bereitstellungsdetails) in der Konsolenausgabe sollte nun eine Liste von Assets enthalten. Eine von ihnen endet auf „index.html“. Das ist die URL der Anwendung, die die Galerie anzeigt.


Deployment Details
...
Functions:
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/incoming
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/media
Assets:
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/asset-manifest.json
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/favicon.ico
   https://twilio-whatsapp-photo-guest-book-1860-dev.twil.io/index.html

Damit ist unser MVP bereit. Wir empfehlen die Erstellung eines QR-Codes, der auf den Twilio WhatsApp-Bot mit einer vorgeschlagenen Nachricht verweist. Dieser Code senkt die Einstiegsbarriere und hilft allen, ihre Lieblingsmomente zu teilen! Damit sind Sie bereit für die bevorstehenden Festlichkeiten.

Herunterladen und löschen der Galerie

Alle diese Erinnerungen sollten als Fotoalbum heruntergeladen werden können. Dies erreichen wir mit einem Skript, das über alle protokollierten Nachrichten iteriert und die Medien-URLs aus den Nachrichten extrahiert.

Danach verwenden Sie Axios, um den Inhalt herunterzuladen und in eine neue Datei zu schreiben. WhatsApp komprimiert automatisch alle Mediendateien, um Bandbreite zu sparen. Aus diesem Grund möchten wir auch den Absender jeder Zusendung protokollieren, um den Fotografen kontaktieren und nach der Originalversion fragen zu können.

Erstellen Sie eine Datei download.js mit folgendem Inhalt:

const twilio = require("twilio");
require("dotenv").load();
const axios = require("axios");
const fs = require("fs");

const NUMBER = process.env.NUMBER;
const client = new twilio(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);

const logger = fs.createWriteStream("downloads/senders.txt", {
flags: "a", // 'a' means appending (old data will be preserved)
});

client.messages.each(
{
  to: `whatsapp:${NUMBER}`
},
async function (message) {
  const mediaURL = message.subresourceUris.media;

  const mediaRes = await axios({
    url: `https://api.twilio.com${mediaURL}`,
    method: "GET",
    auth: {
      username: process.env.ACCOUNT_SID,
      password: process.env.AUTH_TOKEN,
    },
  });

  if (mediaRes.data.media_list.length > 1) {
    console.error("More than one image found", mediaURL);
  }

  const media = mediaRes.data.media_list[0];

  if (!media) {
    return null;
  }

  const fileEnding = media.content_type.replace(/.*\//g, "");
  const file = fs.createWriteStream(`downloads/${media.sid}.${fileEnding}`);
  logger.write(
    `${media.sid}.${fileEnding} - ${message.from} - ${message.body}\n`
  );

  console.time(`Download ${media.sid}.${fileEnding}`);

  await axios({
    url: `https://api.twilio.com${media.uri.replace(/\.json$/, "")}`,
    method: "GET",
    responseType: "stream",
    headers: {
      "Content-Type": media.content_type,
    },
    auth: {
      username: process.env.ACCOUNT_SID,
      password: process.env.AUTH_TOKEN,
    },
  }).then((response) => {
    response.data.pipe(file);
    // close filestream after download completed
    file.on("finish", () => {
      file.close();
      console.timeEnd(`Download ${media.sid}.${fileEnding}`);
    });
  });
}
);

Sie können dieses Skript ausführen, sobald Sie den Download-Ordner erstellt haben.

mkdir downloads
node downloads.js

Bevor der feierliche Anlass beginnt, sollten Sie besser alle Testbilder aus der Galerie entfernen. Das geschieht am zuverlässigsten, indem Sie das Nachrichtenprotokoll löschen. Erstellen Sie das folgende clearGallery.js-Skript und führen Sie es aus, um das Protokoll zu löschen.

require("dotenv").config();
const client = require("twilio")(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);

console.log(`Lösche Whatsapp Nachrichten aus Konto ${process.env.ACCOUNT_SID}.`);

(async () => {
   const sentMessages = await client.messages.list({
       from: `whatsapp:${process.env.NUMBER}`,
   });
   const receivedMessages = await client.messages.list({
       to: `whatsapp:${process.env.NUMBER}`,
   });

   await Promise.all(
       [...sentMessages, ...receivedMessages].map(async (message) => {
           await client.messages(message.sid).remove();
       })
   );
   console.log(
       `${sentMessages.length + receivedMessages.length} WhatsApp Nachrichten wurden gelöscht.`
   );
})();

Wir empfehlen, dieses Skript auszuführen, nachdem das Ereignis beendet ist und Sie alle Mediendateien gespeichert haben.

Mehr erfahren über WhatsApp-Chatbots

Mit diesem WhatsApp-Chatbot können Sie mit Ihren Freund:innen und Ihrer Familie das große Fest begehen.

Dies war nur ein kleiner Vorgeschmack darauf, was Sie mit der WhatsApp Business-API auf der Twilio-Plattform erschaffen können. Die Plattform unterstützt auch umfangreiche Messaging-Funktionen wie Standorte, Nachrichtenheader, Fußzeilen und Nachrichten mit Buttons, die schnelle Antworten ermöglichen. Und wir arbeiten kontinuierlich an weiteren Funktionen. Wenn Sie mehr erfahren möchten, lesen Sie die Dokumentation zur WhatsApp Business API.

Wir sind gespannt, was Sie mit WhatsApp daraus machen.

Marius ist Developer Evangelist bei Twilio. Dabei arbeitet er an inspirierende Demoszenarien, deren open-source Code auf GitHub teilt. Außerdem ist er recht häufig auf verschiedensten Entwicklerkonferenzen in der DACH-Region zu finden und redet gerne über die neusten Trends um Webtechnologien.