I created a function in python 3.7 to create draft emails with attached spreadsheets for all the clients.
The code creates that draft email correctly and attaches the files (.xlsx) but these files can no more be opened on gmail. Not sure if this is specific to the encoding that I am using.
(The files can be opened successfully on my desktop computer)
Below is the code:
def CreateMessageWithAttachment(sender, to, cc, subject, message_text, file_dir, filename):
"""Create email messages with attachment, for sending to partners"""
message = MIMEMultipart('alternative')
message['to'] = to
message['from'] = sender
message['cc'] = cc
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)
# main_type, sub_type = 'gzip', None
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)
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
return {'raw': raw}
#return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
I am assuming, the encoding of the files is what corrupting the spreadsheets attached. Any help would be appreciated.
Try changing these two lines:
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
To just this:
msg = MIMEApplication(fp.read(), sub_type)
Related
import imaplib
import email
from email.header import decode_header
import webbrowser
import os
# account credentials
username = "example#stack.com"
password = "exapleforstack"
imap_server = "imap.one.com"
def clean(text):
# clean text for creating a folder
return "".join(c if c.isalnum() else "_" for c in text)
# create an IMAP4 class with SSL
imap = imaplib.IMAP4_SSL(imap_server)
# authenticate
imap.login(username, password)
status, messages = imap.select("INBOX")
imap.search(None, 'SUBJECT', '"exampleforstack"')
# number of top emails to fetch
N = 3
# total number of emails
messages = int(messages[0])
for i in range(messages, messages-N, -1):
# fetch the email message by ID
res, msg = imap.fetch(str(i), "(RFC822)")
for response in msg:
if isinstance(response, tuple):
# parse a bytes email into a message object
msg = email.message_from_bytes(response[1])
# decode the email subject
subject, encoding = decode_header(msg["Subject"])[0]
if isinstance(subject, bytes):
# if it's a bytes, decode to str
subject = subject.decode(encoding)
# decode email sender
From, encoding = decode_header(msg.get("From"))[0]
if isinstance(From, bytes):
From = From.decode(encoding)
print("Subject:", subject)
print("From:", From)
# if the email message is multipart
if msg.is_multipart():
# iterate over email parts
for part in msg.walk():
# extract content type of email
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
try:
# get the email body
body = part.get_payload(decode=True).decode()
except:
pass
if content_type == "text/plain" and "attachment" not in content_disposition:
# print text/plain emails and skip attachments
print(body)
elif "attachment" in content_disposition:
# download attachment
filename = part.get_filename()
if filename:
folder_name = clean(subject)
if not os.path.isdir(folder_name):
# make a folder for this email (named after the subject)
os.mkdir(folder_name)
filepath = os.path.join(folder_name, filename)
# download attachment and save it
open(filepath, "wb").write(part.get_payload(decode=True))
else:
# extract content type of email
content_type = msg.get_content_type()
# get the email body
body = msg.get_payload(decode=True).decode()
if content_type == "text/plain":
# print only text email parts
print(body)
if content_type == "text/html":
# if it's HTML, create a new HTML file and open it in browser
folder_name = clean(subject)
if not os.path.isdir(folder_name):
# make a folder for this email (named after the subject)
os.mkdir(folder_name)
filename = "index.html"
filepath = os.path.join(folder_name, filename)
# write the file
open(filepath, "w").write(body)
# open in the default browser
webbrowser.open(filepath)
print("="*100)
# close the connection and logout
imap.close()
imap.logout()
I tried using the search method a couple different ways, like:
res, msg = imap.search(None, 'SUBJECT', "example")
and
res, msg = imap.search(None, 'SUBJECT, "example"')
but my code just gives an error and automatically fetches the most recent 3.
I've tried replacing the line:
res, msg = imap.fetch(str(i), "(RFC822)")
with
res, msg = imap.search(None, 'SUBJECT', '"example"')
but the program returns nothing at all.
how would I go about implementing the search I got this code from pythoncode and altered its credentials.
But I'm not sure why I can't implement the search function.
I am receiving a "file not found error" when attempting to attach a csv to an email from tempfile. My end goal is to convert json to csv, attach it to an email, and send via lambda. I can see the files are being created and converted on my local machine but when attempting to attach the csv it errors out at
att = MIMEApplication(open(file_name, 'rb').read())
FileNotFoundError: [Errno 2] No such file or directory: 'C:\Users\CHRIST~1\AppData\Local\Temp\tmpfkrfwzqo\temp.json\sso.csv'
def sso():
tempdir = tempfile.mkdtemp()
path = os.path.join(tempdir, 'temp.json')
with open(path, 'w') as fp:
json.dump(testjson, fp)
# Changes the data to CSV
def json_to_csv(path, fileInput, fileOutput):
data = json.load(open(os.path.join(path, fileInput)))
with open(os.path.join(path, fileOutput), 'w') as fp:
output = csv.writer(fp)
output.writerow(data[0].keys())
for row in data:
output.writerow(row.values())
json_to_csv(tempdir, 'temp.json', 'sso.csv')
# Sends the email
SENDER = "name <no-reply#name.com>"
sender = "name <no-reply#cname.com>"
# recipients = [" Partners <partners#name.com>"]
recipients = ["Test Name <name#cname.com>"]
RECIPIENT = ", ".join(recipients)
# SMTP
USERNAME_SMTP = secret_ses['username']
PASSWORD_SMTP = secret_ses['password']
HOST = "email-smtp.us-east-1.amazonaws.com"
PORT = 465
# Boto3 SES
AWS_REGION = "us-east-1"
SUBJECT = "All SSO Apps"
BODY_TEXT = (f"Please see attached.")
BODY_HTML = f""" <html>Some words</html>"""
CHARSET = "UTF-8"
# SMTP
msg = MIMEMultipart('alternative')
msg['Subject'] = SUBJECT
msg['From'] = SENDER
msg['To'] = RECIPIENT
part1 = MIMEText(BODY_TEXT, 'plain')
part2 = MIMEText(BODY_HTML, 'html')
msg.attach(part1)
msg.attach(part2)
file_name = "".join([path, "\sso.csv"])
# Define the attachment part and encode it using MIMEApplication.
att = MIMEApplication(open(file_name, 'rb').read())
att.add_header('Content-Disposition', 'attachment', filename=file_name)
if os.path.exists(file_name):
print("File exists")
else:
print("File does not exists")
# Attach the multipart/alternative child container to the multipart/mixed
# parent container.
msg.attach(msg)
# Add the attachment to the parent container.
msg.attach(att)
try:
with SMTP_SSL(HOST, PORT) as server:
server.login(USERNAME_SMTP, PASSWORD_SMTP)
server.sendmail(SENDER, RECIPIENT, msg.as_string())
server.close()
except SMTPException as e:
print("Error: ", e)
json_to_csv(tempdir, 'temp.json', 'sso.csv')
sso()
So as I had mentioned, the error occurs with att = MIMEApplication(open(file_name, 'rb').read()). When I try to pass this along instead file_name = path, it returns "file exists" but I then receive a very long recursion error: "RecursionError: maximum recursion depth exceeded while calling a Python object" along several errors coming before it. I'd be so grateful for any help with this.
This code runs and does download the attachments as expected.
That said, It writes all attachments to the directory on my PC. I only want to include an attachment with a .html file type. Anything else should be omitted.
Secondly, I would like it to only import the .html attachment if the subject line = "Whatever" if this is true then I would like it to save to a specific subfolder in the PC directory. So for example: if subject = "Whatever1" then save to c:\Desktop\Folder\Subfolder1, if subject = "Whatever2" then save to c:\Desktop\Folder\Subfolder2 etc
import email, imaplib, os
#Credentials
username = 'myusername'
password = 'mypassword'
#SaveTo Directory on PC
attachment_dir = "C:/Desktop\Folder\Subfolder"
#Functions
def get_body(msg):
if msg.is_multipart():
return get_body(msg.get_payload(0))
else:
return msg.get_payload(None,True)
def get_attachments(msg):
for part in msg.walk():
if part.get_content_maintype()=='multipart':
continue
if part.get('Content-disposition') is None:
continue
filename = part.get_filename()
if bool(filename) :
filepath =os.path.join(attachment_dir, filename)
with open(filepath,'wb')as f:
f.write(part.get_payload(decode=True))
def search(key,value,mail):
result, data = mail.search(none,key,'"()"'.format(value))
return data
def get_emails(result_bytes):
msgs = []
for num in result_bytes[0].split():
typ,data = mail.fetch(num,'(RCF822)')
msgs.append(data)
return msgs
#Create Connection
mail = imaplib.IMAP4_SSL("imap.gmail.com")
mail.login(username, password)
#Which Gmail Folder to Select?
mail.select("Inbox")
result, data = mail.fetch(b'12','(RFC822)')
raw = email.message_from_bytes(data[0][1])
get_attachments(raw)
from imap_tools import MailBox
# get all attachments from INBOX and save them to files
with MailBox('imap.my.ru').login('acc', 'pwd', 'INBOX') as mailbox:
for msg in mailbox.fetch():
print(msg.subject)
for att in msg.attachments:
print('-', att.filename, att.content_type)
with open('C:/1/{}'.format(att.filename), 'wb') as f:
f.write(att.payload)
lib: https://github.com/ikvk/imap_tools
for mList in range(numMessages):
for msg in msg.startswith("Subject:")
if msg.startswith('Subject'):
print(msg)
break
This should parse the subject line, so you can distinguish by whatever you want the subject to be.
I have the code below that works perfectly fine when I use it to send a txt file, an image or audio. However, it doesn't work when I try to send zip files, rar files, or any other file that doesn't have its own MIME (which is not related to MIMEText, MIMEImage or MIMEAudio).
In conclusion, whenever I reach the else part (the MIMEBase command) I do something wrong and get the error:
e.send_mail(TARGET, SUBJECT, "file.zip")
msg.attach(part) //two lines after the else's end
AttributeError: 'str' object has no attribute 'append'
the code:
def send_mail(self, target, subject, *file_names):
"""
send a mail with files to the target
#param target: send the mail to the target
#param subject: mail's subject
#param file_names= list of files to send
"""
msg = email.MIMEMultipart.MIMEMultipart()
msg['From'] = self.mail
msg['To'] = email.Utils.COMMASPACE.join(target)
msg['Subject'] = subject
for file_name in file_names:
f = open(file_name, 'rb')
ctype, encoding = mimetypes.guess_type(file_name)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
# in case of a text file
if maintype == 'text':
part = MIMEText(f.read(), _subtype=subtype)
# in case of an image file
elif maintype == 'image':
part = MIMEImage(f.read(), _subtype=subtype)
# in case of an audio file
elif maintype == 'audio':
part = MIMEAudio(f.read(), _subtype=subtype)
# any other file
else:
part = MIMEBase(maintype, subtype)
msg.set_payload(f.read())
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file_name))
msg.attach(part)
f.close()
# ssl server doesn't support or need tls, so don't call server_ssl.starttls()
self.server_ssl.sendmail(self.mail, target, msg.as_string())
#server_ssl.quit()
self.server_ssl.close()
I have seen similar codes but I don't understand what is wrong with mine.
could you please explain me what I am messing up?
thank you!
if it helps anyone here is the answer:
the main problem was that I changed the msg payload instead of the zip file's
def send_mail(self, target, subject, body, *file_names):
"""
send a mail with files to the target
#param target: send the mail to the target
#param subject: mail's subject
#param file_names= list of files to send
"""
msg = MIMEMultipart()
msg['From'] = self.mail
msg['To'] = target
msg['Subject'] = subject
body_part = MIMEText(body, 'plain')
msg.attach(body_part)
for file_name in file_names:
f = open(file_name, 'rb')
ctype, encoding = mimetypes.guess_type(file_name)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
# in case of a text file
if maintype == 'text':
part = MIMEText(f.read(), _subtype=subtype)
# in case of an image file
elif maintype == 'image':
part = MIMEImage(f.read(), _subtype=subtype)
# in case of an audio file
elif maintype == 'audio':
part = MIMEAudio(f.read(), _subtype=subtype)
# any other file
else:
part = MIMEBase(maintype, subtype)
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file_name))
msg.attach(part)
f.close()
# ssl server doesn't support or need tls, so don't call server_ssl.starttls()
self.server_ssl.sendmail(self.mail, target, msg.as_string())
self.server_ssl.quit()
I m new to python and i was trying to send attached email through Gmail and the path of attachment i'm giving unable to locate Python.
class Email(object):
def __init__(self, from_, to, subject, message, message_type='plain',
attachments=None, cc=None, message_encoding='us-ascii'):
self.email = MIMEMultipart()
self.email['From'] = from_
self.email['To'] = to
self.email['Subject'] = subject
if cc is not None:
self.email['Cc'] = cc
text = MIMEText(message, message_type, message_encoding)
self.email.attach(text)
if attachments is not None:
mimetype, encoding = guess_type(attachments)
if mimetype ==None:
mimetype = "text/html"
mimetype = mimetype.split('/', 1)
with open(os.path.join(attachments, 'TestReport.html')) as f:
attachment = MIMEBase(mimetype[0], mimetype[1])
attachment.set_payload(f.read())
f.close()
encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment',
filename=os.path.basename(attachment))
self.email.attach(attachment)
Below code is the file I'm executing and throwing an error with open(os.path.join(attachments, 'TestReport.html')) as f:
IOError: [Errno 2] No such file or directory: 'C:/RelianceJio/wallet-fp-automation/email_utils\TestReport.html'
from email_utils.Email import EmailConnection, Email
config = ConfigParser.ConfigParser()
config.read(getcwd() + "/configuration.ini")
print 'I need some information...'
name = 'Lokesh Reddy'
email = 'lokeshreddyqa#gmail.com'
password = '***'
mail_server = 'smtp.gmail.com'
to_email = 'lokeshreddz#gmail.com'
to_name = 'Lokesh Reddy'
subject = 'Sending mail easily with Python'
message = 'here is the message body'
#os.chdir(getcwd() + "\ TestReport.html")
MYDIR = os.path.dirname(__file__)
attachments =MYDIR
print 'Connecting to server...'
server = EmailConnection(mail_server, email, password)
print 'Preparing the email...'
email = Email(from_='"%s" <%s>' % (name, email), # you can pass only email
to='"%s" <%s>' % (to_name, to_email), # you can pass only email
subject=subject, message=message, attachments=attachments)
print 'Sending...'
server.send(email)
print 'Disconnecting...'
server.close()
print 'Done!'
You are trying to attach TestReport.html present in the floder 'C:/RelianceJio/wallet-fp-automation/email_utils\'. Please check whether the file is present in the folder specified. If not make sure that the file is present in that folder or else pass the correct attachment folder.
Try:
C:\\RelianceJio\\wallet-fp-automation\\email_utils\\TestReport.html
You gave "\" instead of "\".
Your directory:
C:/RelianceJio/wallet-fp-automation/email_utils**\**TestReport.html