Search code examples
pythonemailgmail-apimime-types

How to send an existing message in a new email without losing formatting


Context: I'm attempting to send an existing email in my inbox to a new thread.

Problem: I've successfully sent the email body using this function however the body loses the formatting of the original email and only sends text.

I think it makes more sense to add the entire payload to the request body, as documented on the gmail API page "Try this API" section:

However when I add payload to the request body:

def create_message(sender, to, subject, thread_id, message_id, payload, service):
  """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.
  """

  message = MIMEMultipart('alternative')
  message['to'] = to
  message['from'] = sender
  message['subject'] = 'Re: %s' %subject

  return {'raw': raw, 'threadId': thread_id, 'payload': payload}

The emails are sent with no content. How can I add an existing email to a new thread without having to decode and encode and lose the email's formatting?


Solution

  • After messing around I've made two functions that can pass the plain and html content types to a new email for anyone else who might be struggling:

    def get_all_parts(service, user_id, msg_id):
      message = service.users().messages().get(userId=user_id, id=msg_id, format='raw').execute()
      msg_bytes = base64.urlsafe_b64decode(message['raw'].encode('ASCII'))
      b = email.message_from_bytes(msg_bytes)
      body = []
    
      if b.is_multipart():
        for part in b.walk():
          if part.get_content_maintype() != 'multipart':
            this_part = []
            this_part.append(part.get_payload(decode=True))
            this_part.append(part.get_content_type())
            body.append(this_part)
    
      return body
    
    def create_message(sender, to, subject, thread_id, message_id, message_text, service):
      message = MIMEMultipart()
      message['to'] = to
      message['from'] = sender
      message['subject'] = 'Re: %s' %subject
      for part in message_text:
        text = part[1].split('/') # 'text/plain' -> ['text', 'plain']
        new_part = MIMEText(str(part[0]), text[1])
        print(part[1])
        message.attach(new_part)
    
      raw = base64.urlsafe_b64encode(message.as_string().encode('UTF-8')).decode('ascii')   
      body = {'raw': raw, 'threadId': thread_id}
    
    enter code here
    

    This is definitely not an exhaustive function for all emails but works for alternative content types.