I have a list of text files and html files generated by two distinct functions. Each file is labeled signal1.txt, signal2, etc. and signal1.html, signal2.html, etc. I need to send an email with each file pair (signal1.txt and signal1.html, signal2.txt and signal.2.html, and so forth). I've tried several different ways, but I keep getting just one file pair attached (the last file number whatever it is). I have no problem sending one file type, but it gets messy when I try with two different files.
Any help is appreciated. The code is as follows.
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
import smtplib, ssl
import os
dirname = r'C:\Path\To\Files'
ext = ('.txt','html')
for files in os.scandir(dirname):
if files.path.endswith(ext):
def sendmail():
html_body = '''
<html>
<body>
<p style="font-size: 12;"> <strong>Alert</strong><br>{html}</p>
</body>
</html>
'''.format(html=html)
subject = f'Text file content'
senders_email = 'mail#mail.com'
receiver_email = 'mail#mail.com'
# Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
#Attach email body
message.attach(MIMEText(html_body, 'html'))
# Name of the file to be attached
filename = f'signal.html'
# Open file in binary mode
with open(filename, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.mail.com", 25) as server:
# server.starttls(context=context)
# server.login(senders_email, 'password')
server.sendmail(senders_email, receiver_email, text)
print("Email sent!")
sendmail()
I've adapted one of these examples for your problem. This puts all the files in one email:
# Import smtplib for the actual sending function.
import smtplib
# Here are the email package modules we'll need.
from email.message import EmailMessage
import os
dirname = 'C:\Path\To\Files'
ext = ('.txt','html')
msg = EmailMessage()
msg['Subject'] = 'Text file content'
msg['From'] = 'mail#mail.com'
msg['To'] = 'mail#mail.com'
# Open the files in binary mode. You can also omit the subtype
# if you want MIMEImage to guess it.
for filename in os.scandir(dirname):
if filename.path.endswith(ext):
with open(filename, 'rb') as fp:
data = fp.read()
msg.add_attachment(data)
# Send the email via our own SMTP server.
with smtplib.SMTP('localhost') as s:
s.send_message(msg)
Found a way that worked using glob.
dirname = r'C:\Path\To\Files'
regex = create_txt_files(event_dict)
html_file = create_html_files(event_dict)
signalfiles = sorted(list(pathlib.Path(dirname).glob('*.txt')))
htmlfiles = sorted(list(pathlib.Path(dirname).glob('*.html')))
for i, path_html_file in enumerate(htmlfiles):
sendmail(html_file[i], regex[i], path_html_file)
Related
I'm trying to send email that contain both plain text in the body and html attachment. I've succeeded in adding the attachment but can't figure out how to add the plain text to the body. Any help is appreciated.
Here is the code:
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
import smtplib, ssl
from email import encoders
def sendmail():
subject = 'Subject here'
body = "Shard Count: 248" #Need to add this to body in plain text
senders_email = 'title#domain.com'
receiver_email = 'security#domain.com'
#Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
message.attach(MIMEText(html_file, 'html'))
filename = 'logs.html'
# Open file in binary mode
with open(filename, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application','octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.domain.com", 25) as server:
# server.starttls(context=context)
# server.login(senders_email, password)
server.sendmail(senders_email,receiver_email,text)
print("Email sent!")
sendmail()
Your program looks fine for sending email with html body and an attachment. For sending plain text body, you will need to make a minor modification. I have tested your code with html body and plain text body. I am attaching both the versions of the code below.
The code with html body and an attachment is given below. I have tested this code at my end.
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
import smtplib, ssl
from email import encoders
html_body = '''
<html>
<body>
<h1>Sample email header</h1>
<p>Sample body</p>
</body>
</html>
'''
def sendmail():
subject = 'Subject here'
body = "Shard Count: 248" # Need to add this to body in plain text
senders_email = 'amal#example.com'
receiver_email = 'amalgjose#example.com'
# Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
# The message body gets set here. Here we can add html body or text body.
# Syntax - message.attach(MIMEText(content, 'content-type'))
message.attach(MIMEText(html_body, 'html'))
# Name of the file to be attached
filename = 'attachment.pdf'
# Open file in binary mode
with open(filename, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.examplemail.com", 25) as server:
server.starttls(context=context)
server.login(senders_email, 'password')
server.sendmail(senders_email, receiver_email, text)
print("Email sent!")
sendmail()
If you want to send an email with plain text and an attachment, you just need a very minor change in the code. Make the following modification in the code.
text_body = "Hello, this is my text data in email"
message.attach(MIMEText(text_body))
The updated code which sends emails with plain text body and attachment is given below.
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
import smtplib, ssl
from email import encoders
text_body = '''
Hello, this is the sample text body. This program sends email with text body and an attachment.
I hope this helps
'''
def sendmail():
subject = 'Subject here'
body = "Shard Count: 248" # Need to add this to body in plain text
senders_email = 'amal#example.com'
receiver_email = 'amalgjose#example.com'
# Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
# The message body gets set here. Here we can add html body or text body.
# Syntax - message.attach(MIMEText(content, 'content-type'))
message.attach(MIMEText(text_body))
# Name of the file to be attached
filename = 'attachment.pdf'
# Open file in binary mode
with open(filename, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.examplemail.com", 25) as server:
server.starttls(context=context)
server.login(senders_email, 'password')
server.sendmail(senders_email, receiver_email, text)
print("Email sent!")
sendmail()
I'm trying to create an email server to send multiple attached pdfs, I have the code below, it works good sending just one file. But with the for-loop it only sends the last document from 3, help please here maybe I didn't made the for correctly. It uses a csv file with the emails from contacts you can find that file here.
I'll put the for part after the full code...
import email, csv, smtplib, ssl
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
subject = "An email with attachment from Python"
body = """Hi {name_lastname},
How are you?
"This is an email with attachment sent from Python"
Your mail is to Login in the platform is {mail} and your Access Code is {epassword}
Real Python has many great tutorials:
www.realpython.com"""
sender_email = input('enter sender mail address:') # Enter your address
password = input("Type your password and press enter:")
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["Subject"] = subject
#message["Bcc"] = receiver_email # Recommended for mass emails
# Add body to email
message.attach(MIMEText(body, "plain"))
filenames = ["document.pdf", "document2", "document3.pdf"] # In same directory as script
#HERE START THE FOR
# Open PDF file in binary mode
for f in filenames or []:
with open(f, "rb") as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
f"attachment; filename= {f}",
)
#HERE ENDS THE FOR
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender_email, password)
with open("contacts.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name_lastname, mail ,epassword in reader:
server.sendmail(
sender_email,
mail,
text.format(name_lastname=name_lastname, mail=mail, epassword=epassword),
)
Here is the for:
#HERE START THE FOR
# Open PDF file in binary mode
for f in filenames or []:
with open(f, "rb") as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
f"attachment; filename= {f}",
)
#HERE ENDS THE FOR
I have been trying to send a mail via python with a Docx attachment
I'm able to get a mail but with no attachment to it, below is the code I used for attaching a file
And I didn't get any error while attaching a file
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
sender_email = email
receiver_email = toemail
password = password
message = MIMEMultipart("alternative")
message["Subject"] = Subject
message["From"] = sender_email
message["To"] = receiver_email
message.attach(htmlmessage)
attach_file = open(attach_file_name, 'rb') # Open the file as binary mode
payload = MIMEBase('application', 'octet-stream')
payload.set_payload((attach_file).read())
encoders.encode_base64(payload)
#encode the attachment
#add payload header with filename
payload.add_header('Content-Disposition', "attachment; filename= %s" % attach_file)
message.attach(payload)
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender_email, password)
server.sendmail(
sender_email, receiver_email, message.as_string()
)
Mime types are to be respected. The type multipart/alternative should be used for messages containing the same information in plain text and in HTML. The mail reader can then choose which representation it can use.
On the other hand, multipart/mixed should be used when the message contains multiple different parts, said differently to transport attachements.
So, provided htmlmessage is a valid email.mime.text.MIMEText, your code should not declare "alternative":
...
message = MIMEMultipart()
...
Furthermore, you should avoid to directly use MIMEBase and instead rely on the defaults for MIMEApplication:
payload = MIMEApplication(attach_file.read(),
'vnd.openxmlformats-officedocument.wordprocessingml.document')
payload.add_header('Content-Disposition',
"attachment; filename= %s" % attach_file)
message.attach(payload)
But I must acknowledge that this last point is mainly a matter of taste...
The goal is to send an email with excel attachment.
I found an example online but not written for Excel format.
It sends attachment but not like typical excel spreadsheet, so I am unable to open it.
Is it something I can modify in a code in order to receive .xlsx file?
# libraries to be imported
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
fromaddr = "From#gmail.com"
toaddr = "To#gmail.com"
# instance of MIMEMultipart
msg = MIMEMultipart()
# storing the senders email address
msg['From'] = fromaddr
# storing the receivers email address
msg['To'] = toaddr
# storing the subject
msg['Subject'] = "Sending Attachement"
# string to store the body of the mail
body = "Hello, This is Oleg and my attached file"
# attach the body with the msg instance
msg.attach(MIMEText(body, 'plain'))
# open the file to be sent
filename = "FileName"
attachment = open("C:\\Mylocation\\FileName.xlsx", "rb")
# instance of MIMEBase and named as p
p = MIMEBase('application', 'octet-stream')
# To change the payload into encoded form
p.set_payload((attachment).read())
# encode into base64
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
# attach the instance 'p' to instance 'msg'
msg.attach(p)
# creates SMTP session
s = smtplib.SMTP('smtp.gmail.com', 587)
# start TLS for security
s.starttls()
# Authentication
s.login(fromaddr, "password")
# Converts the Multipart msg into a string
text = msg.as_string()
# sending the mail
s.sendmail(fromaddr, toaddr, text)
# terminating the session
s.quit()
There is just a small error in your code! change your filename variable to "FileName.xlsx" instead of just "FileName"
I noticed that your file didn't have an extension, and since your filename variable didn't have an extension - that is how I quickly came to this conclusion. The documentation for add_header() seems to use the file extensions as well.
I need to figure out how to make a script that scans for new files in a directory, and when there is a new one, sends the file via email.
Someone keeps stealing bikes in my apartment building! First it was my fault (I for got to lock it), now the crook upgraded by cutting chains. I had it after the crook stole my second bike by cutting 1/2 inch airplane wire.
Anyway, using a raspberry pi as a motion activated security camera, I want it to send me a video file as soon as the video program finishes recording it. This is incase they steal the pi.
I am looking at these examples, but I can't figure how to make the script run continuously (every minute) or how to make it scan a folder for a new file.
How do I send attachments using SMTP?
OK
I got it down to scanning and then trying to email. It fails when trying to attach the video file. Can you help? Here is the revised code:
The failure is:
msg = MIMEMultipart()
TypeError: 'LazyImporter' object is not callable, line 38
import time, glob
import smtplib
import email.MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders, MIMEMultipart
import os
#Settings:
fromemail= "Jose Garcia <somerandomemail#gmail.com>"
loginname="somerandomemail#gmail.com"
loginpassword="somerandomepassword"
toemail= "Jose Garcia <somerandomemail#gmail.com>"
SMTPserver='smtp.gmail.com'
SMTPort=587
fileslocation="/Users/someone/Desktop/Test/*.mp4"
subject="Security Notification"
def mainloop():
files=glob.glob(fileslocation) #Put whatever path and file format you're using in there.
while 1:
new_files=glob.glob(fileslocation)
if len(new_files)>len(files):
for x in new_files:
if x in files:
print("New file detected "+x)
print("about to call send email")
sendMail(loginname, loginpassword, toemail, fromemail, subject, gethtmlcode(), x, SMTPserver, SMTPort)
files=new_files
time.sleep(1)
def sendMail(login, password, to, frome, subject, text, filee, server, port):
# assert type(to)==list
# assert type(filee)==list
msg = MIMEMultipart()
msg['From'] = frome
msg['To'] = COMMASPACE.join(to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
# #for filee in files:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(filee,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"'
% os.path.basename(filee))
msg.attach(part)
smtp = smtplib.SMTP(SMTPserver, SMTPort)
smtp.sendmail(frome, to, msg.as_string() )
server.set_debuglevel(1)
server.starttls()
server.ehlo()
server.login(login, password)
server.sendmail(frome, to, msg)
server.quit()
def gethtmlcode():
print("about to assemble html")
html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
html +='<body style="font-size:12px;font-family:Verdana"><p>A new video file has been recorded </p>'
html += "</body></html>"
return(html)
#Execute loop
mainloop()
I finally got it working. I had to use python 2.5 to get rid of the LazyImporter error. Every time a new file is added to the security folder, it gets emailed to me. Logging is broken.
import time, glob
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEMultipart import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
import datetime
from email import Encoders
import os
import sys
#Settings:
fromemail= 'RaspberryPI Security Camera <someone>'
loginname='someone#gmail.com'
loginpassword='something'
toemail= 'Jose Garcia < someone#gmail.com>'
SMTPserver='smtp.gmail.com'
SMTPort=587
fileslocation='/Users/someone/Desktop/Test/*.*'
subject="Security Notification"
log='logfile.txt'
def mainloop():
files=glob.glob(fileslocation) #Put whatever path and file format you're using in there.
while 1:
f = open(log, 'w')
sys.stdout = Tee(sys.stdout, f)
new_files=glob.glob(fileslocation)
if len(new_files)>len(files):
for x in new_files:
if x in files:
print(str(datetime.datetime.now()) + "New file detected "+x)
sendMail(loginname, loginpassword, toemail, fromemail, subject, gettext(), x, SMTPserver, SMTPort)
files=new_files
f.close()
time.sleep(1)
def sendMail(login, password, send_to, send_from, subject, text, send_file, server, port):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
part = MIMEBase('application', "octet-stream")
part.set_payload( open(send_file,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(send_file))
msg.attach(part)
smtp = smtplib.SMTP(SMTPserver, SMTPort)
smtp.set_debuglevel(1)
smtp.ehlo()
smtp.starttls()
smtp.login(login, password)
smtp.sendmail(send_from, send_to, msg.as_string() )
smtp.close()
def gettext():
text = "A new file has been added to the security footage folder. \nTime Stamp: "+ str(datetime.datetime.now())
return(text)
class Tee(object):
def __init__(self, *files):
self.files = files
def write(self, obj):
for f in self.files:
f.write(obj)
#Execute loop
mainloop()
It looks like the email module has been refactored over time. This fixed the 'LazyImporter' object not callable error for me on Python 2.7:
from email.mime.text import MIMEText
Noteably it was not happy with (what I thought were) synonyms like import email; email.mime.text.MIMEText(...)
I use python3 and I could not for the life of me get any of these examples to work, but I was able to come up with something that works and is a whole lot simpler.
import smtplib
from email.message import EmailMessage
from email.mime.base import MIMEBase
from email import encoders
# Defining Objects and Importing Files ------------------------------------
# Initializing video object
video_file = MIMEBase('application', "octet-stream")
# Importing video file
video_file.set_payload(open('video.mkv', "rb").read())
# Encoding video for attaching to the email
encoders.encode_base64(video_file)
# creating EmailMessage object
msg = EmailMessage()
# Loading message information ---------------------------------------------
msg['From'] = "person_sending#gmail.com"
msg['To'] = "person_receiving#gmail.com"
msg['Subject'] = 'text for the subject line'
msg.set_content('text that will be in the email body.')
msg.add_attachment(video_file, filename="video.mkv")
# Start SMTP Server and sending the email ---------------------------------
server=smtplib.SMTP_SSL('smtp.gmail.com',465)
server.login("person_sending#gmail.com", "some-clever-password")
server.send_message(msg)
server.quit()
Just put your script in a loop and have it sleep for 60 seconds. You can use glob to get a list of files in the directory. in is pretty useful for seeing what is in a list (i.e. the list of files in the directory).
import time, glob
files=glob.glob("/home/me/Desktop/*.mp4") #Put whatever path and file format you're using in there.
while 1:
new_files=glob.glob("/home/me/Desktop/*.mp4")
if len(new_files)>len(files):
for x in new_files:
if x not in files:
print("New file: "+x) #This is where you would email it. Let me know if you need help figuring that out.
files=new_files
time.sleep(60)