Wysyłanie i odbieranie SMS-ów w Node.js za pomocą Twilio Webhooks i Express
Stwórz mały serwer Express, który wysyła teksty przez Twilio, odbiera odpowiedzi za pośrednictwem webhooku udostępnionego przez ngrok i automatycznie odpowiada na nie za pomocą TwiML.
Źródła: **Build a Two-Way SMS with Node.js and Twilio (Full Code Included)**
Większość poradników dotyczących SMS kończy się w momencie, gdy wiadomość opuszcza serwer. Bardziej przydatna jest część dotycząca odbierania wiadomości: zauważanie, że użytkownik odpowiedział, czytanie jego wiadomości i reagowanie na nią. Ten cykl jest podstawą procesów OTP, botów obsługi klienta oraz przypomnień o umowieniach, które potwierdza się jednym słowem. Po ukończeniu tego przewodnika będziesz miał serwer Express, który wysyła wiadomości na telefon i reaguje za każdym razem, gdy telefon odpisze.
Wymagania wstępne
- Konto Twilio. W chwili pisania tego tekstu darmowy okres próbny obejmuje około 15 dolarów kredytu, co wystarcza na eksperymenty.
- Postman lub dowolny klient HTTP.
- Node.js zainstalowany lokalnie.
- ngrok, który zapewnia Twilio publiczny adres, który przekierowuje ruch na twój laptop.
Minimalny serwer Express
Stwórz folder zawierający plik server.js. Serwer łączy się z localhost na porcie 3000, aktywuje obsługę JSON oraz parsowanie ciała wiadomości w formacie URL-encoded i udostępnia ścieżkę do sprawdzenia stanu pod adresem /api/v1/. Parsowanie w formacie URL-encoded jest istotne: Twilio dostarcza przychodzące wiadomości w formie ciał POST z kodowaniem typu form, więc bez niego req.body byłby pusty później.
const express = require("express");
const bodyParser = require("body-parser");
const hostname = "127.0.0.1";
const port = 3000;
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const baseUrl = "/api/v1/";
app.get(`${baseUrl}`, (req, res) => {
res.status(200).json({ message: "Hello World" });
});
app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Zainstaluj wymagane zależności:
npm init -y
npm install express body-parser
Zrób wywołanie node server.js i otwórz adres http://127.0.0.1:3000/api/v1/; odpowiedź „Hello World” potwierdza poprawne działanie podstaw. Express 4.16+ zawiera już funkcje express.json() i express.urlencoded(), więc użycie body-parser jest opcjonalne w nowych projektach.
Udostępnianie localhost za pomocą ngrok
Twilio działa w publicznej sieci internetowej, a twój serwer na maszynie prywatnej, więc Twilio nie może do niego bezpośrednio dotrzeć. ngrok tworzy tunel i dostarcza publiczną adres URL HTTPS przekierowującą ruch na lokalny port. Heroku kiedyś był darmową opcją do tego celu, ale jego plan darmowy już nie istnieje.
Zainstaluj i zaloguj się do ngrok korzystając z dokumentacji ngrok, a następnie dodaj skrypt do pliku package.json:
"scripts": {
"ngrok": "ngrok http http://127.0.0.1:3000/"
}
Zachowaj serwer uruchomiony w jednym terminalu, a następnie uruchom tunel w drugim:
npm run ngrok
ngrok wyświetla adres taki jak https://abcd-1234.ngrok-free.app. Zachowaj ten terminal otwarty.
Uwaga: w planie darmowym generowany adres URL zazwyczaj zmienia się za każdym razem, gdy ngrok zostanie ponownie uruchomiony. Gdy działający webhook nagle przestaje reagować, prawdopodobną przyczyną jest przestarzały adres w konsoli Twilio, dlatego należy go aktualizować po każdym restartowaniu.
Konfiguracja Twilio
Na liście kontrolnej konsoli skopiuj swoje SID konta oraz token autoryzacji do pliku .env. Token jest tajemnicą – nigdy go nie dodawaj do repozytorium i umieść plik .env w pliku .gitignore.
ACCOUNT_SID_TWILIO=your_account_sid
AUTH_TOKEN_TWILIO=your_auth_token
Możesz przypiąć produkty w nawigacji bocznej; istotne są tutaj tylko Messaging i Numeri telefonów.
Pobieranie numeru telefonu
- Otwórz zakładkę Numeri telefonów, a następnie Kup numer.
- Wybierz dowolny dostępny numer; numery w USA zazwyczaj są najtańsze.
- Koszt zostanie odjęty z salda próbnego, a numer pojawi się wśród twoich aktywnych numerów.
Tworzenie usługi Messaging
- W sekcji Messaging utwórz nową usługę Messaging Service.
- W kroku Sender Pool dodaj swój numer.
/api/v1/sms. Ta ścieżka zostanie stworzona poniżej.Zapisz Messaging Service SID oraz swój numer w pliku .env:
FROM_TWILIO=+1XXXXXXXXXX
MESSAGING_SERVICE_SID_TWILIO=your_messaging_service_sid
Podłączanie SDK Twilio do Node
Zainstaluj SDK wraz z dotenv, który ładuje zawartość pliku .env do zmiennej process.env:
npm install twilio dotenv
Umieść to na samym początku pliku server.js. Kolejność ma znaczenie: klient odczytuje dane uwierzytelniające podczas swojej inicjalizacji, więc jeśli dotenv zostanie załadowane później, obie wartości będą miały wartość undefined, co spowoduje nieudane uwierzytelnianie przy każdej próbie.
require("dotenv").config();
const client = require("twilio")(
process.env.ACCOUNT_SID_TWILIO,
process.env.AUTH_TOKEN_TWILIO
);
Wysyłanie wiadomości tekstowej
Ta ścieżka wywołuje client.messages.create() i raportuje wynik w formacie JSON. Blok try/catch zwraca błędy Twilio, takie jak nieprawidłowy numer, do wywołującego zamiast powodować awarię procesu.
app.post(`${baseUrl}sendSms`, async (req, res) => {
try {
await client.messages.create({
body: "Hello",
from: process.env.FROM_TWILIO,
messagingServiceSid: process.env.MESSAGING_SERVICE_SID_TWILIO,
to: "+92XXXXXXXXXX", // whoever you're texting
});
res.status(200).json({ message: "Text sent successfully" });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Znaczenie pól:
body: tekst wiadomości.from: twój numer Twilio.messagingServiceSid: łączy wiadomość z usługą oraz jej pulą nadawców.to: odbiorca w formacie E.164 (+1...,+92...).
Zawołaj POST /api/v1/sendSms z Postman – telefon powinien otrzymać „Hello”. W rzeczywistym kodzie należy odczytać wartości to i body z żądania i je zweryfikować, zamiast używać wartości hardkodowanych.
Konta testowe mogą wysyłać wiadomości tylko do numerów zweryfikowanych w konsoli Twilio. Jeśli żądanie zostanie zrealizowane, ale nic nie przyjdzie, sprawdź odbiorcę lub dokonaj aktualizacji.
Odbieranie odpowiedzi przez webhook
Gdy ktoś wysyła wiadomość na twój numer Twilio, Twilio przesyła ją do webhook konfigurowanego w Messaging Service. Twoj serwer przyjmuje ją i odpowiada za pomocą TwiML, formatu XML używanego przez Twilio do opisania następnych działań. Należy zaimportować ten narzędzie wraz z pozostałymi wymaganiami:
const MessagingResponse = require("twilio").twiml.MessagingResponse;
Następnie dodaj słuchacz. Klasycznym błędem jest niezgodność ścieżki: trasa musi być identyczna ze ścieżką wpisaną w konsoli, czyli /api/v1/sms. Obsługa loguje req.body.Body i zwraca pusty dokument TwiML w formacie text/xml, co oznacza brak odpowiedzi.
app.post(`${baseUrl}sms`, (req, res) => {
// Fires every time someone replies to your number
const twiml = new MessagingResponse();
console.log("INCOMING ----->", req.body.Body);
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(twiml.toString());
});
Wpisz swój numer Twilio, a wiadomość pojawi się w terminalu; pętla zostaje zamknięta. Treść wiadomości zawiera również pole From z numerem nadawcy, które jest potrzebne przy śledzeniu rozmów dla poszczególnych użytkowników.
Ponieważ endpoint jest publiczny, każdy, kto znajdzie adres URL, może wysyłać fałszywe wiadomości. Twilio podpisuje każdy webhook twoim tokenem autoryzacyjnym w nagłówku X-Twilio-Signature, a Node SDK oferuje narzędzia do weryfikacji. Włącz je przed uruchomieniem aplikacji poza środowiskiem lokalnym.
Odpowiadanie automatycznie
Serwer słucha, ale pozostaje cichy. Dodanie wiadomości do odpowiedzi w formacie TwiML przekształca ją w bota reagującego na określone słowa kluczowe. Umieść to w obsłudze endpointu /sms po utworzeniu pliku twiml; funkcja ta usuwa zbędne znaki i zamienia tekst na małe litery przed wybraniem odpowiedzi:
const incoming = req.body.Body.trim().toLowerCase();
if (incoming === "hi") {
twiml.message("Hey there! How can I help?");
} else {
twiml.message("Hmm, I didn't catch that.");
}
Twilio wysyła zwróconą odpowiedź do nadawcy, więc nie jest potrzebna dodatkowa wywołanie messages.create().
Pełny serwer
Wszystko w jednym pliku, przy czym automatyczna odpowiedź jest wyłączona poprzez komentarze:
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const MessagingResponse = require("twilio").twiml.MessagingResponse;
const client = require("twilio")(
process.env.ACCOUNT_SID_TWILIO,
process.env.AUTH_TOKEN_TWILIO
);
const hostname = "127.0.0.1";
const port = 3000;
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const baseUrl = "/api/v1/";
// Health check
app.get(`${baseUrl}`, (req, res) => {
res.status(200).json({ message: "Hello World" });
});
// Goal 1: Send an SMS
app.post(`${baseUrl}sendSms`, async (req, res) => {
try {
await client.messages.create({
body: "Hello",
from: process.env.FROM_TWILIO,
messagingServiceSid: process.env.MESSAGING_SERVICE_SID_TWILIO,
to: "+92XXXXXXXXXX", // whoever you're texting
});
res.status(200).json({ message: "Text sent successfully" });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Goal 2: Receive replies (this endpoint must match your Twilio webhook)
app.post(`${baseUrl}sms`, (req, res) => {
const twiml = new MessagingResponse();
console.log("INCOMING ----->", req.body.Body);
// Optional auto-reply:
// const incoming = req.body.Body.trim().toLowerCase();
// if (incoming === "hi") {
// twiml.message("Hey there! How can I help?");
// } else {
// twiml.message("Hmm, I didn't catch that.");
// }
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(twiml.toString());
});
app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Główne wnioski
- Wysyłanie SMS-ów to jedno wywołanie SDK; odbieranie SMS-ów odbywa się za pomocą webhooka, więc serwer musi być dostępny i rozumieć język TwiML.
- Twilio wysyła dane w formacie kodowanym, stąd konieczność użycia parsera do dekodowania URL.
- Ścieżka webhooka w konsoli i trasa Express muszą się zgadzać, a ponowne uruchomienie darmowego tunelu oznacza nową ścieżkę URL.
- Nieudane testy często wynikają z ograniczeń dotyczących numerów do weryfikacji, a nie z błędów.
- Niechciane błędy mogą wynikać z nieprawidłowych podpisów lub wartości hardkodowanych – należy je sprawdzić przed faktycznym użyciem.
Stąd możesz przechowywać rozmowy, rozwijać bota do analizy słów kluczowych lub dodać logowanie przez OTP. Aby dowiedzieć się więcej na temat wzorców obsługi powtórek i wywołań zwrotnych, zapoznaj się z sześcioma wzorcami integracji umożliwiającymi niezawodne łączenie usług Node.js.