smtplib multiple attachments failed to be received - python

I wrote a code that create a PDF, and I wanted to send it with another file (still a .pdf) with a python code based on SMTPLIB library.
You can see str(names[i]) value for the receiver email, since is taken from a table and also the sending-process is managed with a for cycle, were the name of the just-created pdf is depending on the str(names[i]) value.
I'm trying to manage the following code, considering a two factor authentication in order to send the automated email via python, from a gmail-based email:
sender_email = "sender#gmail.com"
receiver_email = str(names[i])
password = input("Authentication code: ")
subject = "Title"
body = """Hi,
This is the body of the email
"""
attachments = ['file1'+str(names[i])+'.pdf', 'file2.pdf'] # list of attachments
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Bcc"] = receiver_email # For mass emails
# Add body to email
message.attach(MIMEText(body, "plain"))
if 'attachments' in globals() and len('attachments') > 0:
for filename in attachments:
f = filename
part = MIMEBase('application', "octet-stream")
part.set_payload( open(f,"rb").read() )
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
message.attach(part)
# Add header as key/value pair to attachment part
part.add_header("Content-Disposition",f"attachment; filename= {attachments}",)
# 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)
server.sendmail(sender_email, receiver_email, text)
Everything works just fine: PDF is created, the mail is sent and received but.... the attachments are not ok in non-gmail emails.
What I find in the attachment list in a Outlook mail are files (with no extention) called ['file1'+str(names[i])+'.pdf', 'file2.pdf'], and trying with different receivers gives the same result.
It seems like non-gmail servers do not load files in the proper manner, while the gmail server recognizes the overall process
I thought about writing a "multiserver" object in the last with condition, but I don't know how to do it.
Thank you all!

In simplified code you do this:
# block#1 The following is simplified ---
for filename in attachments:
part = ... # construct part, include file content
part.add_header('Content-Disposition', ...)
message.attach(part)
# block#2: The following is original code ----
# Add header as key/value pair to attachment part
part.add_header("Content-Disposition",f"attachment; filename= {attachments}",)
# Add attachment to message and convert message to string
message.attach(part)
Thus, you
in block#1 you first create a part for each file
in block#2 you then take the last part from block#1 and add another content-disposition header, with including the name list as name
in block#2 you then attach this part again to this message
Obviously, block#1 is all you need and block#2 just messes up everything. Remove it.

Related

Problem with For to attach multiples pdfs Email Server Google Colab

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

How to send a complete email using smtplib python

I am trying to send an email using Python smtplib.
My objective is to include the below info in email
Attachment file #works fine
Paste the contents of a table in message body #works fine
Write a few lines about the table (as text) in message body # not works. instead stores as an attachment
So, I tried the below code
message = MIMEMultipart()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
# code to paste table contents in outlook message window - works fine
body_content = output # this has the pretty table - html table
message.attach(MIMEText(body_content, "html"))
# code to paste the written text in outlook message window - not works. instead of writing the text in outlook body,it stores as an attachment
written_text = """\
Hi,
How are you?"""
message.attach(MIMEText(written_text, "plain"))
# code to attach an csv file to a outlook email - works fine
with open(filename, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename= {filename}",
)
message.attach(part)
msg_body = message.as_string()
server = SMTP('internal.org.com', 2089)
server.sendmail(message['From'], message['To'], msg_body)
print("mail sent successfully")
server.quit()
The problem in my code is that it creates a text file (containing the message "Hi, How are you") and sends as an attachment?
But I want "Hi, How are you" as a text message in the main Outlook message window.
The immediate problem is that many email clients assume that text body parts after the first are attachments. You can experiment with adding an explicit Content-Disposition: inline to the part(s) you want rendered as part of the main message, but is there a reason these need to be separate body parts in the first place? Combining the text fragments into a single body part would perhaps make more sense here.
More fundamentally, your email code was written for an older Python version. The email module in the standard library was overhauled in Python 3.6 to be more logical, versatile, and succinct; new code should target the (no longer very) new EmailMessage API. Probably throw away this code and start over with modern code from the Python email examples documentation.
from email.message import EmailMessage
message = EmailMessage()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
message.set_content(output, subtype="html")
written_text = """\
Hi,
How are you?"""
message.add_attachment(
written_text, subtype="plain",
disposition="inline")
with open(filename, "rb") as attachment:
message.add_attachment(
attachment.read(),
maintype="application", subtype="octet-stream",
filename=filename)
with SMTP('internal.org.com', 2089) as server:
server.send_message(message)
print("mail sent successfully")
server.quit()
If the final attachment is really a CSV file, specifying it as application/octet-stream is a bit misleading; the proper MIME type would be text/csv (see also What MIME type should I use for CSV?)

Attach CSV to E-Mail with MIMEBase, only attaches 'bin' document

I want to write a function to attach a csv file to an E-Mail with HTML text. Everything works fine. I sends the E-Mail with the text and the attachment with the right information. Only the data format is wrong. I tried different varieties in MIMEBASE('application', 'octet-stream'). This one gives me a '.bin' data which I cannot open on macOS. Others gave me a plain text data which included the right data, but I don't wanna copy it manually into a csv. Does someone have a solution how I get out the data as '.csv'? Most of the solutions I found here just looked like my code below.
Code:
def send_mail(receiver, subject, filepath, attachname):
#login information
port = 123
smtp_server = "test.server.com"
login = "testlogin" # your login
password = "12345678910" # your password
# specify the sender’s and receiver’s email addresses and text in HTML
sender = "testmail#test.com"
receiver = receiver
message = MIMEMultipart()
message["Subject"] = subject
message["From"] = sender
message["To"] = COMMASPACE.join([receiver])
html_text = """\
<html>
<body>
<p>Hi ,<br>
<p>kind regards</p>
<p> This is an automatized Mail </p>
</body>
</html>
"""
# convert both parts to MIMEText objects and add them to the MIMEMultipart message
text = MIMEText(html_text, "html")
message.attach(text)
#add a file as attachment
file = open(filepath, "rb")
att = MIMEBase('application', 'octet-stream')
att.set_payload(file.read())
encoders.encode_base64(att)
att.add_header("Content_Disposition",
'attachment', filename = attachname)
message.attach(att)
try:
#send your message with credentials specified above
with smtplib.SMTP(smtp_server, port) as server:
server.connect(smtp_server, port)
server.ehlo()
server.starttls()
server.ehlo()
server.login(login, password)
server.sendmail(sender, receiver, message.as_string())
# tell the script to report if your message was sent or which errors need to be fixed
print('Sent')
except (gaierror, ConnectionRefusedError):
print('Failed to connect to the server. Bad connection settings?')
except smtplib.SMTPServerDisconnected:
print('Failed to connect to the server. Wrong user/password?')
except smtplib.SMTPException as e:
print('SMTP error occurred: ' + str(e))
send_mail('testmail#test.com', 'TEST', '/Users/Changer/Desktop/test.csv', 'test.csv')
Email clients may use the content type of the attachment part to determine which program is used to open the attachment, so specifying a suitable content type may help.
This code uses the standard library's mimetypes module to try to guess the correct content type of the attachment, based on its name:
import mimetypes
mimetypes.init()
def send_mail(receiver, subject, filepath, attachname):
...
mimetype, _ = mimetypes.guess_type(attachname)
if mimetype is None:
mimetype = 'application/octet-stream'
type_, _, subtype = mimetype.partition('/')
att = MIMEBase(type_, subtype)
...
The above code will generate these headers for the attachment:
b'Content-Type: text/x-comma-separated-values'
b'MIME-Version: 1.0'
b'Content-Transfer-Encoding: base64'
b'Content_Disposition: attachment; filename="test.csv"'
Whether a mac will open a csv with a spreadsheet application (I assume this is what you want) depends on the configuration of the machine (See this blog for an example). If you want to be sure open the file in a spreadsheet it might be more effective to convert it to a spreadsheet format and send the spreadsheet.

How to email unique attachments to multiple unique recipients using Python?

I am writing an automation Python Script. My intention is to email multiple unique attachments to multiple unique recipients. For example l have 1000 unique statements that must be emailed to 1000 unique clients. I want my Python script to be able to pick an attachment automatically and send it to the right recipient!
I have created the script and that creates the pdf attachments and name them after each email address of recipients so that l can use the names to pick the attachment and match that with the email of the recipient.
It Picks attachment perfectly BUT the problems it keeps incrementing attachment to users in each iteration..
#1.READING FILE NAMES WHICH ARE EMAIL ADDRESS FOR RECEPIENTS
import os, fnmatch
filePath = "C:/Users/DAdmin/Pictures/"
def readFiles(path):
fileNames =fnmatch.filter(os.listdir(path), '*.pdf')
i=0
pdfFilesNamesOnly=[]
while i < len(fileNames):
s =fileNames[i]
removeThePdfExtension= s[0:-4]
pdfFilesNamesOnly.append(removeThePdfExtension)
i+=1
return pdfFilesNamesOnly
-----------------------------------------------------------
#2.SENDING AN EMAIL WITH UNIQUE ATTACHMENT TO MULTIPLE UNIQUE RECEPIENTS
import smtplib
import mimetypes
from optparse import OptionParser
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.application import MIMEApplication
import os,fnmatch
from readFileNames import readFiles
filePath = "C:/Users/DAdmin/Pictures/"
listOfEmails= readFiles(filePath)# Files are named after emails
def sendEmails(listOfFiNames): #sends the email
email = 'goddietshe#gmail.com' # Your email
password = '#####' # Your email account password
send_to_list = listOfEmails# From the file names
#creating a multipart object
subject ="MONTHLY STATEMENTS"
mg = MIMEMultipart('alternative')
mg['From'] = email
mg['To'] = ",".join(send_to_list)
mg['Subject'] = subject
mg.attach(MIMEText("Please receive your monthly statement",'plain'))
# Attaching a file now before emailing. (Where l have a problem)
for i in listOfEmails:
if i in send_to_list:
newFile = (i+'.pdf') # create the name of the attachment to email using the name(which is the email) and will be used to pick the attachment
with open(newFile,'rb') as attachment:
part = MIMEBase('application','x- pdf')
part.set_payload(attachment.read())
attachment.close()
encoders.encode_base64(part)
part.add_header('Content- Disposition','attachment; filename="%s"' % newFile )
mg.attach(part)
text =mg.as_string() # converting the message obj to a string/text obj
server = smtplib.SMTP('smtp.gmail.com', 587) # Connect to the server
server.starttls() # Use TLS
server.login(email, password) # Login to the email server
# this is where it is emailing stuff
server.sendmail(email, i , text) # Send the email
server.quit() # Logout of the email server
print("The mail was sent")
#print("Failed ")
sendEmails(listOfFiNames)
I expect it to email each unique attachment to each unique recipient who are 1000 automatically
You reuse mg (message), using only attach, so your attachments pile up. You need to substitute the whole previous content with new content using set_payload because there is no "remove" method.
In doing this, you have to remember to re-add the text which you set before the loop:
mg.attach(MIMEText("Please receive your monthly statement",'plain'))
for i in listOfEmails:
because by using set_payload you lose all previous parts you attached. I'd just save this as a variable and then add it in the loop.
Moreover:
mg['To'] = ",".join(send_to_list)
This line makes all messages send to all people. You need to move this part to the loop as well, setting only one email address at once.
EDIT Applying those changes:
def sendEmails(): #sends the email
email = 'goddietshe#gmail.com' # Your email
password = '#####' # Your email account password
#creating a multipart object
subject ="MONTHLY STATEMENTS"
mg = MIMEMultipart('alternative')
mg['From'] = email
# mg['To'] = ",".join(listOfEmails) # NO!
mg['Subject'] = subject
text_content = MIMEText("Please receive your monthly statement",'plain')) #safe it for later, rather than attach it - we'll have to re-attach it every loop run
for i in listOfEmails:
if i in send_to_list:
newFile = (i+'.pdf') # create the name of the attachment to email using the name(which is the email) and will be used to pick the attachment
with open(newFile,'rb') as attachment:
part = MIMEBase('application','x- pdf')
part.set_payload(attachment.read())
attachment.close()
encoders.encode_base64(part)
part.add_header('Content- Disposition','attachment; filename="%s"' % newFile )
mg.set_payload(text_content) #change the whole content into text only (==remove previous attachment)
mg.attach(part) #keep this - new attachment
mg["To"] = i # send the email to the current recipient only!
text =mg.as_string() # converting the message obj to a string/text obj
server = smtplib.SMTP('smtp.gmail.com', 587) # Connect to the server
server.starttls() # Use TLS
server.login(email, password) # Login to the email server
# this is where it is emailing stuff
server.sendmail(email, i , text) # Send the email
server.quit() # Logout of the email server
print("The mail was sent")
**Works very fine like this. Thanks #h4z3**
def sendEmail():
email = 'goddietshetu#gmail.com' # Your email
password = '####' # Your email account password
subject ="MONTHLY STATEMENTS"
#creating a multipart object
mg = MIMEMultipart('alternative')
mg['From'] = email
mg['Subject'] = subject
# attaching a file now
listOfEmails = readFileNames(filePath)
for i in listOfEmails:
attachment =open(i+'.pdf','rb')
part = MIMEBase('application','octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',f"attachment; filename= {i}.pdf")
mg.set_payload(mg.attach(MIMEText("",'plain')))
mg.attach(MIMEText("Please receive your monthly statement",'plain'))
mg.attach(part)
mg['To'] = i
text =mg.as_string() # converting the message obj to a string/text obj
server = smtplib.SMTP('smtp.gmail.com', 587) # Connect to the server
server.starttls() # Use TLS
server.login(email, password) # Login to the email server
server.sendmail(email, i , text) # Send the email
server.quit() # Logout of the email serv
print("The mail was sent")
sendEmail()

Attach a word document to an e-mail and send without saving locally in python

I have an app that creates a word document, and I need to send it out without saving it locally. In other words, I need to send out an object. I know the code below doesn't work because my document is an object. Is there any way to do this?
TypeError: coercing to Unicode: need string or buffer, Document found
Does anyone know how to do this? Here is what I have:
def document_writer():
document = Document()
document.add_heading('Sample Document', 0)
document.add_paragraph("fsfsa")
document.add_heading('Addresses', 2)
return document
def mailsend(send_from, send_to, subject, text, login, password, document, files=None,
smtpserver='smtp.gmail.com:587'):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(text))
part = MIMEBase('application', "octet-stream")
part.set_payload(open(document, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="crap.docx"')
msg.attach(part)
server = smtplib.SMTP(smtpserver)
server.starttls()
#server.starttls()
server.login(login, password)
server.sendmail(send_from, send_to, msg.as_string())
server.close()
at some point in your code a function expects a string but gets a Document class which python then does not know how to convert to a string.
In your function document_writer you are creating such a class Document
So you should check where such created Document ojects are used.
To then solve this Problem there could be three solutions:
This Document Class can be converted to a string. Then you can just do str(Document)
This Document Class can not be converted to a string via str() but has a special function to do this. Then you can just do document_variable.convert_to_str_function()
You are able to save this Document Class to disk as a file. Then you should have a look at those answers: Binary file email attachment problem to send a disk based file via email

Categories

Resources