I am trying to run a python script at bootup which will take a ~10 second video on applying an external input (such as push button, IR sensor etc, and in our case an ultrasonic sensor), and then mail this video to specified email addresses using the SMTPlib library of Python.
All of this is working fine. However, when using this input multiple times, the Raspberry Pi sends multiple videos (taken by pushing the button in the past) to the email address instead of the one initiated by the last input. Hence, pushing the button 1 time would send 1 video; pushing it 2 times would send a new one along with the last one; pushing it 3 times would send a new one and the last two as well, and so on.
I even tried inserting an os.remove() right after the mail is sent in the python script. After running the program, running an ls shows the file is indeed deleted. Yet somehow, these deleted videos make its way into the email. Could it be it is stored somewhere in the memory when smtplib is used?
The script in mention is below :
from email.mime.multipart import MIMEMultipart
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.text import MIMEText
emailfrom = "xyz#abc.com"
emailto = "xyz123#abc.com"
username = "xyz#abc.com"
password = "pass"
msg = MIMEMultipart()
msg["From"] = emailfrom
msg["To"] = emailto
msg["Subject"] = "Email Subject -- "
msg.preamble = "Email Body --"
while(True):
d=0
# Possibly the relevant section
d = pulse_d*17150
d= round(d, 2)
if(d<100):
with picamera.PiCamera() as camera:
camera.start_preview()
camera.start_recording('/home/pi/video.h264')
time.sleep(5)
camera.stop_recording()
camera.stop_preview()
time.sleep(5)
fileToSend = "/home/pi/video.h264"
ctype, encoding = mimetypes.guess_type(fileToSend)
if ctype is None or encoding is not None:
GPIO.output(test, True)
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
fp = open(fileToSend, "rb")
GPIO.output(test,False)
attachment = MIMEBase(maintype, subtype)
attachment.set_payload(fp.read())
fp.close()
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename=fileToSend)
msg.attach(attachment)
server = smtplib.SMTP("smtp.gmail.com:587")
server.starttls()
server.login(username,password)
server.sendmail(emailfrom, emailto, msg.as_string())
server.quit()
os.remove("/home/pi/video.h264")
I think you should create new message instead of reuse the old one.
Move the code where you create msg inside while loop
The problem is here:
msg.attach(attachment)
You attach files to the same message, but don't remove old attachments.
Related
This question already has an answer here:
How to send different content email to different recipient in Python?
(1 answer)
Closed 11 months ago.
I want my task to be 1 by 1 not bulk so I'm sending from list of emails. I want it sending to first one then repeats task and sends to second one. The code already works properly but it sends as bulk all emails together so when recipient gets the email it's like all the emails list included in header. That's why I want it to send it one by one from my list.
import smtplib, ssl
from time import strftime
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from colorama import *
from datetime import datetime
context = ssl.create_default_context()
def get_contacts(filename):
emails = []
with open(filename, mode='r', encoding='utf-8') as contacts_file:
for a_contact in contacts_file:
emails.append(a_contact.split()[0])
return emails
mails = get_contacts('smtp/contacts.txt')
smtp_user = 'mYsmtp#smtp.com'
def smtp(smtp_server, port, user, password):
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port) as server:
try:
message = MIMEMultipart('alternative')
message['Subject'] = 'TESTING'
message['From'] = str(Header(f'INFO <{smtp_user}>'))
message['To'] = ', '.join(mails)
myfile = open('smtp/letter.txt', 'r')
data = myfile.read()
part = MIMEText(data, 'html')
message.attach(part)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
server.login(user, password)
server.sendmail(user, mails, message.as_string())
for email in mails:
print('\n',email + ''' SENT! ''')
except smtplib.SMTPAuthenticationError or smtplib.SMTPConnectError :
print('Dead SMTP')
Response:
You just have to move put inside your for email in mails:
for email in mails:
print('\n',email + ''' SENT! ''')
message['To'] = email
server.sendmail(user, mails, message.as_string())
Remove from your original script:
message['To'] = ', '.join(mails)
and
server.sendmail(user, mails, message.as_string())
Notes:
Next time consider undersanding your script before posting your question as this is a very simple thing to do and the answer is already all over the internet.
your try have too many instructions you should only have the instruction that may fail with the handled errors inside it, as the errors that you're handling are smtplib.SMTPAuthenticationError and smtplib.SMTPConnectError: server.login and server.sendmail
Do not post your entire script or big code chunks this is rude and you'll not getting appropriated answers this way.
Your code very explicitly adds all recipients to the To: header and then sends a single message.
As an aside, your code seems to be written for Python 3.5 or earlier. The email library was overhauled in 3.6 and is now quite a bit more versatile and logical. Probably throw away what you have and start over with the examples from the email documentation. Something like this, maybe:
from email.message import EmailMessage
from email.headerregistry import Address
...
mails = get_contacts('smtp/contacts.txt')
smtp_user = 'mYsmtp#smtp.com'
def generate_messages(recipients):
with open('smtp/letter.txt', 'r') as myfile:
data = myfile.read()
for recipient in recipients:
message = EmailMessage()
message['Subject'] = 'TESTING'
message['From'] = Address("INFO", *smtp_user.split("#"))
message['To'] = recipient
message.set_content(data, 'html')
yield message
def smtp(smtp_server, port, user, password, messages):
with smtplib.SMTP_SSL(smtp_server, port) as server:
server.login(user, password)
for message in messages:
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
server.send_message(message)
smtp("smtp.example.com", 25, "me#example.com", "xyzzy", generate_messages(mails))
Untested, and I ripped out the exception handling because I'm probably unable to guess what sort of exception handling would make sense with this different flow.
The use of a generator to generate the messages is perhaps a bit confusing if you are new to Python. I'm thinking you want to minimize the number of separate login events to the SMTP server, and so the code basically has a callback to produce the next message from within the smtp function.
Using the email and smtp libraries, I have developed a script that automatically sends mail via outlook's smtp server.
I enter the content of the mail and after the content is written I want to send the picture at the bottom.
But it sends the picture not at the bottom as I want it, but at the top of the mail content.
Example mail ( This is only image ) : https://ibb.co/d5HFwRG
My code:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
def send_mail(gender, messages, subject):
global msg
try:
msg = MIMEMultipart()
s = smtplib.SMTP(host="SMTP.office365.com", port=587)
s.starttls()
s.login(mail, password)
msg['From'] = mail
msg['To'] = example#outlook.com
msg['Subject'] = messages
msg.attach(MIMEText(message, 'plain', 'utf-8'))
attachment = open("image.jpg", "rb")
p = MIMEBase('application', 'octet-stream')
p.set_payload((attachment).read())
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment; filename= %s" % image)
msg.attach(p)
s.send_message(msg)
del msg
except Exception as e:
print(e)
My code is actually much more complex, but I just showed you the function I use to send mail.
I had to change the names of some variables while adding them here, so the above code may not work for you, you need to edit it.
How can I get that picture sent in the Mail to the bottom?
I was trying to send around 10+ emails (each has an Excel file attached) consecutively by invoking the below Python function send_email(), which is based on the sample code shared by the tutorials on the web.
Somehow I found that though the 10+ emails are sent without errors/exceptions, occasionally some of the emails are not recipeved by the recipients. I checked the Sent box of the specified sender email account, for the emails that weren't received by the recipients, they are not in the sender's Sent box either.
I speculated that it could be due to those consecutive invocations of send_email() overflooded the SMTP server (but not sure) so I added 1 sec delay between the invocation (as specified below). However, the issue still happens occasionally. Now I'm trying to increase the delay from 1sec to 10sec.
Please kindly advise if my speculation is in the right direction and if there is anything I need to do to better pinpoint the root cause. Thanks a lot.
My code is as follows:
(a) Invoking send_email() consecutively in a loop. For each loop assign the corresponding email recipients, subject, message, and Excel attachment as the inputs.
(b) To add delays in between send_email() calls, time.sleep(1) is invoked to add 1 sec delay in the hope of not to overflow the SMTP server.
(c) The code of send_email() is as follows:
#Email the generated Excel file out
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os.path
def send_email(email_recipients, email_subject, email_message, attachment_location = ''):
email_sender = 'xxx' #xxx is the sender's email address
msg = MIMEMultipart()
msg['From'] = email_sender
msg['To'] = ", ".join(email_recipients)
msg['Subject'] = email_subject
msg.attach(MIMEText(email_message, 'plain'))
if attachment_location != '':
filename = os.path.basename(attachment_location)
attachment = open(attachment_location, "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)
try:
mailserver = smtplib.SMTP('smtp.office365.com', 587)
mailserver.ehlo()
mailserver.starttls()
mailserver.login('xx', 'xxxx') # xx and xxxx are the credentials for the smtp server
text = msg.as_string()
mailserver.sendmail(email_sender, email_recipients, text)
print('email sent')
except Exception as e:
print(e)
print("SMPT server connection error")
finally:
mailserver.quit()
return True
When I sent email with an attachment using python code below, although the attachment can be downloaded, it will say size unknown next to the attachment, and if I forward that email, the attachment is not in forwarding email, This seemed to be a problem specific to Thunderbird(60.5.2 (32-bit)), but since our company is entirely on TB, I'll need to fix the python code to make it compatible with TB.
I have played with the raw string of the source from "view source" in TB by comparing the source of a email attachment sent within TB which would work, but to no avail.
PS: found related bug tracker in mozilla for reference: https://bugzilla.mozilla.org/show_bug.cgi?id=548507
import os
import smtplib
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.utils import formataddr
from email.utils import parseaddr
sender = "from#gmail.com"
to_address = ["to#gmail.com"]
subject = "Test subject"
body = "Test body"
title = "title"
attach_files = ['c:\\dummy.pdf']
host = 'localhost.com'
user = 'user#mail.com'
password = 'password'
msg_root = MIMEMultipart('related')
addr = '{} <{}>'.format(title, sender)
name, address = parseaddr(addr)
msg_root['From'] = formataddr((
Header(name, 'utf-8').encode(),
address .encode('utf-8') if isinstance(address , unicode) else address))
msg_root['To'] = ','.join(to_address)
msg_root['Subject'] = Header(subject, 'utf-8')
msg_text = MIMEText(body, 'html', 'utf-8')
msg_root.attach(msg_text)
for index, attach_file in enumerate(attach_files, start=1):
with open(attach_file, 'rb') as attach_obj:
attach = MIMEApplication(attach_obj.read(),
_subtype="pdf",
name=os.path.basename(attach_file))
attach.add_header('Content-Disposition', 'attachment',
filename=os.path.basename(attach_file))
msg_root.attach(attach)
connection = smtplib.SMTP_SSL(host=host, timeout=5)
try:
connection.login(user=user, password=password)
connection.sendmail(user, all_address, msg_root.as_string())
finally:
connection.quit()
I can Accept any answer as long as: when forwarding a email with attachment that's send via Python, the attachment from that email is still included within TB(recent version 60+).
Expected result: a file size next to the attachment, and forwarding the attachment email will also include the attachment.
Use 'mixed' instead of 'related'
msg_root = MIMEMultipart('mixed')
You will see size next to attachement, and attachement will be forwarded.
Tested on TB 60.7.2 (64-bit) / Linux Mint 19.1
Let's say I have a script that checks this one folder to see if there is anything in there that I want.
I'd like to have this script run somewhere in the background (this would be cooler) or execute every 2 hours or so and send me an email if there is anything found.
how can I do this?
So you essentially have two parts:
1) How to set up my a scheduled script?
Assuming you're on Windows you can follow the steps here: Schedule Python Script - Windows 7
to set up a scheduled script either through the gui or the command line.
The output of that script can be boolean in nature and prompt the execution of the second part of your question.
2) How to send an email?
For this you could try this code:
# Import smtplib for the actual sending function
import smtplib
# Import the email modules we'll need
from email.mime.text import MIMEText
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
fp = open(textfile, 'rb')
# Create a text/plain message
msg = MIMEText(fp.read())
fp.close()
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()
More information for this step can be found here: How to send an email with Python?