I am trying to read content from Gmail using poplib. The content in my email is mostly base64 encoded but contains some additional symbols. However, when I read the content using poplib, for some reason my original content is base64 encoded again.
Example content in my email: {{{eyJjb250ZW50IjpbeyJjZWxsIjoiQTEiLCJ2YW
Example response that I get from poplib: e3t7ZXlKamIyNTBaVzUwSWpwYmV5SmpaV3hzSWpvaVFURWlMQ0oyWVd
Any suggestions on how to retrieve my original (raw) content (without poplib encoding it again) will be greatly appreciated.
Here is the code that I am using:
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]
psr = parser.FeedParser()
for msg in messages:
psr.feed(str(msg))
mess = psr.close()
payload = mess.get_payload(decode=True)
Related
I need to email the pandas dataframe df as an attachment using exchangelib.
below is my code
import io
attachments=[]
x=io.BytesIO(df.to_records(index=False))
content=x.getvalue()
type(content) #bytes
send_email(account, 'Subject', 'test email', ['testemail.com'],
attachments=attachments)
when i use the above code i get the email with attachment,but it's in the byte format so it's unreadable.
This is because the type(content) is "bytes"
Below code works perfectly fine.
This is because type(content) is "class 'bytes'> "
but i am not allowed to save my dataframe locally as .csv file.
I want send the dataframe directly as an email attachment.
How can I convert my df in the "class 'bytes'> " format and email the attachment in csv ?
attachments = []
with open('test.csv', 'rb') as f:
content = f.read()
type(content) #<class 'bytes'>
attachments.append(('test.csv', content))
below is send_email i am using
from exchangelib import Configuration, Account, DELEGATE
from exchangelib import Message, Mailbox, FileAttachment
def send_email(account, subject, body, recipients, attachments=None):
"""
Send an email.
Parameters
----------
account : Account object
subject : str
body : str
recipients : list of str
Each str is and email adress
attachments : list of tuples or None
(filename, binary contents)
Examples
--------
"""
to_recipients = []
for recipient in recipients:
to_recipients.append(Mailbox(email_address=recipient))
# Create message
m = Message(account=account,
folder=account.sent,
subject=subject,
body=body,
to_recipients=to_recipients)
# attach files
for attachment_name, attachment_content in attachments or []:
file = FileAttachment(name=attachment_name, content=attachment_content)
m.attach(file)
m.send_and_save()
File contents are always bytes, no matter which file type you are talking about. Programs may hide that fact from you, but it's still true.
When you write "it's in the byte format so it's unreadable", I assume you mean that your email client does not help you to view the file in the way you expected. To help your email client do that, try setting the content_type attribute of FileAttachment to something that matches the file type of your attachment. See Response Content type as CSV for suggested values.
BACKGROUND
Regarding the following articles:
https://www.drupal.org/project/mimemail/issues/31524
Exclamation Point Randomly In Result of PHP HTML-Email
https://sourceforge.net/p/phpmailer/bugs/53/
All the problems and solutions refer to PHP issue, but I have run into this problem in Python.
If I send the emails directly to recipients, all is well, no exclamation marks appear, and the message displays properly.
However, utilizing our "Sympa" (https://www.sympa.org/) system that the University uses for it "mailing list" solution, emails from this system have the exclamation marks and line breaks inserted in the message and HTML breaks causing display issues.
The problem stems from line length. Any line longer than a magical 998 character length line gets this exclamation marks and line breaks inserted.
NOW THE QUESTION
One of the solutions they mention is encoding the body of a message in base64, which apparently is immune to the line length issue. However, I can not figure out how to properly form a message in Python and have the proper headers and encoding happen so the message will display properly in an email client.
Right now, I have only succeed in sending emails with base64 encode bodies as attached files. Bleck!
I need to send HTML encoded emails (tables and some formatting). I create one very long concatenated string of all the html squished together. It is ugly but will display properly.
HELP?!
NOTE: If anyone else has had this problem and has a solution that will allow me to send emails that are not plagued by line length issue, I am all ears!
Source Code as Requested
# Add support for emailing reports
import smtplib
# from email.mime.text import MIMEText
from email.mime.message import MIMEMessage
from email.encoders import encode_base64
from email.message import Message
... ...
headerData = {"rDate": datetime.datetime.now().strftime('%Y-%m-%d')}
msg_body = msg_header.format(**headerData) + contact_table + spacer + svc_table
theMsg = Message()
theMsg.set_payload(msg_body)
encode_base64(theMsg)
theMsg.add_header('Content-Transfer-Encoding', 'base64')
envelope = MIMEMessage(theMsg, 'html')
theSubject = "Audit for: "+aService['description']
envelope['Subject'] = theSubject
from_addr = "xxx#xxx"
envelope['From'] = from_addr
to_addrs = "xxx#xxxx"
# to_addrs = aService['contact_email']
envelope['To'] = to_addrs
# Send the message via our own SMTP server.
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()
SOLUTION, thank you #Serge Ballesta
Going back to MIMEText, and specifying a character set seemed to do the trick:
envelope = MIMEText(msg_body, 'html', _charset='utf-8')
assert envelope['Content-Transfer-Encoding'] == 'base64'
envelope['Subject'] = "Audit for: "+aService['description']
from_addr = "f5-admins#utlists.utexas.edu"
envelope['From'] = from_addr
to_addrs = "xx-test#xx.xx.edu"
envelope['To'] = to_addrs
# Send the message via our own SMTP server.
s = smtplib.SMTP('xx.xx.xx.edu')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()
Apparently I was just stabbing around and did not account for character set. Using MIMEText and not MIMEMessage.
Normally, email.mime.MIMEText automatically sets the Content-Transfert-Encoding to base64 if the body is not declared to be plain ASCII. So, assuming that body contains the HTML text of the body of the message (no mail headers there), declaring it as utf-8 should be enough:
msg = email.mime.text.MIMEText(body, 'html', _charset='utf-8')
# assert the cte:
assert msg['Content-Transfer-Encoding'] == 'base64'
theSubject = "Audit for: "+aService['description']
msg['Subject'] = theSubject
from_addr = "xxx#xxx"
msg['From'] = from_addr
to_addrs = "xxx#xxxx"
# to_addrs = aService['contact_email']
msg['To'] = to_addrs
# optionally set other headers
# msg['Date']=datetime.datetime.now().isoformat()
# Send the message
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, msg.as_bytes())
s.quit()
I am attempting to build a client reporting engine using R/Python, and Sendgrid's Email API. I can send emails, but the last thing that I need to do is attach a client's CSV report.
I have attempted a number of approaches, including base64 encoding the file and writing the string to disk from R to python, but not luck. That said, it seems like I am getting stuck on this error:
TypeError: Object of type 'bytes' is not JSON serializable
My code to get there is:
with open('raw/test-report.csv', 'rb') as fd:
b64data = base64.b64encode(fd.read())
attachment = Attachment()
attachment.content = b64data
attachment.filename = "your-lead-report.csv"
mail.add_attachment(attachment)
What is confusing is that if I simply replace b64data with the line
attachment.content = 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12'
an email is sent with an attachment.
For reference, I have been using:
https://github.com/sendgrid/sendgrid-python
and
kitchen sink tutorial
and haven't had any issues until this final step in my project.
Any help will be greatly appreciated. It's worth noting that my strength is in R, but I usually can hack things together in python with the help of the internets.
You need to convert b64data to a regular string before assigning it to attachment.content. Sendgrid builds a JSON payload which it sends in the requests so it doesn't know how to serialize the value assigned to attachment.content which in this case is a bytestring.
str(b64data,'utf-8')
References:
https://github.com/sendgrid/sendgrid-python/blob/master/USAGE.md#post-mailsend
Here's how to attach an in memory CSV to a sendgrid email:
import base64
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
Mail, Attachment, FileContent, FileName,
FileType, Disposition)
from io import BytesIO
import pandas as pd
def send_email_with_csv(from_address, to_address, subject, content, dataframe, filename, filetype):
message = Mail(
from_email=from_address,
to_emails=to_address,
subject=subject,
html_content=content)
#%% Create buffered csv
buffer = BytesIO()
dataframe.to_csv(buffer);
buffer.seek(0)
data = buffer.read()
encoded = base64.b64encode(data).decode()
#%%
attachment = Attachment()
attachment.file_content = FileContent(encoded)
attachment.file_type = FileType(filetype)
attachment.file_name = FileName(filename)
attachment.disposition = Disposition('attachment')
message.attachment = attachment
try:
sendgrid_client = SendGridAPIClient('API_KEY')
response = sendgrid_client.send(message)
print(response.status_code)
print(response.body)
print(response.headers)
except Exception as e:
print(e.message)
df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
send_email_with_csv(from_address='sender#sender.com',
to_address='example#recipient.com',
subject='Sending with Twilio SendGrid is Fun',
content='<strong>and easy to do anywhere, even with Python</strong>',
dataframe=df,
filename='spreadsheet.csv',
filetype='text/csv')
I am fairly new to Python and am excited that I have access to gmail using imap4
Here the code I am using to access email:
from __future__ import print_function
import getpass
import imaplib
import console
import collections
import re
import email
import codecs
import quopri
console.clear()
mail = imaplib.IMAP4_SSL('imap.gmail.com',993)
my password = getpass.getpass("Password: ")
address = 'sch.e#gmail.com'
print('Which email address (TO) would you like to search: ',end='')
EE = raw_input()
SS = r"(TO "+"\""+EE+"\""+r")"
mail.login(address, mypassword)
mail.select("inbox") #select the box on gmail
print("Checking for e-mails TO ",EE)
typ, messageIDs = mail.search(None,'(SINCE "01-Jan-2014")',SS)
MIDs=messageIDs[0].split()
for mailid in MIDs[::-1]:
resp, data = mail.fetch(mailid,'(RFC822)')
raw_body=data[0][1]
print(raw_body.decode('UTF-8','strict'))
print(quopri.encodestring(raw_body))
msg=email.message_from_string(raw_body)
print(msg)
Unfortunately none of the print statements contains correct Umlaute.
(for example Beste Grüße)
Could someone please give me a hint how to deal with encodings? It looks like Utf-8 encoded text except for the "=' characters,
Thank you!!
Erik
The email's body text has been charset-encoded into bytes that are then encoded into 7bit ASCII using MIME's quoted-printable algorithm. You will have to reverse the QP encoding to get the original bytes, and then you can convert them to a string using the email's charset (which is not utf-8, otherwise the QP encoded text would say Beste Gr=C3=BC=C3=9Fe instead. The charset is more likely iso-8859-1). The email headers will tell you the actual charset, and how the body was encoded (QP, base64, etc). However, you are fetching only the email body, so you will need to also fetch the email headers as well, using RFC822.HEADER.
Let's assume the email is encoded as ISO-8859-1 using quoted-printable (fetch the email headers to verify). Try this more like this to decode it:
raw_body=data[0][1]
raw_body=quopri.decodestring(raw_body)
raw_body=raw_body.decode('ISO-8859-1')
Is there a way to send HTML-formatted email using Python's win32com.client (which utilizes Outlook 2007/2010). The format I'm using now looks like this:
import win32com.client
olMailItem = 0x0
obj = win32com.client.Dispatch("Outlook.Application")
newMail = obj.CreateItem(olMailItem)
newMail.Subject = "the subject"
newMail.Body = "body text"
newMail.To = "recipient#example.com"
attachment1 = "c:\\mypic.jpg"
newMail.Attachments.Add(attachment1)
newMail.Send()
This will send an email using Outlook, sent from the currently authenticated user, to the specified recipient, with a subject, content, and attached image.
I want to be able to send an inline image, which can be achieved using an "Embedded" attachment, or simply to link to and image using HTML, or embed an image using HTML and a Base64-encoded image.
HTML is my preferred approach, but any HTML I add to the body is formatted and encoded as plain text (e.g. < becomes <). Is there a way to tell Outlook the body content is HTML and should be parsed as such?
This is the way to make the body in html format
newMail.HTMLBody = htmltext