В этом документе рассказывается, как создать приложение App Engine на языке Python, которое отправляет пользователям письма с аннотациями, предлагая подтвердить подписку на рассылку прямо в папке "Входящие", и собирает подписки в Datastore.
Требования и настройка проекта
В этом руководстве предполагается, что вы уже установили App Engine SDK и знаете, как создавать, запускать и публиковать проекты App Engine.
Сначала создайте каталог для проекта. Поместите все файлы приложения в этот каталог.
Скопируйте следующий код в файл с названием app.yaml и замените плейсхолдер {{ APPID }} на уникальный идентификатор приложения App Engine:
application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
Создайте в папке проекта App Engine файл с названием main.py и скопируйте в него следующий код, чтобы настроить обработчики для сбора и перечисления подписок, а также для отправки аннотированных писем:
import webapp2
from emailsender import EmailSender
from subscribe import SubscribeHandler
app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)
Как добавить структурированные данные в письмо
Начните с письма, в котором пользователь должен подтвердить подписку на список рассылки:
<html>
<head>
<title>Please confirm your subscription to Mailing-List XYZ?</title>
</head>
<body>
<p>
Dear John, please confirm that you wish to be subscribed to the
mailing list XYZ
</p>
</body>
</html>
Вы можете добавить структурированные данные в одном из поддерживаемых форматов (JSON-LD или микроданные) в head электронного письма, чтобы определить сообщение и добавить OneClickAction.
Gmail поддерживает OneClickAction и показывает элементы интерфейса, позволяющие пользователям подтверждать подписку прямо в папке "Входящие".
Скопируйте следующую разметку в файл с названием mail_template.html:
JSON-LD
<html>
<head>
<title>Please confirm your subscription to Mailing-List XYZ?</title>
</head>
<body>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ConfirmAction",
"name": "Confirm Subscription",
"handler": {
"@type": "HttpActionHandler",
"url": "{{ confirm_url }}",
"method": "http://schema.org/HttpRequestMethod/POST"
}
},
"description": "Confirm subscription to mailing list XYZ"
}
</script>
<p>
Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
</p>
</body>
</html>
Микроданные
<html>
<head>
<title>Please confirm your subscription to Mailing-List XYZ?</title>
</head>
<body>
<div itemscope itemtype="http://schema.org/EmailMessage">
<div itemprop="potentialAction" itemscope itemtype="http://schema.org/ConfirmAction">
<meta itemprop="name" content="Confirm Subscription"/>
<div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
<meta itemprop="url" content="{{ confirm_url }}"/>
<link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
</div>
</div>
<meta itemprop="description" content="Confirm subscription to mailing list XYZ"/>
</div>
<p>
Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
</p>
</body>
</html>
Приведенные выше структурированные данные описывают список рассылки под названием "XYZ" и ConfirmAction. Обработчик действия – это HttpActionHandler, который отправляет POST-запросы на URL, указанный в свойстве url.
Важно! В приведенном выше коде замените плейсхолдер
{{ confirm_url }}
на URL приложения App Engine (например, https://mailing-list.appspot.com) перед отправкой любых писем. Код, описанный в следующих шагах, выполняет эту замену.
Как отправлять запросы на подписку пользователям
Скопируйте следующий код в файл с названием emailsender.py в папке проекта App Engine:
import jinja2
import os
import webapp2
from google.appengine.api import mail
from google.appengine.api import users
from urlparse import urlparse
class EmailSender(webapp2.RequestHandler):
def get(self):
# require users to be logged in to send emails
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
email = user.email()
# The confirm url corresponds to the App Engine app url
pr = urlparse(self.request.url)
confirm_url = '%s://%s?user=%s' % (pr.scheme, pr.netloc, user.user_id())
# load the email template and replace the placeholder with the confirm url
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
template = jinja_environment.get_template('mail_template.html')
email_body = template.render({'confirm_url': confirm_url})
message = mail.EmailMessage(
sender = email,
to = email,
subject = 'Please confirm your subscription to Mailing-List XYZ',
html = email_body)
try:
message.send()
self.response.write('OK')
except:
self.error(500)
Для класса EmailSender требуется, чтобы пользователь вошел в аккаунт, чтобы можно было получить его адрес электронной почты. Затем он загружает текст письма из mail_template.html, заменяет плейсхолдер confirm_url в нем корневым URL приложения App Engine (https://APP-ID.appspot.com) и отправляет письмо вошедшему в систему пользователю.
Как собрать и перечислить подписки
Скопируйте следующий код в файл с названием subscribe.py в папке проекта App Engine:
import webapp2
from emailsender import EmailSender
from google.appengine.ext import db
class SubscribeHandler(webapp2.RequestHandler):
def post(self):
user_id = self.request.get('user')
# insert the subscription into the Datastore
subscription = Subscription(user_id=user_id)
subscription.put()
def get(self):
# retrieve up to 1000 subscriptions from the Datastore
subscriptions = Subscription.all().fetch(1000)
if not subscriptions:
self.response.write('No subscriptions')
return
count = len(subscriptions)
for s in subscriptions:
self.response.write('%s subscribed<br/>' % (s.user_id))
self.response.write('<br/>')
self.response.write('%d subscriptions.' % (count))
class Subscription(db.Model):
user_id = db.TextProperty(required=True)
Класс SubscribeHandler обрабатывает запросы POST и GET, отправленные на корневой URL приложения (https://APP-ID.appspot.com). Запросы POST используются Gmail для добавления новых подписок, в том числе параметра user_id, соответствующего пользователю, как в следующем примере:
https://subscribe.appspot.com/?user_id=123abcd
Обработчик запросов проверяет, определен ли обязательный параметр user_id, и сохраняет подписку в хранилище данных.
В Gmail отправляется код ответа HTTP 200, чтобы сообщить об успешном выполнении запроса. Если в запросе нет обязательного поля, обработчик запросов возвращает код ответа HTTP 400, указывая на недопустимый запрос.
Запросы GET к корневому URL приложения позволяют получить список собранных подписок. Обработчик запросов получает все подписки из хранилища данных и показывает их на странице вместе со счетчиком.
Как протестировать приложение
Разверните приложение в App Engine и перейдите на страницу https://APP-ID.appspot.com/email (замените APP-ID идентификатором приложения App Engine), чтобы отправить себе аннотированное письмо.

После того как вы развернете приложение и добавите подписки, перейдите на страницу https://APP-ID.appspot.com, чтобы посмотреть сводную информацию о подписках.