I have my mail my_name#company.com and uses gmails API (and python) to send some mails. The problem is that when the mail hit the inbox the "from" is shown as my_name#company.com '<my_name#company.com>' where I want it to be First Name <my_name#company.com>.
I have tried using different variations of "First Name '<my_name#company.com>'" but I get a RefreshError: ('invalid_request: Invalid impersonation "sub" field.', '{\n "error": "invalid_request",\n "error_description": "Invalid impersonation \\u0026quot;sub\\u0026quot; field."\n}').
from __future__ import print_function
from googleapiclient.discovery import build
from apiclient import errors
from httplib2 import Http
from email.mime.text import MIMEText
import base64
from google.oauth2 import service_account
# Email variables. Modify this!
EMAIL_FROM = "First Last '<my_name#company.com>'"
EMAIL_TO = 'some_mail#hotmail.com'
EMAIL_SUBJECT = 'Hello from Me!'
EMAIL_CONTENT = 'Some body'
# Call the Gmail API
def service_account_login():
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
SERVICE_ACCOUNT_FILE = 'my-credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject(EMAIL_FROM)
service = build('gmail', 'v1', credentials=delegated_credentials)
return service
def create_message(sender, to, subject, message_text):
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
return {"raw": raw}
def send_message(service, user_id, message):
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
service = service_account_login()
message = create_message(EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, EMAIL_CONTENT)
sent = send_message(service,'me', message)
I just sent an email recently showing the name as you wanted, I got it with this line, similar to what you already had but without the single quotation marks:
message['from'] = "First Name <something#example.com>"
So the trick is fairly simple - the EMAIL_FROM is first used to create a service with that email, which is why you cannot write My Name <my_name#company.com>. You can do that when you need to specify the "FROM " mail e.g in the very buttom of create_message= f"First name<{EMAIL_FROM}>,...)
Related
I am following this example on their website:
https://developers.google.com/gmail/api/guides/sending
basically, I want to use create_message_with_attachment() and send_message() to send my email. Below is what I got so far:
#send our spreadsheet
from __future__ import print_function
import httplib2
import os
from apiclient import discovery
from apiclient import errors
from googleapiclient import http
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from apiclient.http import MediaIoBaseDownload
import io
import mimetypes
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
import base64
def create_message_with_attachment(
sender, to, subject, message_text, file):
"""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.
file: The path to the file to be attached.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart()
message['to'] = to
message['from'] = sender
message['subject'] = subject
msg = MIMEText(message_text)
message.attach(msg)
content_type, encoding = mimetypes.guess_type(file)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
fp = open(file, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
filename = os.path.basename(file)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
#return {'raw': base64.urlsafe_b64encode(message.as_bytes())}
def send_message(service, user_id, message):
"""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.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
def main(mail_service,spreadsheetId,new_invoice_filename):
print('We are emailing the spreadsheet.')
sender = 'me'
to = 'me'
subject = 'test '+new_filename
message_text = 'Hi sdf \n P '
attachment = 'D:/My Documents/file.pdf'
msg = create_message_with_attachment(sender, to, subject, message_text, attachment)
send_message(mail_service, sender, msg)
so in the documentation, it says create_message_with_attachment will specifically return the raw field, but when I send the message with send_message(), I don't see how I can put the raw I got in there.
So I am really confused.
Thanks
The code below works. I can send emails through the Gmail API, but it takes awhile to send 10 personalized emails, based on wall time (20-30 secs). Is there a way to optimize the code below to send emails faster? There are quota limitations.
Is the max number emails one can send, a 100 per day? There seems to be a difference between number of emails one can send and the number of receipts per email. This is the documentation I am sourcing: https://developers.google.com/apps-script/guides/services/quotas
I am using the consumer version.
Feature Consumer (gmail.com)
Calendar events created 5,000
Contacts created 1,000
Documents created 250
Email recipients per day 100*
Code:
import httplib2
import os
import oauth2client
from oauth2client import client, tools
import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from apiclient import errors, discovery
import mimetypes
import pandas as pd
import textwrap
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE = 'secret.json'
APPLICATION_NAME = 'AppName'
def get_credentials():
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-send.json')
store = oauth2client.file.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
credentials = tools.run_flow(flow, store)
print 'Storing credentials to ' + credential_path
return credentials
def SendMessage(sender, to, subject,message_text):
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
message1 = CreateMessageHtml(sender, to, subject, message_text)
result = SendMessageInternal(service, "me", message1)
return result
def CreateMessageHtml(sender, to, subject, message_text):
msg = MIMEText(message_text)
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = to
return {'raw': base64.urlsafe_b64encode(msg.as_string())}
def SendMessageInternal(service, user_id, message):
try:
message = (service.users().messages().send(userId=user_id, body=message).execute())
print 'Message Id: %s' % message['id']
return message
except errors.HttpError, error:
print 'An error occurred: %s' % error
return "Error"
return "OK"
def main():
df = pd.read_csv('testdata.csv')
for index,row in df.iterrows():
to = row['Email']
sender = "sender"
subject = "subject"
dedent_text = '''Hello {}, \n
thank you for your question.'''.format(row['First'])
message_text = textwrap.dedent(dedent_text).strip()
SendMessage(sender, to, subject, message_text)
if __name__ == '__main__':
main()
Try caching the result of the service call, so that service is passed to SendMessage. This way, you don't take API call times to setup the API for each individual emails you send.
So at top of your main:
def main():
# Do once
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
df = pd.read_csv('testdata.csv')
for index,row in df.iterrows():
to = row['Email']
sender = "sender"
subject = "subject"
dedent_text = '''Hello {}, \n
thank you for your question.'''.format(row['First'])
message_text = textwrap.dedent(dedent_text).strip()
# service is is reused here for each message
SendMessage(service, sender, to, subject, message_text)
Also, if you need to send many messages, make sure you invoke one Python invokation per large batch, since starting up the interpreter and loading many packages can take a while each time.
Using the sample code from the GMail API Example: Send Mail, and after following rules for authentication, it's simple enough to send a programmatically generated email, via a gmail account. What isn't obvious from the example is how to set that email to be HTML formatted.
The Question
How do I get HTML formatting in my gmail-api send messages, using python?
I have this...
message_body = "Hello!\nYou've just received a test message!\n\nSincerely,\n-Test Message Generator\n"
and I want it to be this...
Hello!
You've just received a test message!
Sincerely,
-Test Message Generator
Example Source Code from GMail-API
Below is a slightly modified version of the example, but still works:
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 __name__ == '__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='cbsd.tools#gmail.com')
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='cbsd.tools#gmail.com')
cli = parser.parse_args()
pprint(dir(cli))
main(cli)
Tried as I might, with this code, and variations on it, I could not get html formatted code, nor could I get simple escape characters to create carriage returns where they needed to be.
Here's what didn't work
Trying the following didn't work either:
modifying line 69 to add additional message dictionary parameters... ie.
{'raw': base64.urlsafe_b64encode(message.as_string()), 'payload': {'mimeType': 'text/html'}}
As documented here in the GMail API Docs
Adding a variety of escaped backslashes into the message text:
\n... ie: \\n nor \\\n and just rendered as those exact characters
Adding <br> </br> <br/> did not add new lines and just rendered as those exact characters
Adding \r did not add new lines and just rendered as that exact character
Reasons this is not a duplicate question
This SO link deals with multipart/signed messages
I am uninterested in multipart messaging because it is an inelegant solution. I want the WHOLE message to be HTML.
Additionally, this link has no accepted answer, and the one answer present is a non-answer as it simply states that they are sending a problem statement to Google.
This one deals with c#, which I am not interested in since I'm writing in python
This one also uses SMTP which I'm not interested in
This one is for Rails
After doing a lot of digging around, I started looking in to the python side of the message handling, and noticed that a python object is actually constructing the message to be sent for base64 encoding into the gmail-api message object constructor.
See line 63 from above: message = MIMEText(message_text)
The one trick that finally worked for me, after all the attempts to modify the header values and payload dict (which is a member of the message object), was to set (line 63):
message = MIMEText(message_text, 'html') <-- add the 'html' as the second parameter of the MIMEText object constructor
The default code supplied by Google for their gmail API only tells you how to send plain text emails, but they hide how they're doing that.
ala...
message = MIMEText(message_text)
I had to look up the python class email.mime.text.MIMEText object.
That's where you'll see this definition of the constructor for the MIMEText object:
class email.mime.text.MIMEText(_text[, _subtype[, _charset]])
We want to explicitly pass it a value to the _subtype. In this case, we want to pass: 'html' as the _subtype.
Now, you won't have anymore unexpected word wrapping applied to your messages by Google, or the Python mime.text.MIMEText object
The Fixed Code
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,'html')
message['to'] = to
message['from'] = sender
message['subject'] = subject
message['cc'] = cc
pprint(message)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
Try this:
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'
The accepted answer works, but in my journey down this rabbit hole, I found the right place to put the payload from the original question, so here's a full example. I'm using a service account with domain-wide delegation:
import base64
from googleapiclient.discovery import build
from google.oauth2 import service_account
from email.message import EmailMessage
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
CREDS = service_account.Credentials.from_service_account_file(
serviceAcct, scopes=SCOPES, subject=senderEmail)
with build('gmail', 'v1', credentials=CREDS) as service:
msg = EmailMessage()
content="Message body in <b>html</b> format!"
msg['To'] = recipientEmail
msg['From'] = senderEmail
msg['Subject'] = 'Gmail API test'
# Use this for plain text
# msg.set_content(content)
# Otherwise, use this for html
msg.add_header('Content-Type','text/html')
msg.set_payload(content)
encodedMsg = base64.urlsafe_b64encode(msg.as_bytes()).decode()
try:
sendMsg = service.users().messages().send(
userId=senderEmail,
body={ 'raw': encodedMsg }
).execute()
print('Msg id:', sendMsg['id'])
except Exception as e:
print('Error:', e)
import httplib2
import os
from httplib2 import Http
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
import base64
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
import mimetypes
from apiclient import errors
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
#SCOPES = 'https://www.googleapis.com/'
SCOPES = 'https://www.googleapis.com/auth/gmail.compose'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Quickstart'
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-quickstart.json')
store = oauth2client.file.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 compatability with Python 2.6
credentials = tools.run(flow, store)
print 'Storing credentials to ' + credential_path
return credentials
def CreateMessage(sender, to, 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 base64 encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
return {'raw': base64.b64encode(message.as_string())}
def SendMessage(service, user_id, message):
"""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.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print 'Message Id: %s' % message['id']
return message
except errors.HttpError, error:
print 'An error occurred: %s' % error
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
testMessage = CreateMessage('hey#gmail.com', 'johndoeisawesome#gmail.com', 'ENTER SUBJECT', 'ENTER EMAIL BODY')
testSend = SendMessage(service, 'me', testMessage)
i get email in johndoeisawesome#gmail.com as the email account that i enabled gmail api as, NOT hey#gmail.com
is that the correct behavior?
Then, whats the point of filling out 'from' in CreateMessage()?
You can use a Gmail alias in the From-header,
From: myalias#gmail.com
You can follow this guide to set up an alias.
I'm trying to send an email using the Gmail API in python. I think I followed the relevant documentation and youtube vids.
I'm running into this error:
googleapiclient.errors.HttpError: HttpError 403 when requesting https://www.googleapis.com/gmail/v1/users/me/messages/send?alt=json returned "Insufficient Permission"
Here is my script:
#!/usr/bin/env python
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
from email.mime.text import MIMEText
import base64
import errors
SCOPES = 'https://mail.google.com/'
CLIENT_SECRET = 'client_secret.json'
store = file.Storage('storage.json')
credz = store.get()
if not credz or credz.invalid:
flags = tools.argparser.parse_args(args=[])
flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPES)
credz = tools.run_flow(flow, store, flags)
GMAIL = build('gmail', 'v1', http=credz.authorize(Http()))
def CreateMessage(sender, to, 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.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
return {'raw': base64.urlsafe_b64encode(message.as_string())}
def SendMessage(service, user_id, message):
"""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.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print 'Message Id: %s' % message['id']
return message
except errors.HttpError, error:
print 'An error occurred: %s' % error
message = CreateMessage('testemail#gmail.com', 'testemail#gmail.com', 'test_subject', 'foo')
print message
SendMessage(GMAIL, 'me', message)
I tried adding scopes, trying different emails, etc. I have authenticated by logging into my browser as well. (The testemail#gmail.com is a dummy email btw)
Try deleting generated storage.json file and then try again afresh.
you might be trying this script with different scopes so "storage.json" might be having wrong details.
I had the same problem.
I solved it by running again the quickstart.py that provides google and change SCOPE so that google can give you all permissions you want. After that don't need to have SCOPE or CLIENT_SECRET on your new code to send a message, just get_credentials(), CreateMessage() and SendMessage() methods.