API Mail para Python

En esta página se describe cómo usar la API Mail, uno de los servicios empaquetados antiguos, con el runtime de Python para el entorno estándar. Tu aplicación puede acceder a los servicios agrupados a través del SDK de servicios de App Engine para Python.

Información general

En Python, la función de gestión de correo se incluye en el módulo google.appengine.api.mail. Esto es diferente de Python 2, donde el módulo mail_handlers se proporcionaba mediante webapp. La API Mail de Python se puede usar para recibir correos y notificaciones de rebote.

Usar la API Mail

Tu aplicación recibe correo cuando se envía un correo como cuerpo de la solicitud en una solicitud HTTP POST. Para que la aplicación gestione los correos entrantes, debe coincidir con la URL con la ruta /_ah/mail/[ADDRESS]. La parte [ADDRESS] de la ruta suele ser una dirección de correo con el sufijo @<Cloud-Project-ID>.appspotmail.com. Los correos enviados a la aplicación con este formato se dirigirán a la función.

Python no requiere que la aplicación especifique una secuencia de comandos de controlador en el archivo app.yaml, por lo que puedes eliminar todas las secciones handler de app.yaml.

Tu archivo app.yaml debe conservar las siguientes líneas:

inbound_services:
- mail
- mail_bounce

Enviar correo

No es necesario que cambie la configuración de su aplicación al actualizar a Python. El comportamiento, las funciones y las instrucciones de configuración para enviar correo siguen siendo los mismos. Consulta las siguientes guías para obtener más información:

Recibir correo

Para recibir correo, debes importar el módulo google.appengine.api.mail y usar la clase InboundEmailMessage para representar un correo. Esta clase debe instanciarse para recuperar el contenido del correo de la solicitud HTTP entrante.

Anteriormente, en Python 2, las aplicaciones podían acceder a la clase InboundEmailMessage sobrescribiendo el método receive() en el controlador de aplicaciones web InboundEmailHandler. No es necesario en Python, sino que la aplicación debe crear una instancia de un objeto nuevo.

Frameworks web

Cuando se usan frameworks web de Python, el constructor InboundEmailMessage recibe los bytes del cuerpo de la solicitud HTTP. Hay varias formas de crear el objeto InboundEmailMessage en Python. A continuación, se muestran ejemplos de aplicaciones Flask y Django:

Python 3 (Flask)

En Flask, request.get_data() proporciona los bytes de la solicitud.

@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)

En Django, request.body proporciona los bytes del cuerpo de la solicitud 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 los ejemplos de código completos de esta guía, consulta GitHub.

Otros frameworks compatibles con WSGI

En el caso de otros frameworks compatibles con WSGI, te recomendamos que uses el mismo método que en los ejemplos de Flask y Django para crear el InboundEmailMessage. Este método funciona cuando los bytes del cuerpo de la solicitud HTTP están disponibles directamente.

Aplicación WSGI sin framework web

Si tu aplicación es una aplicación WSGI que no usa un framework web, es posible que los bytes del cuerpo de la solicitud HTTP no estén disponibles directamente. Si los bytes del cuerpo de la solicitud HTTP están disponibles directamente, te recomendamos que uses un framework web de Python.

En Python, se define un método de fábrica llamado from_environ para InboundEmailMessage. Este método es un método de clase que toma el diccionario WSGI environ como entrada y se puede usar en cualquier aplicación WSGI.

En el siguiente ejemplo, observa cómo se toma environ como entrada para obtener mail_message:

Python 3 (aplicación 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")]

Recibir notificaciones de rebote

Una notificación de rebote es un mensaje automático de un sistema de correo electrónico que indica un problema con la entrega de mensajes de tu aplicación. Para procesar las notificaciones de rebote, tu aplicación debe hacer coincidir las rutas de las URLs entrantes con la ruta /_ah/bounce.

Al igual que InboundEmailMessage, se podía acceder a la clase BounceNotification de Python 2 anulando el método receive() en el controlador de webapp BounceNotificationHandler.

En Python, la aplicación debe crear una instancia del objeto BounceNotification, que se puede crear de varias formas en función del framework web de Python que se utilice.

Frameworks web

El objeto BounceNotification se inicializa con los valores que se obtienen llamando a post_vars.get(key).

Cuando se usa un framework web de Python, como Flask o Django, el constructor BounceNotification recibe un diccionario llamado post_vars, que contiene la solicitud POST de los datos del formulario. Para recuperar los datos, el método get() debe definirse en el objeto de entrada. key es una lista de valores de entrada que BounceNotification puede leer y recuperar. Puede ser cualquiera de los siguientes:

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

En la mayoría de los frameworks web, estos datos están disponibles como un diccionario múltiple en el objeto de solicitud. La mayoría de estos tipos se pueden convertir en un diccionario con claves de cadena.

En todas las claves, excepto raw-message, el valor puede ser cualquiera. Normalmente, el valor es un valor único, como una cadena, o una lista de valores, como {'to': ['bob@example.com', 'alice@example.com']}. El valor predeterminado de todos los campos es una cadena vacía. Estos valores se incluirán en las propiedades original y notification.

En el caso de la clave raw-message, el valor debe ser una entrada válida para el constructor de EmailMessage. Puede ser un valor único o una lista de un solo valor. La clave raw-message se usa para inicializar la propiedad original_raw_message del 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)

En Flask, request.form de tipo werkzeug.datastructures.MultiDict proporciona las variables POST. Sin embargo, el método get() de este tipo solo devuelve un valor, aunque haya varios valores para la clave.

Para obtener todos los valores correspondientes a una clave, la aplicación debe llamar a dict(request.form.lists()), lo que da como resultado un diccionario en el que cada valor es una 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)

En Django, request.POST de tipo django.http.QueryDict proporciona las variables POST. Sin embargo, el método get() de este tipo solo devuelve un valor, aunque haya varios valores para la clave.

Para obtener todos los valores correspondientes a una clave, la aplicación debe llamar a dict(request.POST.lists()), lo que da como resultado un diccionario en el que cada valor es una 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")

Aplicación WSGI sin framework web

Si tu aplicación es una aplicación WSGI que no usa un framework web, es posible que las variables de formulario de la solicitud HTTP POST no estén disponibles directamente en un diccionario.

En Python, se define un método de fábrica llamado from_environ para BounceNotification. Este método es un método de clase que toma el diccionario WSGI environ como entrada y se puede usar en cualquier aplicación WSGI.

En el siguiente ejemplo, observa cómo se toma environ como entrada para obtener 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")]

Códigos de ejemplo

Para ver los ejemplos de código completos de esta guía, consulta GitHub.