G-BC
G-BC

Reputation: 91

Send mail in Gmail with python

I'm trying to send a simple test email using the Gmail API but I keep getting the same error for every code sample I find.

My code at the moment is this:

def validationService():
    SCOPES = ['https://mail.google.com/']
    SERVICE_ACCOUNT_FILE = 'creds.json'
    creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    service = build('gmail', 'v1', credentials=creds)
    return service


def SendMessage(service, user_id, message):
  try:
    message = (service.users().messages().send(userId=user_id, body=message).execute())
    print('Message Id:',message['id'])
    return message
  except errors.HttpError as error:
    print('An error occurred:', error)


def CreateMessage(sender, to, subject, message_text):
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}

service = validationService()

email = CreateMessage("[email protected]", "[email protected]", "Test", "This is a test")

sent = SendMessage(service, "[email protected]", email)

Returns

>>> An error occurred: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/me/messages/send?alt=json returned "Bad Request">

I also don't understand the difference between the "sender" parameter at CreateMessage and userId at SendMessage.

If serves a purpose, I'm using a Service Account credential

- Thanks!

Upvotes: 2

Views: 2932

Answers (2)

Solomon Raja
Solomon Raja

Reputation: 103

You don't break your head... Just use email and smtplib libraries. Very simple and fun sending email through Python, if you ask me. I have given my coding below and you should develop your own code based on this. If it works, just let me know - I will be happy. here it is....

    import email, smtplib, os, time, fnmatch
    from datetime import date, timedelta
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart

    def sendMailTo(recepient_id, today):

      login_id = '[email protected]'
      login_pwd = "mypassword"
      fro_ = login_id
      s_name = 'smtp.gmail.com'
      s_port = 587

      fileList = []
      fi_type = '*.pdf'
      file_path = "C:\\Myfolder\\Subfolder"

      srvr = smtplib.SMTP(s_name, s_port)
      srvr.ehlo()
      srvr.starttls()
      srvr.ehlo()
      srvr.login(login_id, login_pwd)

      sub = "Your subject here"
      # loading MIMEMultipart obj onto outer var
      outer = MIMEMultipart('alternative')
      outer["From"] = 'Your company/Personal Name'
      outer["To"] = recepient_id 
      outer['Subject'] = sub

      # storing only pdf files          
      fileList = fnmatch.filter(os.listdir(file_path), fi_type)

      for fi in fileList:
        fi_name = os.path.join(file_path, fi)
        fp = open(fi_name, 'rb')
        img = MIMEImage(fp.read(), _subtype='jpg')
        fp.close()
        img.add_header('Content-disposition', "attachment", filename = fi)
        outer.attach(img)

      #start sending email with attachment with original file name
      srvr.sendmail(fro_, recepient_id, outer.as_string())

    part = None
    outer = None
    srvr.quit()

Upvotes: 2

alberto vielma
alberto vielma

Reputation: 2342

When using a service account with the Gmail API. You need to set domain-wide delegation, which allows the service account to impersonate any user in your G Suite domain. Therefore, you must have a G Suite Account to be able to use domain wide-delegation as the docs say:

If you have a G Suite domain—if you use G Suite, for example—an administrator of the G Suite domain can authorize an application to access user data on behalf of users in the G Suite domain.

So now, why do you need to impersonate a user(a real person)? it's due to the fact a service account is a bot(not a real person) that is used to server-to-server interactions making possible your app calls Google APIs and although the service account has a parameter called client_email, which has a structure like [email protected] it's not a real email that belongs to a real person (kind of confusing I know).

Having said that, I made some changes to your code. First, I modified your validationService function in order to build the service using domain-wide delegation.

def validationService():
    # Set the crendentials 
    credentials = service_account.Credentials.\
        from_service_account_file(SERVICE_ACCOUNT_FILE, scopes= SCOPES)
    # Delegate the credentials to the user you want to impersonate
    delegated_credentials = credentials.with_subject(USER_EMAIL)
    service = discovery.build('gmail', 'v1', credentials=delegated_credentials)
    return service

In your SendMessage function is not necessarily to pass the user who is going to send the email. Using me is enough as the Users.messages: send Parameters state:

userId string The user's email address. The special value me can be used to indicate the authenticated user.

def SendMessage(service, message):
    message = service.users().messages().send(userId="me", body=message).execute()
    return message

Your whole code at the end could look like this one:

from googleapiclient import discovery, errors
from oauth2client import file, client, tools
from google.oauth2 import service_account
from email.mime.text import MIMEText
import base64

SERVICE_ACCOUNT_FILE = 'service_account.json'
SCOPES = [' https://mail.google.com/']
# The user we want to "impersonate"
USER_EMAIL = "user@domain"


def validationService():
    # Set the crendentials 
    credentials = service_account.Credentials.\
        from_service_account_file(SERVICE_ACCOUNT_FILE, scopes= SCOPES)
    # Delegate the credentials to the user you want to impersonate
    delegated_credentials = credentials.with_subject(USER_EMAIL)
    service = discovery.build('gmail', 'v1', credentials=delegated_credentials)
    return service


def SendMessage(service, message):
    message = service.users().messages().send(userId="me", body=message).execute()
    return message


def CreateMessage(sender, to, subject, message_text):
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}


def main():
    try:
        service = validationService()
        email = CreateMessage(USER_EMAIL, "receiverrmail@domain", "Test", "This is a test")
        email_sent = SendMessage(service, email)
        print('Message Id:', email_sent['id'])
    except errors.HttpError as err:
        print('\n---------------You have the following error-------------')
        print(err)
        print('---------------You have the following error-------------\n')


if __name__ == '__main__':
    main()

Notice

You also need to allow your service account to access Google's API when using domain-wide delegation by setting the Managing API client access on your G Suite account.

Upvotes: 3

Related Questions