How to save a gmail draft with inline images? - python

I want to use a python script to save a gmail draft containing inline images and rendered html text, so that it can be checked before being sent manually.
It seems the cid protocol for attaching inline images doe not work when SENDING the email, even if the draft looks good.
import pickle
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import base64
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
import os
from apiclient import errors
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://mail.google.com/']
def CreateDraft(user_id, message_body):
"""Create and insert a draft email.
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_body: The body of the email message, including headers.
Returns:
Draft object, including draft id and message meta data.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server()
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
try:
message = {'message': message_body}
draft = service.users().drafts().create(userId=user_id, body=message).execute()
return draft
except errors.HttpError as error:
print ('An error occurred: %s' % error)
return None
def CreateMessageWithAttachment(sender, to, subject, message_text, file_dir, filename):
"""Create a message for an email.
Args:
* sender: The email address of the sender.
* to: The email address of the receiver.
* subject: The subject of the email message.
* message_text: The html text of the email message.
* file_dir: The directory containing the file to be attached.
* filename: The name of 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,'html')
message.attach(msg)
path = os.path.join(file_dir, filename)
content_type, encoding = mimetypes.guess_type(path)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(path, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image': #The only useful one for images
fp = open(path, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(path, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(path, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
This script creates a draft with an attached file. However, if the file is an image I would like it to be inline instead of an attachement...

Related

How to authonticate only one / offline with client.json key for Gmail API?

I am running an app that needs to access the gmail emails. I had store the credentials in the json file and writing pickle file to cache the token. Is it really necessary to authenticate every time you run the application ? I am running application on Pi and DO NOT HAVE an access to it once i deploy that. How can store the cache that i can use for longer time/ multiple time as right now it is going to expire in the middle of the run and turn off my application. I followed the Google API docs but hard to follow that. Here is my code for the authentication would look like(it is same as their documentation. You can comment out the Pi library and its function that would not stop you to run the code.)
Could anyone please suggest a better way to do this ?
# GMail Downloader using the GMail API
# Logs into GMail using the API, checks for Iridium SBD messages every minute,
# saves the message and attachment to file, and moves the message out of the inbox
# Follow these instructions to create your credentials:
# https://developers.google.com/gmail/api/quickstart/python
from __future__ import print_function
import httplib2
import os
import base64
import email
import datetime
import time
from apiclient import discovery
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from apiclient import errors
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
# Pi libraries
import RPi.GPIO as GPIO
Relay_Ch1 = 26
Relay_Ch2 = 20
Relay_Ch3 = 21
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(Relay_Ch1, GPIO.OUT)
GPIO.setup(Relay_Ch2, GPIO.OUT)
GPIO.setup(Relay_Ch3, GPIO.OUT)
# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/gmail-python-quickstart.json
#SCOPES = 'https://www.googleapis.com/auth/gmail.readonly' # Read only
# SCOPES = 'https://www.googleapis.com/auth/gmail.modify' # Everything except delete
SCOPES = 'https://mail.google.com/' # Full permissions
# CLIENT_SECRET_FILE = 'client_secret.json'
# APPLICATION_NAME = 'Gmail API Python 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.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
return service
def ListMessagesMatchingQuery(service, user_id, query=''):
"""List all Messages of the user's mailbox matching the query.
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.
query: String used to filter messages returned.
Eg.- 'from:user#some_domain.com' for Messages from a particular sender.
Returns:
List of Messages that match the criteria of the query. Note that the
returned list contains Message IDs, you must use get with the
appropriate ID to get the details of a Message.
"""
response = service.users().messages().list(userId=user_id,q=query).execute()
messages = []
if 'messages' in response:
messages.extend(response['messages'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().messages().list(userId=user_id, q=query,pageToken=page_token).execute()
messages.extend(response['messages'])
return messages
def SaveAttachments(service, user_id, msg_id):
"""Get and store attachment from Message with given id.
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.
msg_id: ID of Message containing attachment.
"""
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
local_date = datetime.datetime.fromtimestamp(float(message['internalDate'])/1000.)
date_str = local_date.strftime("%y-%m-%d_%H-%M-%S_")
for part in message['payload']['parts']:
if part['filename']:
if 'data' in part['body']:
data=part['body']['data']
else:
att_id=part['body']['attachmentId']
att=service.users().messages().attachments().get(userId=user_id, messageId=msg_id,id=att_id).execute()
data=att['data']
file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
path = date_str+part['filename']
with open(path, 'wb') as f:
f.write(file_data)
f.close()
def SaveMessageBody(service, user_id, msg_id):
"""Save the body from Message with given id.
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.
msg_id: ID of Message.
"""
message = service.users().messages().get(userId=user_id, id=msg_id, format='raw').execute()
msg_str = base64.urlsafe_b64decode(message['raw'].encode('ASCII'))
# mime_msg = email.message_from_string(msg_str)
mime_msg = email.message_from_bytes(msg_str)
messageMainType = mime_msg.get_content_maintype()
file_data = ''
#print(messageMainType)
if messageMainType == 'multipart':
for part in mime_msg.get_payload():
partType = part.get_content_maintype()
#print('...'+partType)
if partType == 'multipart':
for multipart in part.get_payload():
multipartType = multipart.get_content_maintype()
#print('......'+multipartType)
if multipartType == 'text':
file_data += multipart.get_payload()
break # Only get the first text payload
elif partType == 'text':
file_data += part.get_payload()
elif messageMainType == 'text':
file_data += mime_msg.get_payload()
local_date = datetime.datetime.fromtimestamp(float(message['internalDate'])/1000.)
date_str = local_date.strftime("%y-%m-%d_%H-%M-%S_")
subject = GetSubject(service, user_id, msg_id);
for c in r' []/\;,><&*:%=+#!#^()|?^': # substitute any invalid characters
subject = subject.replace(c,'_')
path = date_str+subject+".txt"
with open(path, 'w') as f:
f.write(file_data)
f.close()
def GetSubject(service, user_id, msg_id):
"""Returns the subject of the message with given id.
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.
msg_id: ID of Message.
"""
subject = ''
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
payload = message["payload"]
headers = payload["headers"]
for header in headers:
if header["name"] == "Subject":
subject = header["value"]
break
return subject
def MarkAsRead(service, user_id, msg_id):
"""Marks the message with given id as read.
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.
msg_id: ID of Message.
"""
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'removeLabelIds': ['UNREAD']}).execute()
def MoveToLabel(service, user_id, msg_id, dest):
"""Changes the labels of the message with given id to 'move' it.
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.
msg_id: ID of Message.
dest: destination label
"""
# Find Label_ID of destination label
results = service.users().labels().list(userId=user_id).execute()
labels = results.get('labels', [])
for label in labels:
if label['name'] == dest: dest_id = label['id']
# service.users().messages().get(userId='me', id=message['id']).execute()
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'addLabelIds': [dest_id]}).execute()
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'removeLabelIds': ['INBOX']}).execute()
def main():
"""Creates a Gmail API service object.
Searches for unread messages with "SBD Msg From Unit" in the subject.
Saves the message body and attachment to disk.
Marks the message as read.
Moves it to the SBD folder.
"""
# credentials = get_credentials()
# http = credentials.authorize(httplib2.Http())
# service = discovery.build('gmail', 'v1', http=http)
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
results = service.users().messages().list(userId='me', labelIds = ['INBOX']).execute()
# results = service.users().messages().list(userId='me, q="subject:"SBD Msg From Unit" is:unread has:attachment").execute()
# messages = results.get('messages', [])
messages = ListMessagesMatchingQuery(service, 'me', 'subject:\"Emergency Alert Activated: GSATMicro\" is:unread') # has:attachment
if messages:
for message in messages:
print('Processing: '+GetSubject(service, 'me', message["id"]))
SaveMessageBody(service, 'me', message["id"])
# SaveAttachments(service, 'me', message["id"])
MarkAsRead(service, 'me', message["id"])
# MoveToLabel(service, 'me', message["id"], 'SBD')
#email_subject = message['subject']
#email_from = message['from']
#if email_from == "alerts#gsattrack.com" and email_subject == 'EMERGENCY: GSATMicro - Emergency On':
print("Emergency Alert received from GSATMicro..")
print("\n Help will be on its way .. Turning relay ON.. \n")
#Control the Channel 1
print("\n Turning Relay 1 ON.. \n")
GPIO.output(Relay_Ch1, GPIO.HIGH)
print("Channel 1:The Common Contact is access to the Normal Open Contact!")
time.sleep(0.5)
GPIO.output(Relay_Ch1, GPIO.LOW)
print("Channel 1:The Common Contact is access to the Normal Closed Contact!\n")
time.sleep(0.5)
GPIO.output(Relay_Ch1, GPIO.HIGH)
#else:
#print('No messages found!')
if __name__ == '__main__':
print('GMail API Downloader')
print('Press Ctrl-C to quit')
try:
while True:
#print('Checking for messages...')
main()
for i in range(60):
time.sleep(1) # Sleep
except KeyboardInterrupt:
print('Ctrl-C received!')

Downloading password-protected PDFs using GMAIL API in Python

This is my code to download the pdf attachments using GMAIL API in Python. It works fine in sense that I am able to download the pdf attachments but they don't open. I get the following error "There was an error opening this document. The file is damaged and could not be repaired".
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
import base64
from apiclient import errors
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
# to manage a different gmail account, delete the existing token.json file from the folder
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail API
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
# Call the Gmail API to fetch INBOX
results = service.users().messages().list(userId='me', labelIds=['Label_9213971794059785832']).execute()
messages = results.get('messages', [])
print(messages[1])
if not messages:
print("No messages found.")
else:
print("Message snippets:")
for message in messages:
GetAttachments(service,'me',message['id'])
# msg = service.users().messages().get(userId='me', id=message['id']).execute()
# print(msg)
# break
def GetAttachments(service, user_id, msg_id, store_dir="attachments/"):
"""Get and store attachment from Message with given id.
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.
msg_id: ID of Message containing attachment.
store_dir: The directory used to store attachments.
"""
try:
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
parts = [message['payload']]
while parts:
part = parts.pop()
if part.get('parts'):
parts.extend(part['parts'])
if part.get('filename'):
if 'data' in part['body']:
file_data = base64.urlsafe_b64decode(part['body']['data'].encode('UTF-8'))
#self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], part['size']))
elif 'attachmentId' in part['body']:
attachment = service.users().messages().attachments().get(
userId=user_id, messageId=message['id'], id=part['body']['attachmentId']
).execute()
file_data = base64.urlsafe_b64decode(attachment['data'].encode('UTF-8'))
#self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], attachment['size']))
else:
file_data = None
if file_data:
#do some staff, e.g.
path = ''.join([store_dir, part['filename']])
with open(path, 'w') as f:
f.write(str(file_data))
except errors.HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
please help!
Found the mistake! I needed to write the bytes, not string.
modified two lines of the code as following:
with open(path, 'wb') as f:
f.write(file_data)

Ipython error in jupyter notebook, no stack trace

I'm using the code from here: Sending email via gmail & python
Step 3, (my version shown below)
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
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Send Email'
def get_credentials():
home_dir = "C:\\Users\\kelvi_000\\Documents\\Jupyter Notebooks\\"
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-email-send.json')
store = oauth2client.file.Storage(credential_path)
#credentials = store.get()
credentails = False
if True: #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, msgHtml, msgPlain, attachmentFile=None):
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
if attachmentFile:
message1 = createMessageWithAttachment(sender, to, subject, msgHtml, msgPlain, attachmentFile)
else:
message1 = CreateMessageHtml(sender, to, subject, msgHtml, msgPlain)
result = SendMessageInternal(service, "me", message1)
return result
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 as error:
print('An error occurred: %s' % error)
return "Error"
return "OK"
def CreateMessageHtml(sender, to, subject, msgHtml, msgPlain):
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = to
msg.attach(MIMEText(msgPlain, 'plain'))
msg.attach(MIMEText(msgHtml, 'html'))
return {'raw': base64.urlsafe_b64encode(msg.as_string())}
def createMessageWithAttachment(
sender, to, subject, msgHtml, msgPlain, attachmentFile):
"""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.
msgHtml: Html message to be sent
msgPlain: Alternative plain text message for older email clients
attachmentFile: The path to the file to be attached.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart('mixed')
message['to'] = to
message['from'] = sender
message['subject'] = subject
messageA = MIMEMultipart('alternative')
messageR = MIMEMultipart('related')
messageR.attach(MIMEText(msgHtml, 'html'))
messageA.attach(MIMEText(msgPlain, 'plain'))
messageA.attach(messageR)
message.attach(messageA)
print("create_message_with_attachment: file: %s" % attachmentFile)
content_type, encoding = mimetypes.guess_type(attachmentFile)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(attachmentFile, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(attachmentFile, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(attachmentFile, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(attachmentFile, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
filename = os.path.basename(attachmentFile)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
def main():
to = "to#address.com"
sender = "from#address.com"
subject = "subject"
msgHtml = "Hi<br/>Html Email"
msgPlain = "Hi\nPlain Email"
SendMessage(sender, to, subject, msgHtml, msgPlain)
# Send message with attachment:
SendMessage(sender, to, subject, msgHtml, msgPlain, '/path/to/file.pdf')
if __name__ == '__main__':
main()
with the difference from the other answer is on line 19, I've modified variable [home_dir] to:
home_dir = "C:\\Users\\kelvi_000\\Documents\\Jupyter Notebooks\\"
In addition to:
#credentials = store.get()
credentails = False
if True: #not credentials or credentials.invalid:
commenting out the declaration for [credentials] and modifying the if credentials aren't valid part. (lines 25-27)
Now the problem is:
usage: ipykernel_launcher.py [--auth_host_name AUTH_HOST_NAME]
[--noauth_local_webserver]
[--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]
[--logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
ipykernel_launcher.py: error: unrecognized arguments: -f C:\Users\kelvi_000\AppData\Roaming\jupyter\runtime\kernel-942a92b5-f8cf-4ec2-8348-138e4580d562.json
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2
C:\Users\kelvi_000\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py:2918: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
No stack trace, no line numbers, no ipykernel_launcher... just this stupid error that doesn't show anything when googled...
Using python3
update:
I've tracked the problem down to the line:
credentials = tools.run_flow(flow, store)
in the function
get_credentials
Ok, this is totally baffling. The solution is you put the script in it's own python file because that's somehow different.
Then from jupyter notebook, you run
%run [your filename].py
and it works?!
Your browser has been opened to visit:
[censored]
If your browser is on a different machine then exit and re-run this
application with the command-line parameter
--noauth_local_webserver
Authentication successful.
Storing credentials to C:\Users\kelvi_000\Documents\Jupyter Notebooks\.credentials\gmail-python-email-send.json

google python3 api send email example confused

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

Email attachment received as 'noname'

The following Python function results in the attachment being named "noname" when it should be "text_file.txt". As you can see I've tried a 2 different approaches with MIMEBase and MIMEApplication. I've also tried MIMEMultipart('alternative') to no avail.
def send_email(from_addr, to_addr_list,
subject, html_body,plain_text_body,
login,
password,
smtpserver='smtp.gmail.com:587',
cc_addr_list=None,
attachment=None,
from_name=None):
message=MIMEMultipart()
plain=MIMEText(plain_text_body,'plain')
html=MIMEText(html_body,'html')
message.add_header('from',from_name)
message.add_header('to',','.join(to_addr_list))
message.add_header('subject',subject)
if attachment!=None:
#attach_file=MIMEBase('application',"octet-stream")
#attach_file.set_payload(open(attachment,"rb").read())
#Encoders.encode_base64(attach_file)
#f.close()
attach_file=MIMEApplication(open(attachment,"rb").read())
message.add_header('Content-Disposition','attachment; filename="%s"' % attachment)
message.attach(attach_file)
message.attach(plain)
message.attach(html)
server = smtplib.SMTP(smtpserver)
server.starttls()
server.login(login,password)
server.sendmail(from_addr, to_addr_list, message.as_string())
server.quit()
How I'm calling the function:
send_email(
from_addr=from_email,
to_addr_list=["some_address#gmail.com"],
subject=subject,
html_body=html,
plain_text_body=plain,
login=login,
password=password,
from_name=display_name,
attachment="text_file.txt"
)
Your header isn't correct. filename is the attribute not a string.
# Add header to variable with attachment file
attach_file.add_header('Content-Disposition', 'attachment', filename=attachment)
# Then attach to message attachment file
message.attach(attach_file)
Old:
message.add_header('Content-Disposition','attachment; filename="%s"' % attachment)
Update to:
message.add_header('content-disposition', 'attachment',
filename='%s' % 'your_file_name_only.txt' )
I think that this might not be relevant but for those who are interested and getting the same problem :
I am using the google API example too (the one without the attachment) and I realised that the attachment is put only when the text in the subject or the body is NOT a complete string i.e. the string which is being put in the subject or the body is not a single string but an collection of strings.
To explain better :
message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)
This (body = ("Your OTP is", OTP)) may work for print() command, but it doesn't work for this case. You can change this :
message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)
to :
CompleteString = "Your OTP is " + OTP
message = (service.users().messages().send(userId='me', body=body).execute())
body = (CompleteString)
The above lines make the 2 parts of the body into a single string.
Also : The 'noname' file that is put as an attachment contains only the written string. So, if you follow this :
message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)
So all you'll get in the file will be : "Your OTP is "
I am also adding the entire code that I got after modifying the already existing sample code here : https://developers.google.com/gmail/api/quickstart/python
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from email.mime.text import MIMEText
import base64
sender = "sender_mail"
print("Welcome to the Mail Service!")
reciever = input("Please enter whom you want to send the mail to - ")
subject = input("Please write your subject - ")
msg = input("Please enter the main body of your mail - ")
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
message = MIMEText(msg)
message['to'] = reciever
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
body = {'raw' : raw}
message = (service.users().messages().send(userId='me', body=body).execute())
Please also note that this code only works for text that is put through the mail.
P.S. I am using Python 3.8 so the above code might not work for Python 2.

Categories

Resources