Надсилання та отримування SMS у Node.js за допомогою Twilio Webhooks та Express
Створіть невеликий сервер Express, який надсилає тексти через Twilio, отримує відповіді за допомогою webhook, розгорнутого через ngrok, та автоматично відповідає на них за допомогою TwiML.
Джерела: **Build a Two-Way SMS with Node.js and Twilio (Full Code Included)**
Більшість посібників з SMS закінчуються там, де повідомлення покидає ваш сервер. Набагато кориснішою є частина, яка стосується вхідних повідомлень: спостереження за тим, що користувач відповів, читання тексту та надсилання відповіді. Саме цей цикл забезпечує роботу процесів OTP, ботів підтримки та нагадувань про зустрічі, які підтверджуються одним словом. До кінця цього посібника у вас буде сервер Express, який надсилає повідомлення на телефон та реагує щоразу, коли телефон відповідає.
Попередні вимоги
- Акаунт у Twilio. На момент написання цього посібника безкоштовна пробна версія включає приблизно 15 доларів кредиту, що цілком достатньо для експериментів.
- Postman або будь-який інший HTTP-клієнт.
- Node.js, встановлений на вашому комп’ютері.
- ngrok — інструмент, який надає Twilio публічну адресу, що перенаправляє трафік на ваш ноутбук.
Мінімальний сервер Express
Створіть папку з файлом server.js. Сервер підключається до localhost за портом 3000, увімкнює обробку JSON та URL-кодованих даних тіла запиту, а також надає маршрут перевірки стану за адресою /api/v1/. Парсер URL-кодування є важливим: Twilio надсилає вхідні повідомлення у форматі POST з кодованими даними, тож без нього req.body згодом буде порожнім.
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}/`);
});
Встановіть необхідні залежності:
npm init -y
npm install express body-parser
Запустіть node server.js та відкрийте адресу http://127.0.0.1:3000/api/v1/; відповідь „Hello World“ підтверджує правильну роботу основних функцій. Express 4.16+ також включає функції express.json() та express.urlencoded(), тому в нових проектах використання body-parser є необов’язковим.
Відкриття localhost за допомогою ngrok
Twilio працює в публічному Інтернеті, а ваш сервер — на приватній машині, тому Twilio не може до нього безпосередньо дістатися. ngrok створює тунель та надає вам публічну HTTPS-адресу, яка перенаправляє трафік на локальний порт. Раніше Heroku був безкоштовним варіантом для цього, але його безкоштовний тариф більше не існує.
Встановіть та автентифікуйте ngrok за допомогою документації ngrok, потім додайте скрипт до package.json:
"scripts": {
"ngrok": "ngrok http http://127.0.0.1:3000/"
}
Залишіть сервер у робочому стані в одному терміналі, а потім запустіть тунель у другому:
npm run ngrok
ngrok виводить адресу на кшталт https://abcd-1234.ngrok-free.app. Залиште цей термінал відкритим.
Увага: у безкоштовному плані генерована URL зазвичай змінюється щоразу після перезапуску ngrok. Якщо робочий webhook раптово перестає працювати, ймовірною причиною є застаріла URL у консолі Twilio, тому оновлюйте її після кожного перезапуску.
Налаштування Twilio
У панелі керування консолі скопіюйте SID вашого облікового запису та токен автентифікації у файл .env. Цей токен є конфіденційним: ніколи не додавайте його до коду, а також включіть файл .env у список .gitignore.
ACCOUNT_SID_TWILIO=your_account_sid
AUTH_TOKEN_TWILIO=your_auth_token
Ви можете фіксувати продукти у бічному меню; тут важливими є лише Месенджеринг та Номери телефонів.
Отримання номера телефону
- Відкрийте розділ Номери телефонів, потім Купити номер.
- Виберіть будь-який доступний номер; номери у США зазвичай є найдешевшими.
- Витрати будуть віднесені до тимчасового балансу, а номер з’явиться серед ваших активних номерів.
Створення сервісу месенджерингу
- У розділі Месенджеринг створіть новий Сервіс месенджерингу.
- На етапі Пул відправників додайте свій номер.
/api/v1/sms. Цей шлях описаний нижче.Збережіть Messaging Service SID та ваш номер у файлі .env:
FROM_TWILIO=+1XXXXXXXXXX
MESSAGING_SERVICE_SID_TWILIO=your_messaging_service_sid
Підключення SDK Twilio до Node
Встановіть SDK разом із dotenv, який завантажує дані з файлу .env у process.env:
npm install twilio dotenv
Розмістіть цей код на самому початку файлу server.js. Порядок має значення: клієнт читає облікові дані під час створення, тому якщо dotenv буде завантажений пізніше, обидві значення будуть undefined, і кожна спроба автентифікації зазнає невдачі.
require("dotenv").config();
const client = require("twilio")(
process.env.ACCOUNT_SID_TWILIO,
process.env.AUTH_TOKEN_TWILIO
);
Надсилання текстового повідомлення
Цей маршрут викликає client.messages.create() та повідомляє результат у форматі JSON. Блок try/catch повертає помилки Twilio, такі як некоректний номер, викликаючому замість того, щоб призвести до зупинки процесу.
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 });
}
});
Що означають поля:
body: текст повідомлення.from: ваш номер у Twilio.messagingServiceSid: пов’язує повідомлення з сервісом та пулом відправників.to: одержувач у форматі E.164 (+1...,+92...).
Виконайте виклик POST /api/v1/sendSms у Postman, і телефон має отримати „Hello“. У реальному коді необхідно читати значення to та body з запиту та перевіряти їх, а не використовувати жорстко закодовані значення.
Тестові облікові записи можуть надсилати повідомлення лише на номери, перевірені в консолі Twilio. Якщо запит успішний, але нічого не надходить, перевірте одержувача або оновіть пакет.
Отримання відповідей через webhook
Коли хтось надсилає повідомлення на ваш номер у Twilio, сервіс Twilio надсилає його на webhook, налаштований у сервісі Messaging Service. Ваш сервер приймає це повідомлення та відповідає у форматі TwiML — XML-форматі від Twilio для опису наступних дій. Імпортуйте цей інструмент разом з іншими необхідними елементами:
const MessagingResponse = require("twilio").twiml.MessagingResponse;
Потім додайте слухача. Класичною помилкою є незбіг шляху: маршрут має збігатися з шляхом, введеним у консолі, тобто /api/v1/sms. Обробник записує req.body.Body та повертає порожній документ у форматі TwiML як text/xml, що означає відсутність відповіді.
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());
});
Введіть номер Twilio, і повідомлення з’явиться у терміналі; цикл буде завершений. У навантаженні також міститься поле From — номер відправника, який знадобиться для відстеження розмов за кожним користувачем.
Оскільки кінцева точка є публічною, будь-хто, хто знайде URL, може надсилати фальшиві повідомлення. Twilio підписує кожен webhook вашим Auth Token у заголовку X-Twilio-Signature, а Node SDK містить інструменти для перевірки. Увімкніть їх перед розгортанням поза межами локальних тестів.
Автоматична відповідь
Сервер прослуховує, але залишається мовчазним. Додавання повідомлення до відповіді у форматі TwiML перетворює його на бота на основі ключових слів. Розмістіть цей код у обробнику /sms після створення об’єкта twiml; він очищує та перетворює текст на маленькі літери перед вибором відповіді:
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 надсилає отриману відповідь відправнику, тому не потрібен додатковий виклик messages.create().
Повний сервер
Усе в одному файлі, причому автоматична відповідь залишена коментованою:
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}/`);
});
Основні моменти
- Вихідні SMS — це один виклик SDK; вхідні SMS — це webhook, тому сервер має бути доступний та розуміти TwiML.
- Twilio надсилає дані у форматі форм, тому використовується парсер URL-кодування.
- Шлях webhook у консолі та маршрут Express мають збігатися, а перезапуск безкоштовного тунелю означає нову URL.
- Поразки тестів без повідомлень часто спричинені обмеженнями щодо перевірених номерів, а не багами.
- Перед реальним використанням перевіряйте підписи та видаляйте жорстко закодовані значення.
Звідси ви можете зберігати розмови, розвивати бота на основі ключових слів чи додавати входження за OTP. Щоб дізнатися більше про шаблони використання повторних запитів та спроб, перегляньте шість шаблонів інтеграції для надійного з’єднання сервісів Node.js.