Create a .eml file in Python [duplicate] - python

I am trying to generate emails using the standard email library and save them as .eml files.
I must not be understanding how email.generator works because I keep getting the error 'AttributeError: 'str' object has no attribute 'write.'
from email import generator
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
active_dir = 'c:\\'
class Gen_Emails(object):
def __init__(self):
self.EmailGen()
def EmailGen(self):
sender = 'sender'
recepiant = 'recipiant'
subject = 'subject'
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = recepiant
html = """\
<html>
<head></head>
<body>
<p> hello world </p>
</body>
</html>
"""
part = MIMEText(html, 'html')
msg.attach(part)
self.SaveToFile(msg)
def SaveToFile(self,msg):
out_file = active_dir
gen = generator.Generator(out_file)
gen.flatten(msg)
Any ideas?

You are supposed to pass an open file (in write mode) to Generator(). Currently you pass it just a string, which is why it fails when it tries to call .write() on the string.
So do something like this:
import os
cwd = os.getcwd()
outfile_name = os.path.join(cwd, 'message.eml')
class Gen_Emails(object):
# ...
def SaveToFile(self,msg):
with open(outfile_name, 'w') as outfile:
gen = generator.Generator(outfile)
gen.flatten(msg)
Note: with open(outfile_name, 'w') as outfile opens the file at the path outfile_name in write mode and assigns the file pointer to the open file to outfile. The context manager also takes care of closing the file for you after you exit the with block.
os.path.join() will join paths in a cross-plattform way, which is why you should prefer it over concatenating paths by hand.
os.getcwd() will return your current working directory. If you want to your file to be saved somewhere else just change it out accordingly.

Here is a modified solution that works with extra headers too. (This was tested with Python 2.6)
import os
from email import generator
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
html_data = ...
msg = MIMEMultipart('alternative')
msg['Subject'] = ...
msg['From'] = ...
msg['To'] = ...
msg['Cc'] = ...
msg['Bcc'] = ...
headers = ... dict of header key / value pairs ...
for key in headers:
value = headers[key]
if value and not isinstance(value, basestring):
value = str(value)
msg[key] = value
part = MIMEText(html_data, 'html')
msg.attach(part)
outfile_name = os.path.join("/", "temp", "email_sample.eml")
with open(outfile_name, 'w') as outfile:
gen = generator.Generator(outfile)
gen.flatten(msg)
print "=========== DONE ============"

Related

Python extract a specific attachment from email

I am trying to achieve the following:
I have an email object (eml) which can contain multiple attachments like zip/images/txt etc.
I want to download only a specific attachment and not all the attachments from this object.
I have tried the following code:
import email
import mimetypes
import uuid
import os
m = email.message_from_file(open('hello.eml'))
for part in m.walk():
filename = part.get_filename()
print('fileName: ', filename)
if part.get_content_type() == 'multipart/mixed' and not filename:
number_of_attachments = (len(part.get_payload()) - 1)
print('number_of_attachments: ', number_of_attachments)
for attachment in range(number_of_attachments):
tmp_name = str(uuid.uuid4())
fp = open('.' + tmp_name, 'wb')
attachment += 1
fp.write(part.get_payload()[attachment].get_payload(decode=True))
fp.close()
This extracts all the attachments.
Is there a way to filter only specific attachment and process that.
Any help is much appreciated!
So, I found the following way.
import email
import mimetypes
import uuid
import os
m = email.message_from_file(open('hello.eml'))
for part in m.walk():
filename = part.get_filename()
print('fileName: ', filename)
if part.get_content_type() == 'multipart/mixed' and not filename:
number_of_attachments = (len(part.get_payload()) - 1)
print('number_of_attachments: ', number_of_attachments)
for attachment in range(number_of_attachments):
filteredFile = m.get_payload()[attachment]
if filteredFile.get_content_type() == 'text/html':
print("Search successful.")
else:
print("Search unsuccessful.")
Thanks all for your inputs!

How can I get an attached eml file from email message content using Python?

I am using python 3.7 and the email, imap library to read email and extract the content of email and attachments , all the attachment ( like excel, csv, pdf) is downloading as attachment but when i received any .eml file in email , it shows me error, please find the below code to read email content and attachment with error showing in case of eml file is received as attachment.
it is showing error at the time of writing eml file.
at the time of write part.get_payload(decode=True) is coming blank in eml file case.
filename = part.get_filename()
if filename is not None:
dot_position = filename.find('.')
file_prefix = filename[0:dot_position]
file_suffix = filename[dot_position:len(filename)]
# print(dot_position)
# print(file_prefix)
# print(file_suffix)
now = datetime.datetime.now()
timestamp = str(now.strftime("%Y%m%d%H%M%S%f"))
newFileName = file_prefix + "_" + timestamp + file_suffix
sv_path = os.path.join(svdir, newFileName)
# allfiles = allfiles.append([{"oldfilename": filename, "newfilename": newFileName}])
mydict = filename + '$$' + newFileName
mydict1 = mydict1 + ',' + mydict
print(mydict1)
if not os.path.isfile(sv_path):
print("oldpath:---->" + sv_path)
# filename = os.rename(filename, filename + '_Rahul')
# sv_path = os.path.join(svdir, filename)
# print("Newpath:---->" + sv_path)
fp = open(sv_path, 'wb')
# print("Rahul")
print(part.get_payload(decode=True))
# try:
# newFileByteArray = bytearray(fp)
# if part.get_payload(decode=True) is not None:
fp.write(part.get_payload(decode=True))
# except (TypeError, IOError):
# pass
fp.close()
Error is
<class 'TypeError'> ReadEmailUsingIMAP.py 129
a bytes-like object is required, not 'NoneType'
Just to explain why this is happening (it hit me too), quoting the v. 3.5 library doc. (v2 says the same):
If the message is a multipart and the decode flag is True, then None is returned.
If your attachment is an .EML, it's almost always going to be multi-part, thus the None.
Jin Thakur's workaround is appropriate if you're only expecting .EML multipart attachments (not sure if there is any other use cases); it should have been accepted as an answer.
Use eml_parser
https://pypi.org/project/eml-parser/
import datetime
import json
import eml_parser
def json_serial(obj):
if isinstance(obj, datetime.datetime):
serial = obj.isoformat()
return serial
with open('sample.eml', 'rb') as fhdl:
raw_email = fhdl.read()
parsed_eml = eml_parser.eml_parser.decode_email_b(raw_email)
print(json.dumps(parsed_eml, default=json_serial))

How to edit .eml file generated with email.generator in Outlook?

I have a code, that generate me .eml message file which I open in Outlook. But the message file is uneditable (can't edit this file). E.g.: I want to add new sender or recepiant, but I can't do that.
Does anybody know, how fix this problem, or may be there is another way to create message file?
import email
from email import generator
from email.mime.text import MIMEText
from email.encoders import encode_base64
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
import json
import SQL_from_DB
import os
import re
def create_mail(attach_file_name, message_json, mail_template_path, save_path, database, db_username, db_password, driver):
file_name_mail_template = mail_template_path + re.sub("QWE:\[.*?\]\s+","",message_json['message']) + '.txt'
try:
mail_template_file = open(file_name_mail_template.encode('utf-8'), 'r', encoding="utf-8")
except:
print("Ошибка открытия шаблона пиьсма \"" + re.sub("QWE:\[.*?\]\s+","",message_json['message']) + ".txt\"")
sys.exit()
mail_template_text = mail_template_file.read()
for var, value in message_json.items():
mail_template_text = mail_template_text.replace('${' + str(var) + '}', str(value))
# print(mail_template_text)
msg = MIMEMultipart()
msg['Subject'] = message_json['message']
msg['From'] = 'qwe#qwe.qwe'
msg['To'] = SQL_from_DB.SQL_select(database, db_username, db_password, driver, message_json['DOMAINNAME'])
#add attachment
attach_file = open(attach_file_name.encode('utf-8'), 'rb')
attachment = MIMEBase("application", "msword")
#attachment = MIMEBase("application", "pdf")
attachment.set_payload(attach_file.read())
attach_file.close()
encode_base64(attachment)
attachment.add_header('Content-Disposition','attachment',filename='Events' + ".zip")
msg.attach(attachment)
#текст письма
msg.attach(MIMEText(mail_template_text, 'html'))
#save message file
with open(save_path.encode("utf-8"), 'w') as out:
gen = email.generator.Generator(out)
gen.flatten(msg)
return(save_path)
To generate an editable Outlook file, just need generate not .eml but .emltpl (Outlook message template file).
#save message file
with open('/save_path/message.emltpl', 'w') as out:
gen = email.generator.Generator(out)
gen.flatten(msg)
return(save_path)

Python 3 sending congratulations to customer

How I may send birthday congratulations with attached jpg file to our cutomers by email?
I write script, he show notifications:
import time
import os
birthdayFile = 'birthdays.csv'
def checkTodaysBirthdays():
fileName = open(birthdayFile, 'r')
today = time.strftime('%m%d')
flag = 0
for line in fileName:
if today in line:
line = line.split(' ')
flag =1
os.system('notify-send "Birthdays Today: ' + line[1]
+ ' ' + line[2] + '"')
if flag == 0:
os.system('notify-send "No Birthdays Today!')
But I dont know, how send congrats by email.
File birthdays.csv contain next row: date, name, email
You can use smtplib. Below is an implementation of the simple notification system using a class, to utilize clean context-manager behavor:
import datetime
import smtplib, csv
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
class SendEmail:
def __init__(self, _to, _from = 'youremail#domaim.com', subject = 'Happy Birthday!', attachment = 'birthday.jpg'):
self._to = _to
self._from = _from
self._subject = subject
self.attachment = attachment
def __enter__(self):
msg = MIMEMultipart()
for part in ['Subject', 'From', 'To']:
msg[part] = getattr(self, f'_{i.lower()}')
part = MIMEBase('application', "octet-stream")
part.set_payload(open(self.attachment, "rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename="{self.attachment}"')
msg.attach(part)
server = smtplib.SMTP(self.EMAIL_SERVER)
server.sendmail(self.EMAIL_FROM, self.EMAIL_TO, msg.as_string())
return self
def __exit__(self, *args):
pass
#classmethod
def send_bulk(cls, users:list):
for date, name, email in users:
_d = datetime.datetime.now()
if _d.month in date and _d.day in date:
with cls(email, subject = f'Happy Birthday, {name}!') as f:
pass
with open('filename.csv') as f:
SendEmail.send_bulk(csv.reader(f))

How to add multiple embed images in an email using python

from email.MIMEMultipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
from datetime import datetime
import socket
import email
import smtplib
import uuid
class EmailSender:
def __init__(self):
self.msgRoot = MIMEMultipart('related')
self.msgAlternative = MIMEMultipart('alternative')
self.msgRoot.attach(self.msgAlternative)
def AddImage(self, fileName, title):
internalFileName = '%s-%s-%s' %(fileName, datetime.now().strftime('%Y%m%d%H%M%S'), uuid.uuid4())
mimetext ='<p style="background-color:lightgrey;font-size:20px;font-weight:bold;font-family:Comic Sans MS">%s</p><br><img src="cid:%s"><br>' %(title, internalFileName)
msgText = MIMEText(mimetext, 'html')
self.msgAlternative.attach(msgText)
fp = open(fileName, 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<%s>' %(internalFileName))
self.msgRoot.attach(msgImage)
def AddFile(self, fileName):
fg = open(fileName, 'rb')
fA = MIMEApplication(fg.read(), Name=fileName)
fA['Content-Disposition'] = 'attachment; filename="%s"' %fileName
self.msgRoot.attach(fA)
This is my code of adding a image to the result email AddImage(). But when I invoke AddImage() for more than once, only the first image is shown in the result email?
Can you help find the problem?
The solution turns out to be:
def AddImage(self, fileName, title):
internalFileName = '%s-%s-%s' %(fileName, datetime.now().strftime('%Y%m%d%H%M%S'), uuid.uuid4())
self.imgHtml +='<p style="font-size:15px;font-weight:bold;font-family:Comic Sans MS">%s</p><br><img src="cid:%s"><br>' %(title, internalFileName)
fp = open(fileName, 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<%s>' %(internalFileName))
self.msgRoot.attach(msgImage)
def Send(self, toList):
msgText = MIMEText(self.imgHtml, 'html')
self.msgAlternative.attach(msgText)
self.msgRoot['Subject'] = 'Audience Ingestion Integrated Test Report #%s [%s]' %(datetime.now().strftime('%Y-%m-%d'), socket.gethostname())
strFrom = 'notifier#freewheel.tv'
self.msgRoot['From'] = strFrom
strTo = email.Utils.COMMASPACE.join(toList)
self.msgRoot['To'] = strTo
smtp = smtplib.SMTP('smtp1.dev.fwmrm.net', 25)
smtp.sendmail(strFrom, strTo, self.msgRoot.as_string())
smtp.quit()
which means that when AddImage(), just attach the MIMEImage to the MIMEMultipart and add the string to the html string, and when Send() after several invocations of AddImage(), attach the MIMEText generated from the html string to MIMEMultipart.
This code works but i am also trying to add text with each image but idk how to do that :
home = str(Path.home())
path = home + "\\Pictures"
list_of_images = glob( os.path.join(path, "*.png") )
mail = MIMEMultipart("related")
#Jinja2 for html template
main = Template('''
<html><body>
<h2>This is a Test email for python script</h2>
<br />
{% for image in pictures %}<p> $(name) </p><br /><img src="cid:{{image}}"><br />{% endfor %}
</body></html>''')
for filename in list_of_images:
fp = open(filename, 'rb')
msg_img = MIMEImage(fp.read())
fp.close()
msg_img.add_header('Content-ID', '<{}>'.format(filename))
msg_img.add_header('Content-Disposition', 'inline', filename=filename)
mail.attach(msg_img)
mail['Subject'] = "Python Script Test | You will receive this email every 30 minutes"
mail['From'] = me
mail['To'] = you
This was helpful for me.
What I found out that helped, after I have included (looped over) all the <img src="cid:%s"> use msgAlternative.as_string() and that fixed the issue. The issue being how it only included the first image now matter how many images I included.

Categories

Resources