Ich sage euch: man muß noch Chaos in sich haben, um einen tanzenden Stern gebären zu können (“Also sprach Zarathustra”, Friedrich Nietzsche)
Как с помощью Python и Twilio сделать чат‑бота для WhatsApp
Наверняка, Вы уже подумывали о том, как автоматизировать свой WhatsApp ботом, например, для отправка ровно в полночь напоминания о днях рождения друзей или напоминания о своих повседневных задачах.
Из этого туториала Вы узнаете, как создать чат‑бота для WhatsApp, который поможет вам создавать напоминания, используя Twilio API для WhatsApp для общения, Python для разработки, Таблицы Google в качестве базы данных и Heroku для развертывания. Поехали.
Вот схема нашего сегодняшнего урока:
Будем создавать и развертывать два отдельных приложения python в папках whatsapp‑bot, которые используется для реализации диалога с чат‑ботом и сохранения введенной пользователем даты и сообщения напоминания; Scheduler, для доступа и планирования дат и сообщений напоминания. Развертывание наших приложений на Heroku будет отличаться для обеих вышеупомянутых функций, поэтому и приложений должно быть два.
Итак, без лишних слов, прямо сейчас погружаемся!
Что нам нужно?
Вам понадобятся следующие компоненты:
Python 3.6 или новее.
Смартфон с действующим номером телефона и установленным WhatsApp.
Пакет Requests (v2.26.0) для доступа к сторонним API
Пакет gspread (v4.0.1), для чтения и записи в таблицы Google
Пакет oauth2client (v4.1.3) для доступа к ресурсам, защищенным OAuth 2.0.
Пакет APScheduler (v3.1.0), чтобы запланировать напоминания
Пакет datetime (v3.10.0) для работы с датой и временем
Пакет python-dateutil (v2.8.2),который представляет собой синтаксический анализатор строки даты/времени, способный анализировать наиболее известные форматы для представления даты и/или времени.
Пакет pytz (v2021.3), который полезен при работе с другим часовым поясом.
1. Получение данных и сохранение их в Google Таблицах
Прежде всего, создайте пустой каталог и назовите его whatsapp‑bot. После выполнения шагов, описанных в этом руководстве, ваш каталог в конечном итоге будет иметь структуру, указанную ниже:
Twilio — это американская компания, предлагающая облачную коммуникационную платформу как услугу (CPaaSએ), которая была запущена в 2008 году. Twilio позволяет разработчикам программного обеспечения программно совершать и принимать телефонные звонки, отправлять и получать текстовые сообщения, а также выполнять другие коммуникационные функции, используя свои API веб‑службаએ.
Для использования Twilio для WhatsApp Twilio предоставляет песочницу WhatsApp, где вы можете легко разработать и протестировать свое приложение. Чтобы подключить смартфон к песочнице, перейдите в консоль Twilio, выберите Programmable SMS и нажмите WhatsApp. На странице песочницы WhatsApp будет показан номер тестовой среды, назначенный вашей учетной записи, и код присоединения.
Чтобы включить песочницу WhatsApp для вашего мобильного телефона, вам нужно будет отправить сообщение WhatsApp с указанным кодом на номер, присвоенный вашей учетной записи. Код будет начинаться со слова join, за которым следует случайно сгенерированная фраза из двух слов.
После того, как вы отправите сообщение, вы должны получить ответ от Twilio, указывающий, что ваш номер мобильного телефона подключен к песочнице и может начать отправлять и получать сообщения.
Шаг 2. Создайте службу чат‑бота Flask
Python имеет несколько веб‑фреймворков, таких как Flask, Django и т.д., Которые можно использовать для создания веб‑приложений и API. Эти веб‑платформы предоставляют функциональные возможности для создания веб‑приложений, включая управление HTTP-запросами и шаблоны отрисовки. В этом руководстве мы будем использовать Flask в качестве веб‑фреймворка для Python. В этом разделе мы создадим базовое приложение Flask.
Для начала давайте сначала установим Flask с помощью команды pip install flask. Flask сопоставляет HTTP-запросы с функциями Python. Затем создайте файл Python app.py и введите следующий код:
from flask import Flask, request
app = Flask(__name__)
@app.route(“/sms”, methods=[‘POST’])
def reply():
...
if __name__ == “__main__”:
app.run(debug=True)
В приведенном выше коде app = Flask (__name__) создает объект приложения Flask, который содержит данные о приложении, а также методы (функции объекта), которые сообщают приложению о выполнении определенных действий. Последняя строка, app.run(), является одним из таких методов.
Позже мы сопоставили один URL-путь (‘/’) с одной функцией, sms, потому что мы будем получать данные в конечной точке /sms, используя команду POST HTTP. Каждый раз, когда на номер WhatsApp поступает входящее сообщение от конечного пользователя, Twilio, в свою очередь, вызывает конечную точку /sms с подробной информацией. Этот процесс сопоставления URL-адресов функциям называется маршрутизацией.
Затем мы определяем ответ функции.Основная логика приложения для связи с Twilio API для WhatsApp находится внутри этой функции.
Синтаксис @app.route('/sms', methods = [‘POST’]) является частью программы, которая сообщает Flask, что эта функция, ответ должна быть сопоставлена с путем /sms. Список методов (method = [‘POST’]) — это аргумент ключевого слова, который позволяет Flask узнать, какие типы HTTP-запросов разрешены. В этом руководстве мы будем использовать только запросы POST, но многие веб‑приложения должны использовать как запросы GET (для отправки данных из приложения пользователю), так и запросы POST (для получения данных от пользователя).
Кроме того, строка кода __name__ == "__main__" гарантирует, что сервер разработки запускается только тогда, когда app.py выполняется в качестве основного скрипта. App.run(debug = True) — это метод, запускающий сервер приложений. С debug = True, если ваш код имеет неправильный формат, вы увидите сообщение об ошибке при загрузке своего приложения.
После этого, расширим наш предыдущий код до приведенного ниже:
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
app = Flask(__name__)
@app.route(“/sms”, methods=[‘POST’])
def reply():
incoming_msg = request.form.get(‘Body’).lower()
response = MessagingResponse()
message=response.message()
if __name__ == “__main__”:
app.run(port=5000)
В приведенном выше коде мы импортировали из twilio.twiml.messaging_response import MessagingResponse, поскольку ответ, который Twilio ожидает от веб‑перехватчика, должен быть дан на TwiML или языке разметки Twilio, который является языком на основе XML. Вспомогательная библиотека Twilio для Python, такая как MessagingResponse, поставляется с классами, которые упрощают создание этого ответа без непосредственного создания XML.
Затем сообщение, отправленное пользователем, включается в полезную нагрузку запроса POST с ключом Body. В Flask мы можем получить доступ к сообщению, отправленному пользователем, через объект запроса incoming_msg = request.form.get(‘Body’).Lower(). Далее мы преобразуем incoming_msg в нижний регистр с помощью встроенного строкового метода .lower(), чтобы убедиться в отсутствии расхождений.
Шаг 3. Кодирование логики чат‑бота
В этом разделе, вы увидите логику нашего чат‑бота и то, как его написать на Python. По нашей логике строка входящего сообщения от пользователя будет в одном из следующих форматов:
«Привет» в качестве триггерного слова для начала разговора с чат‑ботом.
«Да/Нет» в ответ на «Вы хотите установить напоминание?»
«Дата @ введите дату», где «введите дату» будет заменено датой для напоминания.
«Напоминание @ введите сообщение», где «введите сообщение» будет заменено напоминанием для напоминания. Для обработки этих входных данных ChatBot будет разделен на следующие функции:
set_reminder_date(msg). Для сохранения даты напоминания в таблицах Google с помощью пакета Python gspread, который будет объяснен в следующем разделе.
set_reminder_body(msg), Для сохранения напоминания в таблицах Google с помощью пакета Python gspread, о котором в следующем разделе.
Вы можете найти полный код для app.py, включая логику чат‑бота ниже:
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
from gsheet_func import *
from dateutil.parser import parse
app = Flask(__name__)
count=0
@app.route("/sms", methods=['POST'])
def reply():
incoming_msg = request.form.get('Body').lower()
response = MessagingResponse()
print(incoming_msg)
message=response.message()
responded = False
words = incoming_msg.split('@')
if "hello" in incoming_msg:
reply = "Hello! \nDo you want to set a reminder?"
message.body(reply)
responded = True
if len(words) == 1 and "yes" in incoming_msg:
reminder_string = "Please provide date in the following format only.\n\n"\
"*Date @* _type the date_ "
message.body(reminder_string)
responded = True
if len(words) == 1 and "no" in incoming_msg:
reply="Ok. Have a nice day!"
message.body(reply)
responded = True
elif len(words) != 1:
input_type = words[0].strip().lower()
input_string = words[1].strip()
if input_type == "date":
reply="Please enter the reminder message in the following format only.\n\n"\
"*Reminder @* _type the message_"
set_reminder_date(input_string)
message.body(reply)
responded = True
if input_type == "reminder":
print("yuhu")
reply="Your reminder is set!"
set_reminder_body(input_string)
message.body(reply)
responded = True
if not responded:
print("why", input_type)
message.body('Incorrect request format. Please enter in the correct format')
return str(response)
def set_reminder_date(msg):
p= parse(msg)
date=p.strftime('%d/%m/%Y')
save_reminder_date(date)
return 0
def set_reminder_body(msg):
save_reminder_body(msg)
return 0
return reminder_message
if __name__ == "__main__":
app.run(debug=True)
В приведенном выше коде мы используем 'hello' как слово-триггер, чтобы начать разговор с чат‑ботом. Условный оператор if 'hello' in incoming_msg: обрабатывает начало разговора, когда бот спрашивает пользователя, хотят ли они установить напоминание или нет.
Потом, инициализируем переменные слова как список, сгенерированный после разделения полученного входящего сообщения с помощью ('@'). Мы разбиваем строку входящего сообщения, используя символ ('@').
Если длина words равна 1, а входящее сообщение — yes or no, возвращаем пользователю соответствующее ответное сообщение относительно его ответа, чтобы установить напоминание или нет.
И, если длина списка, созданного после разделения входящего сообщения, больше 1, тогда слово в 0-м индексе списка является одним из типов ввода, которым может быть Date или Reminder, в то время как слово в индексе 1 — это дата и сообщение, введенное пользователем.
Шаг 4. Сохраните собранные дату и сообщение напоминания в Google Таблицах
Нам нужно сохранить дату и сообщение с напоминанием, полученное от пользователя, в базу данных, чтобы запланировать его на желаемую дату и время. Следовательно, для этой цели мы будем использовать Google Таблицы в качестве нашего облачного хранилища. Воспользуемся пакетом gspread, для подключения нашего приложения к таблицам Google. В нём есть такие функции, как открытие электронной таблицы по заголовку, ключу или URL-адресу, чтение, запись и форматирование диапазонов ячеек, совместное использование и контроль доступа и т.д.
Чтобы начать работу с пакетом gspread в своем коде Python, установите его с помощью команды pip install gspread. Начнем с создания нового файла gsheet_func.py и добавим наши функции gspread, которые позже будут импортированы в наш app.py. Кроме того, перед использованием таблиц Google в нашем коде нам потребуется аутентифицировать нашу учетную запись. Вы можете аутентифицировать свою учетную запись Google, выполнив следующие действия:
В приведенном выше коде начинаем с импорта пакета gspread. Позже используем import ServiceAccountCredentials from oauth2client.service_account в файл python для доступа к ресурсам, защищенным OAuth 2.0. Приведенный ниже код используется для аутентификации и предоставления нам доступа к нашим таблицам Google.
Позднее мы используем функции gspread, чтобы открыть, open(), желаемый лист по имени (Sheet1 в нашем случае) электронной таблицы Google (reminders в нашем случае) и получить значения строки и столбца с помощью соответствующих функций. Далее, мы создали две функции save_reminder_date(date) и save_reminder_body(msg), чтобы сохранить собранные данные в последнюю пустую строку на нашем листе. Эти функции вызываются в нашем файле app.py.
Ниже вы можете найти снимок экрана Google Таблиц после сохранения некоторых данных с помощью чат‑бота.
2. Запланируйте напоминания
Создайте пустой каталог и назовите его Scheduler. После чего, в конечном итоге, будем иметь структуру, указанную ниже:
В этой части урока мы рассмотрим создание второй половины нашего проекта. Для этого мы должны создать новую папку с именем scheduler, которая будет содержать наш код, который будет планировать наши напоминания на установленную дату и время. Для простоты мы будем отправлять напоминание в установленное время каждый день (например, в 9:00). Для этого, запланируем наше приложение Heroku для этого каталога, чтобы анализировать данные таблицы Google один раз в 9:00 каждый день. Если во время синтаксического анализа дата напоминания совпадает с датой этого дня, наш код отправит сообщение с напоминанием, связанное с ним.
Чтобы получить данные из Google Таблиц и отправить напоминания нашим пользователям через WhatsApp, мы снова воспользуемся пакетом gspread.
Шаг 1. Создайте вспомогательную функцию Twilio
Создайте файл Python с именем twilio_func.py, чтобы включить вспомогательную функцию Twilio, используя приведенный ниже код. Подробнее об этом коде можно узнать здесь.
from twilio.rest import Client
account_sid = 'XXXX' # Add your Twilio Account's SID
auth_token = 'XXX' # Add your Twilio Account's Auth Token
client = Client(account_sid, auth_token)
def send_rem(date,rem):
message = client.messages.create(
from_='whatsapp:+14155238886',
body='*REMINDER* '+date+'\n'+rem,
to='whatsapp:XXX' # Add your WhatsApp No. here
)
print(message.sid)
Шаг 2. Запланируйте сообщения
Кроме того, для планирования сообщений мы будем использовать другой пакет Python под названием APScheduler. Advanced Python Scheduler (APScheduler) — это библиотека Python, которая позволяет вам запланировать выполнение кода Python позже, либо только один раз, либо периодически.
Используйте команду pip install apscheduler для установки пакета. В нашем случае мы будем использовать BlockingScheduler. Вы можете узнать больше о различных типах планировщиков здесь. Среди доступных вариантов триггеров date, interval и cron мы бы использовали date, поскольку хотим, чтобы сообщение было запланировано на определенную дату и время.
Чтобы получить все наши дела утром для лучшего планирования дня, мы установили время на 9:00 утра. Мы можем расширить вариант использования, чтобы получать напоминания в определенное время дня, просто внося небольшие изменения в код. Мы также будем использовать пакет Python datetime для работы с датой и временем. Модуль datetime предоставляет классы для управления датой и временем.
Предупреждение: по умолчанию Heroku ответит на звонки на текущее время по UTC.
Итак, чтобы позже развернуть наше приложение на Heroku и синхронизировать все, мы меняем время с индийского часового пояса («‘Asia/Kolkata») на всемирное координированное время, используя пакет pytz.
Полный код для планировщика, использующего APSchedulerand datetime, приведен ниже:
from twilio_func import *
from datetime import datetime
from datetime import date
from datetime import time
import apscheduler
import pytz
from apscheduler.schedulers.blocking import BlockingScheduler
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from dateutil.parser import parse
s=['https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive']
dt=date.today().strftime('%d/%m/%Y')
now_date=datetime.strptime(dt,'%d/%m/%Y')
rem_day=now_date.day
rem_month=now_date.month
rem_year=now_date.year
t=datetime(rem_year,rem_month,rem_day,9,0)
local = pytz.timezone("Asia/Kolkata")
local_dt = local.localize(t, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)
scheduler = BlockingScheduler()
creds= ServiceAccountCredentials.from_json_keyfile_name("credentials.json",s)
client=gspread.authorize(creds)
worksheet = client.open("Reminders").sheet1
list_of_lists = worksheet.get_all_values()
print(list_of_lists)
for row in list_of_lists:
#print(row[2])
p= parse(row[0])
print(p)
pp=p.strftime('%d/%m/%Y')
print(pp)
print(dt)
print(row[1])
if pp == dt:
scheduler.add_job(send_rem, 'date', run_date=utc_dt, args=[row[0],row[1]])
print("yoyo")
else:
pass
scheduler.start()
Согласно нашей логике, объясненной выше, нам нужно получить текущую дату, используя пакет datetime, используя date.today(). Чтобы избежать противоречий, конвертируем дату в определенный формат («%d/%m/%y»). Позже мы инициализируем переменную utc_dt значениями даты и времени в часовом поясе UTC, используя функции библиотеки pytz. Используем команду scheduler = BlockingScheduler() для настройки планировщика и команду scheduler.start() для его запуска.
scheduler.add_job(send_rem, ‘date’, run_date = utc_dt, args = [row [0], row [1]]) для того, чтобы добавить задание в наш планировщик и установить параметры как функцию send_rem из twilio_func.py. Параметр date есть триггер. Аргументы row[0] и row[1] содержат параметры даты и сообщения, которые мы извлекли из наших таблиц Google с помощью функции пакета gspread get_all_values().
3. Развертывание приложения Flask на Heroku
Чтобы наши приложения работали постоянно, развернем оба наших приложения, whatsapp‑bot и Scheduler, отдельно на облачной платформе Heroku. Heroku — это платформа как услуга (PaaSએ), которая позволяет разработчикам создавать, запускать и управлять приложениями полностью в облаке. Для обеих папок приложений нам нужно будет создать дополнительные файлы:
Procfile: Procfile — это механизм для объявления, какие команды выполняются динамическими модулями вашего приложения на платформе Heroku.
Предостережение: убедитесь, что файл назывался именно «Procfile».
Для папки whatsapp‑bot напишите приведенную ниже строку кода в Procfile. web gunicorn app:app и установите Gunicorn в своей среде, запустив команду pip install gunicorn.
Для папки Scheduler напишите следующую строку кода:
Clock: python scheduler.py
По сути, это было причиной для создания двух папок, поскольку оба наши приложения развернуты на Heroku по-разному.
requirements.txt: этот файл содержит все сторонние библиотеки, необходимые для приложения. Просто выполните: pip freeze> requirements.txt для обеих папок, чтобы сгенерировать в них файл requirements.txt.
gitignore: файл gitignore определяет шаблоны, которые используются для исключения определенных файлов в вашем рабочем каталоге из вашей истории Git.
Используйте приведенный ниже код для этого файла:
myvenv/
*.pyc
runtime.txt: этот файл используется для указания конкретной версии Python. Используйте приведенный ниже код для этого файла: python-3.7.2.
Итак, теперь мы готовы создать приложения Heroku для обеих наших папок. Следуйте инструкциям ниже.
Настройте репозиторий Git для обоих проектов WhatsApp‑Bot и Scheduler по отдельности.
Создайте новое приложение Heroku, используя команду heroku create .
Наконец-то, вы готовы развернуть свое приложение, переместив локальный репозиторий git в репозиторий git удаленного приложения Heroku, запустив git push heroku master.
Чтобы проверить журналы вашего приложения Heroku, используя журналы heroku.
Теперь, чтобы включить нашего чат‑бота, подключите наше первое приложение Heroku, то есть whatsapp‑bot, к песочнице Twilio WhatsApp, установив URL-адрес веб‑перехватчика вашей песочницы с URL-адресом вашего приложения Heroku. Поскольку наш чат‑бот отображается под URL-адресом /sms, добавьте его в конец корневого URL-адреса приложения Heroku. Убедитесь, что метод запроса установлен на HTTP Post. Не забудьте нажать красную кнопку Save внизу страницы, чтобы записать эти изменения.
Ура! После всей тяжелой работы мы наконец разработали потрясающего чат‑бота WhatsApp, который будет устанавливать для нас напоминания.
Заключение
В этом руководстве мы создали чат‑бота WhatsApp, который помогает устанавливать и получать напоминания в WhatsApp. Наша реализация основана на Flask, Python, Twilio, API и Google Sheets. Вы также можете расширить его, чтобы установить напоминания для других, добавив простые функции для добавления их мобильных номеров. Вы также можете использовать эти простые соединения для создания более сложного чат‑бота для обработки различных вариантов использования в реальной жизни.