ti-enxame.com

Como faço para enviar e-mails formatados em HTML, através do gmail-api para python

Usando o código de exemplo do Exemplo da API do GMail: Enviar email e depois de seguir as regras de autenticação, é simples o suficiente enviar um email gerado por programação, por meio de uma conta do Gmail. O que não é óbvio no exemplo é como configurar esse email para ser formatado em HTML.


A questão

Como obtenho formatação HTML em minhas mensagens de envio do gmail-api, usando python?

Eu tenho isto...

message_body = "Hello!\nYou've just received a test message!\n\nSincerely,\n-Test Message Generator\n"

e eu quero que seja isso ...

Hello!
You've just received a test message!

Sincerely,
-Test Message Generator

Exemplo de código fonte da API do GMail

Abaixo está uma versão ligeiramente modificada do exemplo, mas ainda funciona:

import argparse
import base64
from pprint import pformat
from pprint import pprint
import httplib2
import os
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage


SCOPES = 'https://mail.google.com/'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Test EMail App'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """

    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'gmail-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def create_message(sender, to, cc, subject, message_text):
    """Create a message for an email.

    Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

    Returns:
    An object containing a base64url encoded email object.
    """
    print(sender + ', ' + to + ', ' + subject + ', ' + message_text)
    message = MIMEText(message_text)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    message['cc'] = cc
    pprint(message)
    return {'raw': base64.urlsafe_b64encode(message.as_string())}

def send_message(service, user_id, message_in):
    """Send an email message.

    Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

    Returns:
    Sent Message.
    """
    pprint(message_in)
    try:
        message = (service.users().messages().send(userId=user_id, body=message_in).execute())
        pprint(message)
        print ('Message Id: %s' % message['id'])
        return message
    except errors.HttpError, error:
        print ('An error occurred: %s' % error)

def main(cli):
    """Shows basic usage of the Gmail API.

    Creates a Gmail API service object and outputs a list of label names
    of the user's Gmail account.
    """


    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('gmail', 'v1', http=http)

    email_msg = create_message(cli.addr_from, cli.addr_to, cli.addr_cc, cli.subject, cli.message)
    msg_out = service.users().messages().send(userId = 'me', body = email_msg).execute()
    pprint(msg_out)


if __== '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-m', '--message',   help = 'The message to send in the email', default='<MESSAGE = unfinished>')
    parser.add_argument('-t', '--addr_to',   help = 'the list of comma separated emails to send', default='[email protected]')
    parser.add_argument('-s', '--subject',   help = 'the email subject', default='<SUBJECT = undefined>')
    parser.add_argument('-c', '--addr_cc',   help = 'email CC\'s', default='')
    parser.add_argument('-f', '--addr_from', help = 'Email address to send from', default='[email protected]')
    cli = parser.parse_args()
    pprint(dir(cli))

    main(cli)

Por mais que eu tentasse, com este código, e variações nele, eu não conseguia código formatado em HTML, nem poderia obter caracteres de escape simples para criar retornos de carro onde eles precisavam estar.


Aqui está o que não funcionou

Tentando o seguinte não funcionou:

  1. modificando line 69 para adicionar outros parâmetros do dicionário de mensagens ... ou seja,
    • {'raw': base64.urlsafe_b64encode(message.as_string()), 'payload': {'mimeType': 'text/html'}}
    • Conforme documentado aqui no GMail API Docs
    • enter image description here
  2. Adicionando uma variedade de barras invertidas no texto da mensagem:
    • \n... ie: \\n nem \\\n e apenas processados ​​como aqueles caracteres exatos
    • Adicionar <br> </br> <br/> não adicionou novas linhas e acabou de ser renderizado como esses caracteres exatos
    • Adicionar \r não adicionou novas linhas e acabou de ser renderizado como esse caractere exato

Razões que isto não é uma questão duplicada

16
Andrew

Depois de pesquisar bastante, comecei a examinar o lado python da manipulação de mensagens e notei que um objeto python está realmente construindo a mensagem a ser enviada para a codificação base64 no construtor do objeto de mensagem gmail-api.

Veja a linha 63 acima: message = MIMEText(message_text)

O único truque que finalmente funcionou para mim , depois de todas as tentativas de modificar os valores de cabeçalho e payload dict (que é um membro do objeto message), foi set (line 63):

  • message = MIMEText(message_text, 'html') <- adiciona o 'html' como o segundo parâmetro do construtor do objeto MIMEText

O código padrão fornecido pelo Google para sua API do gmail apenas informa como enviar e-mails de texto sem formatação, mas eles escondem como estão fazendo isso. Ala ... message = MIMEText(message_text)

Eu tive que procurar a classe python email.mime.text.MIMEText object. É aí que você verá essa definição do construtor para o objeto MIMEText:

  • class email.mime.text.MIMEText (_text [ _subtype [ _charset]]) Queremos explicitamente passar um valor para o _subtype. Neste caso, queremos passar: 'html' como o _subtype.

Agora, você não terá mais quebra automática inesperada do Word aplicado às suas mensagens pelo Google ou o objeto mime.text.MIMEText do Python


O código fixo

 def create_message (remetente, para, cc, assunto, mensagem_texto): 
 "" "Crie uma mensagem para um email. 
 
 Args: 
 remetente: Endereço de e-mail do remetente. 
 para: Endereço de e-mail do destinatário. 
 assunto: O assunto da mensagem de e-mail. 
 message_text: O texto da mensagem de e-mail. 
 
 Retorna: 
 Um objeto contendo um objeto de email codificado base64url. 
 "" "
 Print (remetente + ',' + to + ' , '+ assunto +', '+ mensagem_texto) 
 mensagem = MIMETexto (mensagem_texto,' html ') 
 mensagem [' para '] = para 
 mensagem [' de '] = remetente 
 mensagem ['assunto'] = assunto 
 mensagem ['cc'] = cc 
 pprint (mensagem) 
 return {'raw': base64. urlsafe_b64encode (message.as_string ())} 
17
Andrew

Tente isto:

    def CreateMessage(emailSubject, emailTo, emailFrom, message_body, emailCc, html_content=None):
        try:
            message = MIMEMultipart('alternative')
            message['to'] = emailTo
            message['from'] = emailFrom
            message['subject'] = emailSubject
            message['Cc'] = emailCc
            body_mime = MIMEText(message_body, 'plain')
            message.attach(body_mime)
            if html_content:
                html_mime = MIMEText(html_content, 'html')
                message.attach(html_mime)
            return {
                'raw': base64.urlsafe_b64encode(
                    bytes(
                        message.as_string(),
                        "utf-8")).decode("utf-8")}
        except Exception as e:
            print('Error in CreateMessage()', e)
            return '400'
1
Januka samaranyake