Invalid image in attachment - python-3.x

I used SMTP to send images through mail. It can send mail, but the images can't be opened; it said unsupported format. The image was taken using opencv. Here is the code I used. Please tell me what I did wrong in the program. There is no error but the image can't opened.
Cods:
import cv2
i=0
camera = cv2.VideoCapture(0)
if(i==0):
for i in range(1):
return_value, image = camera.read()
cv2.imwrite('Disease'+str(i)+'.png', image)
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 = "xxxxxx#gmail.com"
toaddr = "yyyyyy#gmail.com"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "DISEASE"
body = "FOR NOTIFICATION"
msg.attach(MIMEText(body, 'plain'))
filename = "Disease.png"
attachment = open("C:/tensorflow1/models/research/object_detection/capture.py", "rb")
p = MIMEBase('application', 'octet-stream')
p.set_payload((attachment).read())
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(p)
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login(fromaddr, "password")
text = msg.as_string()
s.sendmail(fromaddr, toaddr, text)
s.quit()

You are attaching a piece of Python code and claiming that it is an image. I imagine you probably want to run that code and attach its output.
Assuming C:/tensorflow1/models/research/object_detection/capture.py is the path to the code which you included which creates the image, and that C:/tensorflow1/models/research/object_detection/ is on your PYTHONPATH, you simply want to
import disease
though arguably, a better design is to restructure this file so that it declares a function you can call when you need to, rather than running the code exactly once immediately when you import it; the standard mechanism for this is
def functionname():
# ... Your code here
if __name__ == '__main__':
functionname()
and the way to import and call this code is then
import disease
# ... Other code ...
disease.functionname()
(Obviously, a better name than functionname would be something properly descriptive, like generate_png.)
Perhaps you might want to avoid writing the image to disk at all if you are only using it to attach it to an email message and then discarding it, but as this is somewhat speculative, and hitting the disk is not a major efficiency problem, I won't elaborate on that at this point.
As another detail, you are attaching the image as a generic application/octet-stream body part. Images have their own designated MIME types; you'll want image/png for a PNG picture.
The Python class for this type of attachment is MIMEImage.
from email.mime.image import MIMEImage
# ...
image_filename = './Disease.png'
image = MIMEImage(open(image_filename, 'rb').read(), name=os.path.basename(image_filename))
msg.attach(image)
The class generally already knows how to set detailed attributes like the precise image type and the content disposition and transfer encoding.
As a stylistic aside, import statements should generally be at the top of the file; see PEP-8.

Related

Image doesn't appear when mailing python 3

I am creating a program where I send an image to someone by mail with Tkinter and smtplib in python 3. Everything works fine but when someone receives the email it looks like this:
but I want it to look like this:
does anyone know why the { ? } shows up?
the code I use to send the photo is this:
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
msg = MIMEMultipart()
fp = open(image, 'rb')
photo = MIMEImage(fp.read(), _subtype="jpg")
fp.close()
msg.attach(photo)
Thanks!

Unable to attach Excel file to email using python - TypeError: expected bytes-like object, not Workbook

I am working on python code which will add one string in excel file and will attach that file to email. I am sending this email using AWS SES.
When I am trying to run my code it is giving me below error -
TypeError: expected bytes-like object, not Workbook
Below is my code-
import boto3
import xlsxwriter
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
def lambda_handler(event, context):
client = boto3.client('ses',region_name=AWS_REGION)
sender = "xxx#gmail.com"
to = "aaa#gmail.com"
workbook = xlsxwriter.Workbook('abc.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write('A1', 'Hello..')
#send email with attachment
msg = MIMEMultipart()
msg['Subject'] = 'Test Email'
msg['From'] = sender
msg['To'] = to
body_text = MIMEText(BODY_TEXT, "html")
attachment = MIMEApplication(workbook)
attachment.add_header('Content-Disposition', 'attachment', filename='Expenses01.xlsx')
msg.attach(attachment)
msg.attach(body_text)
response = client.send_raw_email(
Source=sender,
Destinations=[to],
RawMessage={"Data": msg.as_string()}
)
I know there is something wrong with workbook object. But I don't know how to resolve this issue. Can someone please help me out?
After reading XLSXWriter documentation, I found answer for this. Link - https://xlsxwriter.readthedocs.io/workbook.html
I am posting this answer, so that it can help other new python developers like me.
Earlier MIMEApplication() was not accepting workbook object so we need to convert it. I have updated my code. I have used BytesIO to create Workbook object and then added that object to MIMEApplication(). This example will create excel file and will attach that file to email.
New code-
from io import BytesIO
output = BytesIO()
workbook = xlsxwriter.Workbook(output)
worksheet = workbook.add_worksheet()
worksheet.write('A1', 'Hello..')
attachment = MIMEApplication(output.getvalue())
attachment.add_header('Content-Disposition', 'attachment', filename='abc.xlsx')
attachment.add_header('Content-Type', 'application/vnd.ms-excel; charset=UTF-8')
Your question did not say where the error occurs,
but I think it happens here:
attachment = MIMEApplication(workbook)
You should pass in bytes, rather than a complex python object.
Call workbook.close() to write out the 'abc.xlsx' file,
and send that binary file as an attachment.

Python - How can I send email with attachments in Python using gmail?

As you may well know, some mail addresses need to turn off security for less secure apps in gmail.
Turning off options works like a charm with smtplib and attachments, but without turning off it don't works at all.
Then I discovered an API way using Auth 2.0 in ezgmail module, and it can send emails very easy, but however the attachments are not attached well. they have some problem at encoded, because they don't display well the documents.
The code I ended up with is:
import ezgmail
import os, sys, glob
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
pathFile = '<path>'
folder = os.getcwd()
def compose():
# email_from = '<my email>'
subject = 'Hello,'
body = '''Here I put the text I want.'''
return [subject, body]
def send_email(email_to):
subject, body = compose()
ezgmail.EMAIL_ADDRESS = '<my email>' # email_from
os.chdir(pathFile)
list_pdfs = glob.glob('*.pdf')
file1 = max(list_pdfs, key=os.path.getctime) # select latest file
# this should do the *encode part*
attachment = open(file1, 'rb')
file = MIMEBase('application','octet-stream')
file.set_payload((attachment).read())
encoders.encode_base64(file)
os.chdir(folder)
ezgmail.send(email_to, subject, body, file.as_string())
print("Mail sent")
send_email(to#example.com)
And the question it's : how to attach properly documents (pdf, word, excel) to ezgmail ?
The attachment(s) need to be a string (or list of strings) representing the path(s) to the document(s):
def send_email(email_to):
subject, body = compose()
ezgmail.EMAIL_ADDRESS = '<my email>' # email_from
os.chdir(pathFile)
list_pdfs = glob.glob('*.pdf')
file1 = max(list_pdfs, key=os.path.getctime) # select latest file
os.chdir(folder)
ezgmail.send(email_to, subject, body, os.sep.join([pathFile, file1]))
print("Mail sent")
The encoding is a separate issue.
The author says:
EZGmail isn't meant to be comprehensive and do everything the Gmail API lets
you do, it's meant to make the simple things simple
It does attempt to do the MIME conversion, but there is nothing specific for pdf files
the following code needs to be inserted into the EZgmail module in the _createMessageWithAttachments method:
elif sub_type == 'pdf':
mimePart = MIMEBase('application','octet-stream')
mimePart.set_payload((fp).read())
encoders.encode_base64(mimePart)
and you need to import encoders from email

Edit header in '.eml'

As a brief summary, I have a bunch of '.eml' files in a directory. I need to forward these emails back to 'email#example.com'.
The problem is that the field 'From' in the header of the '.eml' file contains another email address which doesn't match with 'email#example.com'.
I've searched for a way to parse the file and update the content of the header.
At first, I was using the following modules:
eml.parser to parse the file.
pyo365 to connect to MSGraph API
I was able to send the content of the body but when I would try to send the attachments, I had to decode from base64 and extract the attachments in a folder, then send everything. I did not need to change the content of the header.
I know this was a bad move because there is probably a way to send the attachments encoded.
Also, since MSGraph attachment's file size limit is 4mb per requests, I decided to try to change for:
smtplib to send the email
I tried mail-parser without success to update anything in the content since the updated values would not be permanent, for instance:
mail = mailparser.parse_from_bytes(byte_mail)
mail.from_ = [('My Name' , 'email#example.com')]
print(mail.headers) #This would print the original header
I also tried with mail.update() and various method using this module without success.
I found a post Python: Modify Values in eml file (email header) which suggested to use Parser, replace_header and as_string from email but I was unable to make it work either as I wouldn't be able to call replace_header and as_string:
from email.message import EmailMessage #contains as_string
from email.parser import HeaderParser
file = open(filename, 'r')
h = HeaderParser().parse(file)
#stuck here
I know this is probably not only one question but the main goal is to send eml files back to a particular address, from 'email#example.com'.
The issue was resolved by parsing the email with eml_parser. I created my own header, attached the HTML body content and the attachments.
from passlib.context import CryptContext
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.header import Header
def send(self, dst):
try:
self.m = MIMEMultipart()
self.m['From'] = self.client_addr
self.m['To'] = dst
# Must have Header() in python 3.x otherwise you get UnicodeError
self.m['Subject'] = Header(self.get_subject(), 'utf-8')
#Attach HTML body with the right encoding
self.m.attach(MIMEText(self.get_body().encode('utf-8'), 'html', 'utf-8'))
# Extract attachments to self.attachment_path
self.extract_attachments(self.parsed_eml)
server = smtplib.SMTP('smtp.office365.com', 587)
server.ehlo()
server.starttls()
# Compare hash in config.json file
if self.pwd_context.verify(self.client_plain_secret, self.client_secret):
server.login(self.client_addr, self.client_plain_secret)
server.sendmail(self.m['From'], self.m['To'], self.m.as_string())
server.quit()
except:
print("An error occured trying to send the email.")
finally:
self.clean_attachments()

Python3 - Web Scraper w/ BeautifulSoup, Email Price Alert

I'm attempting to setup a script to email me when a phone meets my price and condition threshold on swappa.com. I've gotten to the point when I can email out the latest post that meets my criteria, but I've hit a bit of wall. I need it to only email me when a new post meets my criteria. Currently, it will email me each time the script is ran, whether or not the listing is new.
I'm very new to Python and spent the better part of yesterday and today hobbling together this script, but any insight or information that would help accomplish my goal would be much appreciated!
from bs4 import BeautifulSoup
import re
import requests
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# define the url in a variable and get the content
page_url = 'https://swappa.com/buy/google-pixel-2-xl-unlocked?sort=date_min'
page_content = requests.get(page_url)
# Put the content into a variable using BeautifulSoup
swappa = BeautifulSoup(page_content.text, 'html.parser')
# Pull text from the h1 tag
phone_name = swappa.h1
# Pull the device details into a list
condition = swappa.find(class_='condition_label')
color = swappa.find(class_='color_label')
storage = swappa.find(class_='storage_label')
price = swappa.find(class_='price')
links = []
for link in swappa.findAll('a', attrs={'href': re.compile("^/listing")}):
links.append(link.get('href'))
listing_detail = [
phone_name.contents[0], condition.contents[0], color.contents[0], storage.contents[0],
'$' + price.contents[1], 'https://swappa.com' + links[0]
]
def listing_result():
if (price.contents[1] <= '420') and (condition.contents[0] == 'Good' or condition.contents[0] == 'Mint'):
return listing_detail
with open("result.txt", "w+") as result_file:
result_file.write(str(listing_result()))
with open('result.txt', 'r') as result_file_read:
result_data = result_file_read.read().replace('\n', '')
if result_data != result_file:
# setup sending an email via SMTP
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.login("redacted", "redacted")
# use mime to send email
msg = MIMEMultipart()
# setup parameters of the email
msg['From'] = 'redacted'
msg['To'] = 'redacted'
msg['Subject'] = 'Swappa - New Pixel 2 XL Listing'
# add in the message body
body = str(listing_result())
body = MIMEText(body) # convert the body to a MIME compatible string
msg.attach(body) # attach it to your main message
# send the email
server.send_message(msg)
del msg
My idea was to have the latest listing be sent to a text file, then have the contents of that text file saved to a variable so that I could compare it to the text being created. However, I'm at a loss for how to accomplish that.

Resources