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.
Related
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!
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)
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))
I'm new to tkinter and trying to create an email gui. So far everything works but after adding a select file functionality to it the program will only work if the user selects a file. I get the error
'GetInfo' object has no attribute 'fname'
Which I'm assuming means that since fname (file name) has no value added to it the program won't continue. I tried to use a try/except thing here:
try:
filename = self.fname
attachment = open(self.fname, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
except:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.ehlo()
server.login(self.frominf.get(), self.passinf.get()) #sender email, password
text = msg.as_string()
server.sendmail(self.frominf.get(), self.toinf.get(), text)
server.quit()
# STOP TIMER
elapsed_time = timer() - start # in seconds
print ("Email took " + (str(elapsed_time)) + " seconds to send, sent to " + self.toinf.get() )
time.sleep(0.5)
root.destroy()
But this pretty much did the opposite, giving an error if I did supply a file.
So my question is: how can I let the user decide if they want to supply a file or not?
Here is my whole code:
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter import ttk
import time
import datetime
import smtplib
from timeit import default_timer as timer
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
#colors
darkred = (139, 0, 0)
grey = (90, 90, 90)
root = Tk()
root.title("Email")
class GetInfo:
def __init__(self, master):
#window size
master.minsize(width=250, height=75)
#senders email
Label(root, text="Your email").pack(fill=X)
self.frominf = Entry(master)
self.frominf.pack(fill=X)
#sending to email
Label(root, text="Sending to").pack(fill=X)
self.toinf = Entry(root)
self.toinf.pack(fill=X)
#subject line
Label(root, text="Subject").pack(fill=X)
self.subinf = Entry(root)
self.subinf.pack(fill=X)
#add file
Label(root, text="File?").pack(fill=X)
file_button = Button(root, text="Click to browse files", command= self.Load_file, height = 1, bg="grey")
file_button.pack(fill=X)
#senders password
Label(root, text="Your password").pack(fill=X)
self.passinf = Entry(master, show="*")
self.passinf.pack(fill=X)
#body
Label(root, text="Body of email").pack(fill=X)
self.bodyinf = Text(root)
self.bodyinf.pack(fill=X)
#submit button
submit_button = Button(root, text="Submit(click when done)", command=self.send_email, height = 2, bg="darkred")
submit_button.pack(fill=X)
def Load_file(self):
self.fname = askopenfilename(filetypes=(("All Files","*.*"),
("HTML files", "*.html;*.htm"),
("Template files", "*.tplate") ))
def send_email(self):
# START TIMER
start = timer()
msg = MIMEMultipart()
msg['From'] = self.frominf.get()
msg['To'] = self.toinf.get()
msg['Subject'] = self.subinf.get()
body = self.bodyinf.get('1.0', END)
msg.attach(MIMEText(body, 'plain'))
filename = self.fname
attachment = open(self.fname, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.ehlo()
server.login(self.frominf.get(), self.passinf.get()) #sender email, password
text = msg.as_string()
server.sendmail(self.frominf.get(), self.toinf.get(), text)
server.quit()
# STOP TIMER
elapsed_time = timer() - start # in seconds
print ("Email took " + (str(elapsed_time)) + " seconds to send, sent to " + self.toinf.get() )
time.sleep(0.5)
root.destroy()
app = GetInfo(root)
root.mainloop()
Use this try and catch. If the filename exist, the try part code will be executed. If there's no file selected, it will go to the catch part and will do nothing, just continue with the code without showing any error.
try:
filename = self.fname
attachment = open(self.fname, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
except:
pass
If I understood the question correctly, You can check whether self.fname exists. One way to do it:
if self.fname:
attachment = open(self.fname, "rb")
Of course, You need to check later if You have attachment, to avoid another bug. So, further:
if self.fname:
attachment = open(self.fname, "rb")
# This part of code also should go under condition check
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
I would add the self.fname attribute at the start of the program and set it as:
self.fname = ""
Then I would change this line:
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
To an if statement like this:
if self.fname != "":
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
else:
part.add_header('Content-Disposition')
This should prevent the error when a file is not selected.
I cannot test this right now but should be functional.
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 ============"