as_string() method return an AttributeError - python

I try to send message from gmail with python 3.6 by this part of code:
import smtplib as smtp
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
SUBJECT = *subject message*
SOURCE = *directory*
TEXT = *some text in message*
msg = MIMEMultipart()
msg['From'] = *send from email*
msg['To'] = *send to email*
msg['Subject'] = SOURCE
#################### part with attachment
msg.attach(MIMEText(TEXT, 'plain'))
filename = os.path.basename(SOURCE)
attachment = open(SOURCE, '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)
#################### end of attachment part
#################### server part
server = smtp.SMTP(smtp.gmail.com', 587)
server.starttls()
server.login(*send from email*, *send from email password*)
server.sendmail(*send from email*, *send to email*, msg.as_string())
server.close()
But get an AtributeError:
AttributeError: 'list' object has no attribute 'encode'
If i delete Attachment part of code and add msg = MIMEText(TEXT) before server part i get an letter in my email, but it doesn't contain subject. So i get a letter with some text only.
What i do wrong? Any thoughts?
EDIT: The error appears in msg.as_string() line

Here's how I send emails over python
#this will allow us to send emails over our smtp server
import smtplib
#who the message will be from, doesn't really need to be set since the message variable will do this automatically
sender ='sender#email.com'
#who the message will be sent to (this one matters)
receiver = 'receiver#email.com'
#the actual email we'll be sending, note its contents- the To section is irrelevant but neccessary
message = """From: From Person <sender#email.com>
To: To Person <receiver#email.com>
Subject: SMTP Email Test
this is a test email
"""
try:
#proc our server to send the mail by providing it's ip to the program
smtpObj = smtplib.SMTP('<your smtp ip address goes here>')#this is the ip of the server I'm on
#attempt to send the piece of mail
smtpObj.sendmail(sender, receiver, message)
#if the above executes send a confirmation to the user
print "Successfully sent mail!"
except:
#if something goes wrong catch it ad send a message to the user
print "Error: unable to send mail"
to add attachments simply add the following which I retrieved from here
# open the file to be sent
filename = "File_name_with_extension"
attachment = open("Path of the file", "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'
smtpObj.attach(p)

Changing:
msg.attach(MIMEText(TEXT, 'plain'))
filename = os.path.basename(SOURCE)
attachment = open(SOURCE, '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)
with:
msg.attach(MIMEText(text))
for f in files:
with open(f, "rb") as fil:
part = MIMEApplication(fil.read(), Name = basename(f))
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
worked for me.
Thanks to How to send email attachments? #Oli answer.

Related

Getting a KeyError when attaching attachments to an email

So I'm trying to create an automated email script that reads surnames, recipient emails, and files to be attached from an excel file.
For some reason, when I send the email with the attachments it returns a KeyError, which I think traces back to the placeholder for the filename of the attachment that I'm using .add_header('Content-Disposition', 'attachment; filename = {file}') with. Because whenever I change the placeholder, the KeyError specifies what I wrote in it.
Anyways, here's the source code
import pandas as pd
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
context = ssl.create_default_context()
sender = 'john#email.com'
sender_pass = '123456789'
email_subject = 'subject'
attachments_list = 'testingemail.xlsx'
body = 'Dear Mx. {surname} body'
def attach_attachment(attachment_list, payload):
# opening excel file into dataframe
attachments = pd.read_excel(attachment_list, sheet_name = 'attachments')
for file in attachments['attachments']:
with open(file, 'rb') as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part) # encoding attachment
part.add_header(
'Content-Disposition',
'attachment; filename = {file}',
)
# attaching attachment
payload.attach(part)
def composing_email(e_sender, e_password, e_subject, e_recipient, attachment_list):
# setup the MIME
message = MIMEMultipart()
message['From'] = e_sender
message['To'] = e_recipient
message['Subject'] = e_subject
# attaching body and the attachments for the mail
message.attach(MIMEText(body, 'plain'))
attach_attachment(attachment_list, message)
# create SMTP session for sending the mail
session = smtplib.SMTP('smtp.gmail.com', 587) # gmail port
session.starttls(context = context) # securing connection
session.login(e_sender, e_password) # logging into account
text = message.as_string()
# actually sending shit
session.sendmail(e_sender,
e_recipient,
text.format(surname = surname,
receiver_email = recipient,
sender = e_sender,
)
)
session.quit()
print('Mail successfully sent to {surname} with emal {recipient}')
if __name__ == '__main__':
surname_email = pd.read_excel('testingemail.xlsx', sheet_name = 'surname_email')
for surname, recipient in surname_email.itertuples(index=False):
composing_email(sender, sender_pass, email_subject, recipient, attachments_list)
and here's the error
Traceback (most recent call last):
File "c:\Users\zddj9\Desktop\auto_email\script.py", line 77, in <module>
composing_email(sender, sender_pass, email_subject, recipient, attachments_list)
File "c:\Users\zddj9\Desktop\auto_email\script.py", line 64, in composing_email
text.format(surname = surname,
KeyError: 'file'
I just realized that I needed to use f-string formatting
instead of this
part.add_header(
'Content-Disposition',
'attachment; filename = {file}',
)
add an f before the argument with the placeholder
part.add_header(
'Content-Disposition',
f'attachment; filename = {file}',
)
The confirmation also wouldn't work without f-string formatting
print('Mail successfully sent to {surname} with email {recipient}')
It should be this
print(f'Mail successfully sent to {surname} with email {recipient}')

Python: sending mail via python creates unknown attachment

I'm trying to send a mail + attachment (.pdf file) via python.
The mail is send but the attachment becomes an unknown attachment instead of being a .pdf file
My code looks like this:
import smtplib
import os
import ssl
import email
from email import encoders
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
port = 465
smtp_server = "smtp.gmail.com"
subject = "An example of txt.file"
sender = "..."
receiver = "..."
password = "..."
message = MIMEMultipart()
message["From"] = sender
message["To"] = receiver
message["Subject"] = subject
filename = '318.pdf'
attachment = open(filename, "rb")
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
part.add_header('Content Disposition', 'attachment', filename=filename)
encoders.encode_base64(part)
message.attach(part)
message.attach(part)
body = "This is an example of how to send an email with an .pdf-attachment."
message.attach(MIMEText(body, 'plain'))
text = message.as_string()
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
server.login(sender, password)
server.sendmail(sender, receiver, text)
print('Sent')
What is wrong with it or what do I have to do differently?
I've tried different file types, the .pdf file is in the python file directory,...
You wrote
part.add_header('Content Disposition', 'attachment', filename=filename)
But the 'filename' argument-name must be provided as a string and you also missed the hyphen in 'Content-Disposition'. Try
part.add_header('Content-Disposition', 'attachment; filename=' + filename)
This should solve your issue. Just pointing out, you attached 'part' twice - on lines 20 and 22.
I think you might already be following this article. But if not, I think you'll find it useful.

Sending an email via python from my raspberry pi

id like to send emails regularly from my raspberry pi with a small appended excel file. For this i use my a gmail account. I can send emails but not with something appended.
These following lines are faulty:
SendMail.prepareMail(…)
and
part.set_payload(os.open(file), "rb").read()) -> this is the error i get "IsADirectoryError: [Errno 21] Is a directory: '/'
i hope you can help me with that
import sys, smtplib, os
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
class SendMail(object):
mailadress = 'teststand#gmail.com'
smtpserver = 'smtp.googlemail.com'
username = 'xxx'
password = 'xxx'
def send(self, files):
# Gather information, prepare mail
to = self.mailadress
From = self.mailadress
#Subject contains preview of filenames
if len(files) <= 3: subjAdd = ','.join(files)
if len(files) > 3: subjAdd = ','.join(files[:3]) + '...'
subject = 'Dateiupload: ' + subjAdd
msg = self.prepareMail(From, to, subject, files)
#Connect to server and send mail
server = smtplib.SMTP(self.smtpserver)
server.ehlo() #Has something to do with sending information
server.starttls() # Use encrypted SSL mode
server.ehlo() # To make starttls work
server.login(self.username, self.password)
failed = server.sendmail(From, to, msg.as_string())
server.quit()
def prepareMail(self, From, to, subject, attachments):
msg = MIMEMultipart()
msg['From'] = From
msg['To'] = to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
# The Body message is empty
msg.attach( MIMEText("") )
for file in attachments:
#We could check for mimetypes here, but I'm too lazy
part = MIMEBase('application', "octet-stream")
part.set_payload( open(os.open(file),"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
msg.attach(part)
#Delete created Tar
return msg
if __name__ == '__main__':
mymail = SendMail()
# Send all files included in command line arguments
mymail.send(sys.argv[1:])
SendMail.prepareMail("teststand#gmail.com", "teststand#gmail.com", "empfanger#gmx.de", "Titel 1", "/home/pi/Desktop/Teststand/export/protokoll/Protokoll04_May_2020.xlsx")
You are iterating through the variable attachments using the for loop, which is a string. So right now for file in attachment means file would contain every character in the string attachments.
Try:
SendMail.prepareMail("teststand#gmail.com", "teststand#gmail.com", "empfanger#gmx.de", "Titel 1", ["/home/pi/Desktop/Teststand/export/protokoll/Protokoll04_May_2020.xlsx"])
Pass the value of attachments in a list. So when you do for file in attachments, the value of file will be equal to the required location string.

Sending corrupt files by email python

I'm sending an excel spreadsheet as an attachment using this code:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
def send_email(subject, mail_body, attachment= None):
to_addr = input("Enter the recipient's email address: ")
from_addr = 'cloudops#noreply.company.com'
content = mail_body
msg = MIMEMultipart()
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = subject
body = MIMEText(content, 'html')
msg.attach(body)
server = smtplib.SMTP('smtpout.us.cworld.company.com', 25)
with open(attachment, 'r') as f:
part = MIMEApplication(f.read(), Name=basename(attachment))
part['Content-Disposition'] = 'attachment; filename="{}"'.format(basename(attachment))
msg.attach(part)
try:
server.send_message(msg, from_addr=from_addr, to_addrs=to_addr)
print(f"Email was sent to: {to_addr}")
except Exception as e:
print(f"Exception: {e}")
print("Email was not sent.")
And when I open the file I get a message that says:
Excel cannot open teh file 'Cost Allocation - 201906.xlsx' because the
file format or extension is not valid. Verify that the file has not
been corrupted and that the file extension matches the format of the
file.
Why am I getting this error and how do I correct this?
You have to open file in bytes mode
with open(..., 'rb') as f:
In text mode it converts byte used for "new line" and finally it sends incorrect data.

Attaching some files to MIME multipart email

How can I attach some files to an MIME multipart email using Python3?
I want to send some attachments as "downloadable content" to my HTML-mail (with a plain text fallback). Couldn't find anything so far...
Edit: After a few trys I just made it to send my file. Thanks for the tip #tripleee. But unfortunatly my HTML is now sent as plain text...
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.utils import formatdate
from os.path import basename
import smtplib
login = "*********"
password = "*********"
server = "smail.*********:25"
files = ['Anhang/file.png']
# Create the root message and fill in the from, to, and subject headers
msg = MIMEMultipart('related')
msg['From'] = "*********"
msg['To'] = "*********"
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = "*********"
msg['Reply-To'] = "*********"
msg.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
with open('*********.txt', 'r') as plainTXT:
plain = plainTXT.read()
plainTXT.close()
msgAlternative.attach(MIMEText(plain))
with open('*********.html', 'r') as plainHTML:
html = plainHTML.read()
plainHTML.close()
msgAlternative.attach(MIMEText(html))
msg.attach(msgAlternative)
# Image
for f in files or []:
with open(f, "rb") as fp:
part = MIMEImage(
fp.read(),
Name=basename(f)
)
# closing file
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
# create server
server = smtplib.SMTP(server)
server.starttls()
# Login Credentials for sending the mail
server.login(login, password)
# send the message via the server.
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
print("successfully sent email to %s:" % (msg['To']));
I just had to use MIMEText(html, 'html') for the attaching part of my HTML.
Working code:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.utils import formatdate
from os.path import basename
import smtplib
login = "YourLogin"
password = "YourPassword"
server = "SMTP-Server:Port"
files = ['files']
# Create the root message and fill in the from, to, and subject headers
msg = MIMEMultipart('related')
msg['From'] = "FromEmail"
msg['To'] = "ToEmail"
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = "EmailSubject"
msg['Reply-To'] = "EmailReply"
msg.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
with open('YourPlaintext.txt', 'r') as plainTXT:
plain = plainTXT.read()
plainTXT.close()
msgAlternative.attach(MIMEText(plain, 'plain'))
with open('YourHTML.html', 'r') as plainHTML:
html = plainHTML.read()
plainHTML.close()
msgAlternative.attach(MIMEText(html, 'html'))
msg.attach(msgAlternative)
# Image
for f in files or []:
with open(f, "rb") as fp:
part = MIMEImage(
fp.read(),
Name=basename(f)
)
# closing file
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
# create server
server = smtplib.SMTP(server)
server.starttls()
# Login Credentials for sending the mail
server.login(login, password)
# send the message via the server.
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
print("successfully sent email to %s:" % (msg['To']));
Thanks to #tripleee

Categories