0

I started off by implementing 37201250 but quickly realized that things have changed since 2017. I managed to implement using googleapiclient. I can send authenticated plain and html mails. These are the things that I need help:

  1. Attachments: The mail goes through but attachment is stripped. When I check my gmail account I see that the sent mails went through without attachments and sending through the UI e-mails arrive as expected.

  2. Errors: the line "errors.HttpError as error:" is showing undefined in pylint. I need the equivalent functionality in googleapiclient?

  3. Refresh: Currently the refresh requires user interaction, which the original script from 37201250 does it programatically using oauth2client I think. How can I implement this without user interaction using googleapiclient?

Here is the code I created:

import os
import pickle
import base64
import mimetypes
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

def GetCredentials(CredentialFilePrefix):
    creds=None
    CFP=CredentialFilePrefix+'.pkl'
    if os.path.exists(CFP):
        print("found file "+CFP+",loading...")
        with open(CFP, 'rb') as token:
            creds = pickle.load(token)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            print("Opening web UI for refreshing token...")
            creds.refresh(Request())
        else:
            CFJ=CredentialFilePrefix+'.json'
            print("Opening web UI for creating token from "+CFJ+"...")
            flow = InstalledAppFlow.from_client_secrets_file(
                CFJ, Scopes)
            creds = flow.run_local_server(port=0)
            print('Created token')
        # Save the credentials for the next run
        print('Saving...')
        with open(CFP, 'wb') as token:
            pickle.dump(creds, token)
        print('Saved token')
    else:
        print("loaded successfully")
    return creds

def CreateHtmlMessage(sender,to,subject,message_text,attached=""):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = to
    msg.attach(MIMEText(message_text, 'html'))
    if len(attached)>0:
        my_mimetype, encoding = mimetypes.guess_type(attached)
        if my_mimetype is None or encoding is not None:
            my_mimetype = 'application/octet-stream' 
        print("Attachment mimetype:"+my_mimetype)
        main_type, sub_type = my_mimetype.split('/', 1)

        if main_type == 'text':
            temp = open(attached, 'r')  # 'rb' will send this error: 'bytes' object has no attribute 'encode'
            attachment = MIMEText(temp.read(), _subtype=sub_type)
            temp.close()

        elif main_type == 'image':
            temp = open(attached, 'rb')
            attachment = MIMEImage(temp.read(), _subtype=sub_type)
            temp.close()

        elif main_type == 'audio':
            temp = open(attached, 'rb')
            attachment = MIMEAudio(temp.read(), _subtype=sub_type)
            temp.close()            

        elif main_type == 'application' and sub_type == 'pdf':   
            temp = open(attached, 'rb')
            attachment = MIMEApplication(temp.read(), _subtype=sub_type)
            temp.close()
        elif main_type == 'application' and sub_type == 'vnd.openxmlformats-officedocument.wordprocessingml.document':   
            temp = open(attached, 'rb')
            attachment = MIMEApplication(temp.read(), _subtype=sub_type)
            temp.close()
        else:                              
            attachment = MIMEBase(main_type, sub_type)
            temp = open(attached, 'rb')
            attachment.set_payload(temp.read())
            temp.close()
        filename = os.path.basename(attached)
        attachment.add_header('Content-Disposition', 'attachment', filename=filename) # name preview in email
        msg.attach(attachment) 

    raw = base64.urlsafe_b64encode(msg.as_bytes())
    raw = raw.decode()
    body = {'raw': raw}
    return body

def SendMessage(encoded,creds):
    service = build('gmail', 'v1', credentials=creds)
    user_id="me"
    try:    
        message = (service.users().messages().send(userId=user_id, body=encoded).execute())
        print('Message Id: %s' % message['id'])
        return message
    except errors.HttpError as error:
        print('An error occurred: %s' % error)
    return ""

#Global Variables
Toolname='SendMail'
ToolVersion='0.5.20201223.1'
Scopes = 'https://www.googleapis.com/auth/gmail.send'
Client_Secret_File = 'credentials'
App_Name = 'MailSender'
to = "xxx@test.com"
sender = "xxx@gmail.com"
subject = "Test Mail with attachment"
msgHtml = "Hi<br/><b>Html Email</b>"
msgPlain = "Hi\nPlain Email"
attachment="doc.pdf"
#Global Variables

def main():
    credentials = None
    credentials = GetCredentials(Client_Secret_File)
    print("Got credentials")
    msgEncoded=CreateHtmlMessage(sender, to, subject,msgHtml,attachment)
    SendMessage(msgEncoded,credentials)
'/path/to/file.pdf')

if __name__ == '__main__':
    main()
  • "These are the things that I need help"—[please ask only one question at a time](https://meta.stackexchange.com/q/39223/248627) and please read [Why is "Can someone help me?" not an actual question?](https://meta.stackoverflow.com/a/284237/354577). – ChrisGPT was on strike Dec 23 '20 at 17:04
  • Closing this question per comment, asked revised version [here](https://stackoverflow.com/questions/65436970/sending-email-via-gmail-with-python-fails-to-send-attachments) – Murat Erenturk Dec 24 '20 at 10:24

0 Answers0