How to send a complete email using smtplib python - 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?)

Related

smtplib multiple attachments failed to be received

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.

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.

Send Excel file via Gmail API but attachment is corrupted

I was trying to get a python program send an attachment via Gmail.
I used the sample code found:
Sending email via gmail & python
The problem is when I sent Excel files like .xls, .xlsx, .xlsm to myself, I cannot open the attachments as they were corrupted, even though the original files are fine. But sending .csv works fine. The entire process does not pop up any warnings or error.
Question is: did oauth2.0 or Gmail or MIME mess up the attachment for some formats? And how can I tell the program upload and send attachment without modifying it?
Had similar issue.
The error is probably in encoding file into bytes.
My gmail still sends corrupted file, when I do want to send xlsx, but I managed to get correct email with xls format.
This is my code:
def create_message_with_excel_attachment(sender, to, subject, message_text, file):
message = MIMEMultipart()
message['to'] = to
message['from'] = sender
message['subject'] = subject
msg = MIMEText(message_text)
message.attach(msg)
part = MIMEBase('application', "vnd.ms-excel")
part.set_payload(open(file, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename=file)
message.attach(part)
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
return {'raw': raw}
def send_message(service, user_id, message):
message = (service.users().messages().send(userId=user_id, body=message).execute())
try:
print ('Message Id: %s' % message['id'])
return message
except:
print ('An error occurred:')

Sending a PDF attachment with a HTML+plain_text email in Python

I'm trying to send a PDF attachment with an email body that summarises the contents of the PDF file. The email body is in both HTML and plaintext.
I'm using the following code to build the email email message object:
#Part A
logging.debug(" Building standard email with HTML and Plain Text")
msg = MIMEMultipart("alternative")
msg.attach(MIMEText(email_obj.attachments["plain_text"], "plain", _charset="utf-8"))
msg.attach(MIMEText(email_obj.attachments["html_text"], "html", _charset="utf-8"))
#Part B
logging.debug(" Adding PDF report")
pdf_part = MIMEApplication(base64.decodestring(email_obj.attachments["pdf_report"]), "pdf")
pdf_part.add_header('Content-Disposition', 'attachment', filename="pdf_report.pdf")
logging.debug(" Attaching PDF report")
msg.attach(pdf_part)
My problem is that my email body disappears if I attach the PDF. If I comment out the code that attaches the PDF (Part B), the email body appears.
Unless I'm mistaken, it looks as though my PDF attachment is overwriting the email body.
Such a message should have a more complex structure. The message itself contains two "top-level" MIME parts and has content-type "multipart/mixed". The first of these MIME part has type of "multipart/alternative" with two subparts, one for plain text and another for HTML. The second of the main parts is PDF attachment
pdfAttachment = MIMEApplication(pdf, _subtype = "pdf")
pdfAttachment.add_header('content-disposition', 'attachment', filename = ('utf-8', '', 'payment.pdf'))
text = MIMEMultipart('alternative')
text.attach(MIMEText("Some plain text", "plain", _charset="utf-8"))
text.attach(MIMEText("<html><head>Some HTML text</head><body><h1>Some HTML Text</h1> Another line of text</body></html>", "html", _charset="utf-8"))
message = MIMEMultipart('mixed')
message.attach(text)
message.attach(pdfAttachment)
message['Subject'] = 'Test multipart message'
f = open("message.msg", "wb")
f.write(bytes(message.as_string(), 'utf-8'))
f.close()
You may try to open a "source view" of a message in your favorite mail program (mail user agent) and see yourself (Ctrl-U in Thunderbird)

adding attachment using sendmail from unix

I am using the following code to send email from unix.
Code
#!/usr/bin/python
import os
def sendMail():
sendmail_location = "/usr/sbin/sendmail" # sendmail location
p = os.popen("%s -t" % sendmail_location, "w")
p.write("From: %s\n" % "myname#company.com")
p.write("To: %s\n" % "yourname#company.com")
p.write("Subject: My Subject \n")
p.write("\n") # blank line separating headers from body
p.write("body of the mail")
status = p.close()
if status != 0:
print "Mail Sent Successfully", status
sendMail()
I am not sure how to add attachment to this email (attachment being on a different directory /my/new/dir/)
Sendmail is an extremely simplistic program. It knows how to send a blob of text over smtp. If you want to have attachments, you're going to have to do the work of converting them into a blob of text and using (in your example) p.write() to add them into the message.
That's hard - but you can use the email module (part of python core) to do a lot of the work for you.
Even better, you can use smtplib (also part of core) to handle sending the mail.
Check out http://docs.python.org/2/library/email-examples.html#email-examples for a worked example showing how to send a mail with attachments using email and smtplib
Use the email.mime package to create your mail instead of trying to generate it manually, it will save you a lot of trouble.
For example, sending a text message with an attachment could be as simple as:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
msg = MIMEMultipart()
msg['From'] = 'fromaddress'
msg['To'] = 'toaddres'
msg['Subject'] = 'subject'
msg.attach(MIMEText('your text message'))
with open(filename, 'rb') as f:
attachment = MIMEApplication(f.read(), 'subtype')
attachment['Content-Disposition'] = 'attachment; filename="%s";' % filename
msg.attach(attachment)
message = msg.as_string()
Then you can write the message to sendmail, or use smtplib to send it.
'subtype' should either be replaced with the mime subtype of the attached document, or left out to send the attachment with the default type of application/octet-stream. Or if you know your file is text, you can use MIMEText instead of MIMEApplication.
I normally use the following to send a file "file_name.dat" as attachment:
uuencode file_name.dat file_name.dat | mail -s "Subject line" arnab.bhagabati#gmail.com

Categories

Resources