API Mail para Python

Nesta página, descrevemos como usar a API Mail, um dos serviços incluídos no pacote, com o ambiente de execução do Python para o ambiente padrão. O aplicativo pode acessar os serviços incluídos pelo SDK de serviços do App Engine para Python.

Visão geral

No Python, a funcionalidade de processamento de e-mails está incluída no módulo google.appengine.api.mail. Isso é diferente do Python 2, em que o módulo mail_handlers era fornecido pelo webapp. A API Mail para Python pode ser usada para receber e-mails e notificações de rejeição.

Como usar a API Mail

O aplicativo recebe e-mails quando um e-mail é enviado como o corpo de uma solicitação HTTP POST. Para que o app processe e-mails recebidos, ele precisa combinar o URL ao caminho /_ah/mail/[ADDRESS]. A parte [ADDRESS] do caminho geralmente é um endereço de e-mail com o sufixo @<Cloud-Project-ID>.appspotmail.com. E-mails enviados ao aplicativo nesse formato serão encaminhados para a função.

Como o Python não exige que o aplicativo especifique um script de gerenciador no arquivo app.yaml, é possível remover todas as seções handler de app.yaml.

Seu arquivo app.yaml deve manter as seguintes linhas:

inbound_services:
- mail
- mail_bounce

Como enviar e-mails

Não é necessário alterar a configuração do aplicativo ao fazer upgrade para o Python. O comportamento, os recursos e as instruções de configuração para enviar e-mails permanecem os mesmos. Consulte os guias a seguir para saber mais:

Como receber e-mails

Para receber e-mails, importe o módulo google.appengine.api.mail e use a classe InboundEmailMessage para representar um e-mail. Essa classe precisa ser instanciada para recuperar o conteúdo do e-mail da solicitação HTTP recebida.

Antes, no Python 2, os aplicativos podiam acessar a classe InboundEmailMessage substituindo o método receive() no gerenciador InboundEmailHandler do webapp. Isso não é necessário no Python. Em vez disso, o app precisa instanciar um novo objeto.

Frameworks da Web

Ao usar frameworks da Web em Python, o construtor InboundEmailMessage recebe os bytes do corpo da solicitação HTTP. Há várias maneiras de criar o objeto InboundEmailMessage no Python. Veja a seguir exemplos de apps Flask e Djago:

Python 3 (Flask)

No Flask, request.get_data() fornece os bytes da solicitação.

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

No Django, request.body fornece os bytes do corpo da solicitação HTTP.

def receive_mail(request):
    message = mail.InboundEmailMessage(request.body)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for _, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    return HttpResponse("OK")

Para ver os exemplos de código completos deste guia, consulte o GitHub.

Outros frameworks compatíveis com WSGI

Para outros frameworks compatíveis com WSGI, recomendamos o uso do mesmo método dos exemplos Flask e Django para criar InboundEmailMessage. Esse método funciona quando os bytes do corpo da solicitação HTTP estão diretamente disponíveis.

App WSGI sem um framework da Web

Se o aplicativo for um WSGI que não usa um framework da Web, é possível que os bytes do corpo da solicitação HTTP não estejam diretamente disponíveis. Se os bytes do corpo da solicitação HTTP estiverem diretamente disponíveis, recomendamos que você use um framework da Web em Python.

No Python, um método de fábrica chamado from_environ foi definido para InboundEmailMessage. Esse é um método de classe que usa o dicionário environ do WSGI como entrada e pode ser usado para qualquer aplicativo WSGI.

No exemplo a seguir, observe como environ é usado como entrada para receber mail_message:

Python 3 (aplicativo WSGI)

def HelloReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    message = mail.InboundEmailMessage.from_environ(environ)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for content_type, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

Como receber notificações de rejeição

Uma notificação de rejeição é uma mensagem automática de um sistema de e-mail que indica um problema com a entrega da mensagem do aplicativo. Para processar notificações de rejeição, seu app precisa combinar os caminhos de URL de entrada com o caminho /_ah/bounce.

Assim como InboundEmailMessage, a classe BounceNotification do Python 2 era acessível substituindo o método receive() no gerenciador do webapp BounceNotificationHandler.

No Python, o aplicativo precisa instanciar o objeto BounceNotification, que pode ser criado de várias maneiras, dependendo do framework da Web em Python usado.

Frameworks da Web

O objeto BounceNotification é inicializado com os valores que são recuperados chamando post_vars.get(key).

Ao usar um framework da Web em Python, como Flask ou Django, o construtor BounceNotification assume um dicionário chamado post_vars, que contém a solicitação POST dos dados do formulário. Para recuperar os dados, o método get() precisa ser definido no objeto de entrada. O key é uma lista de valores de entrada que podem ser lidos e recuperados por BounceNotification e ser qualquer um dos seguintes:

original-to, original-cc, original-bcc, original-subject, original-text, notification-from, notification-to, notification-cc, notification-bcc, notification-subject, notification-text, raw-message

Na maioria dos frameworks da Web, esses dados estão disponíveis como um dicionário múltiplo no objeto da solicitação. A maior parte desses tipos pode ser convertida em um dicionário codificado por strings.

Para todas as chaves, exceto raw-message, o valor pode ser qualquer um. Normalmente, o valor é um valor único, como uma string, ou uma lista de valores, como {'to': ['bob@example.com', 'alice@example.com']}. O valor padrão de todos os campos é uma string vazia. Esses valores preencherão as propriedades original e notification.

Para a chave raw-message, o valor precisa ser uma entrada válida para o construtor de EmailMessage. Pode ser um valor único ou uma lista com um único valor. A chave raw-message é usada para inicializar a propriedade original_raw_message do objeto.

Python 2 (webapp2)

class LogBounceHandler(BounceNotificationHandler):
    def receive(self, bounce_message):
        logging.info('Received bounce post ... [%s]', self.request)
        logging.info('Bounce original: %s', bounce_message.original)
        logging.info('Bounce notification: %s', bounce_message.notification)

Python 3 (Flask)

No Flask, o request.form do tipo werkzeug.datastructures.MultiDict fornece as variáveis POST. No entanto, o método get() para esse tipo retorna apenas um valor, mesmo que haja vários valores para a chave.

Para receber todos os valores correspondentes a uma chave, o app precisa chamar dict(request.form.lists()), o que resulta em um dicionário em que cada valor é uma lista.

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

No Django, request.POST do tipo django.http.QueryDict fornece as variáveis POST. No entanto, o método get() para esse tipo retorna apenas um valor, mesmo que haja vários valores para a chave.

Para receber todos os valores correspondentes a uma chave, o app precisa chamar dict(request.POST.lists()), o que resulta em um dicionário em que cada valor é uma lista.

def receive_bounce(request):
    bounce_message = mail.BounceNotification(dict(request.POST.lists()))

    # Do something with the message
    print(f"Bounce original: {bounce_message.original}")
    print(f"Bounce notification: {bounce_message.notification}")

    return HttpResponse("OK")

App WSGI sem um framework da Web

Se o aplicativo for um WSGI que não usa um framework da Web, é possível que as variáveis do formulário da solicitação HTTP POST não estejam diretamente disponíveis em um dicionário.

No Python, um método de fábrica chamado from_environ foi definido para BounceNotification. Esse é um método de classe que usa o dicionário environ do WSGI como entrada e pode ser usado para qualquer aplicativo WSGI.

No exemplo a seguir, observe como environ é usado como entrada para receber bounce_message:

Python 3

def BounceReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    bounce_message = mail.BounceNotification.from_environ(environ)

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    # Return suitable response
    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

Amostras de código

Para ver os exemplos de código completos deste guia, consulte o GitHub.