i'm writing a python script which reads emails from Outlook then extract the body
The problem is that when it reads an email answer, the body contains the previous emails.
Is there away to avoid that and just extract the body of the email.
This is a part of my code :
import requests
import json
import base64
utlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders.Item("UGAP-AMS-L2")
inbox = folder.Folders.Item("Inbox")
mails = inbox.Items
mails.Sort("[ReceivedTime]", False)
for mail in mails:
if mail.UnRead == True :
print(" mail.Body")
This is what i get :
-Email body of the current email-
De : "tracker#gmail.fr" tracker#gmail.fr
Date : vendredi 21 mai 2021 à 08:44
À : Me Me#outlook.com
Objet : object
-body of previous email-
The Outlook object model (nor MAPI) doesn't provide anything for that.
There is no universal solution or specific property or method for getting the job done. You may try to find your own algorithm of extracting the latest content from the message body.
You would have to parse the body yourself to cut off anything you don't want.
What you want is not really possible - message body is a free-form text, the user is allowed to type anywhere. I personally do that all the time - I just type "see below" and insert my comments in the original email. There is no way to separate the two.
Related
So I know how to look through the inbox (or any other folder) and find emails to reply to. However in my case, I have a .msg email file from which I extract the MessageID, and I'm looking to use win32com module to reply to that specific email.
Basically I'm looking for something like this:
from extract_msg import Message
msg = Message("message.msg")
outlook = win32com.client.Dispatch('outlook.application')
mail = outlook.CreateItem(0x0)
mail.To = "; ".join(to)
mail.Subject = subject
mail.Body = body
mail.InReplyTo = msg.messageId
I understand that something similar is doable using the smtplib module using:
message['In-Reply-To'] = msg.messageId
but I cannot get smtplb to work with Outlook. And thus, I'm using win32com.
The PR_IN_REPLY_TO_ID property should be set to the PR_INTERNET_MESSAGE_ID property value. Make sure that such value exists in Outlook messages. You can get the value in Outlook in the following way (the sample is in C# but the Outlook object model is common for all kind of programming languages):
string PR_INTERNET_MESSAGE_ID = "http://schemas.microsoft.com/mapi/proptag/0x1035001F";
Microsoft.Office.Interop.Outlook.PropertyAccessor pal = mailItem.PropertyAccessor;
string Internet_Message_Id = pal.GetProperty(PR_INTERNET_MESSAGE_ID).ToString();
You need to set the PR_IN_REPLY_TO_ID MAPI property (DASL name "http://schemas.microsoft.com/mapi/proptag/0x1042001F") using MailItem.PropertyAccessor.SetProperty.
i'm using python 3, and want to validate emails sent to my inbox
im using imaplib,
i've managed to get the email content,
however, the mail is unreadable and kind of corrupted ( variable html123 in code)
after i'm fetching the mail, and getting the content using :
mail_body = email.message_from_string(str(data[1][0][1], 'utf-8'))
this is the original mail i see in mailbox:
dear blabla,
We’ve added new tasks to your account. Please log in to your account to review and.....
this is the mail i get in python
| |
dear blabla,
We=E2=80=99ve added new tasks to your account. Plea= se log in to your account....
so 3 issues in this example, i have much more in the real mail:
1 -the ' was replaced with =E2=80=99
2- the word please cut at end of line, with =
3 -all the signs\char || --- you see above
this is the relevant part in code:
data = self.mail_conn.fetch(str(any_email_id), f'({fetch_protocol})')
mail_body = email.message_from_string(str(data[1][0][1], 'utf-8'))
html123 = mail_body.get_payload()
x1 = (html2text.html2text(html123))
The data you get from imaplib is in "quoted-printable" encoding. https://en.wikipedia.org/wiki/Quoted-printable
To decode you can use the builtin quopri module
import quopri
quopri.decodestring("we=E2=80=99ve").decode() # -> we've
I'm sending emails with pyramid_mailer and found this weird issue that when I use Office365 as my SMTP server it adds random = characters into my message. I don't get that issue with any other mail server (I tested this with gmail and also with my own postfix server)
I send emails like below:
from pyramid_mailer.mailer import Mailer
from pyramid_mailer.message import Attachment, Message
mailer = Mailer()
mailer.smtp_mailer.hostname = "test.mail.at.office365"
mailer.smtp_mailer.username = "my_user"
mailer.smtp_mailer.password = "secret"
mailer.smtp_mailer.port = 587
mailer.smtp_mailer.tls = True
message = Message(
subject="Test",
sender="my_user#my_domain.com",
recipients="test_user#test_domain.com",
body="very long text, at least 75 characters long so Office 365 will break it and insert annoying '=' into message",
html="very long text, at least 75 characters long so Office 365 will break it and insert annoying '=' into message",
)
mailer.send_immediately(message)
I searched on google and found this has something to do with line breaks and Transfer-Content-Encoding. And indeed, if I add \r\n every ~50 characters I won't see = added. But the problem is that I might want to send a hyperlink that will be longer than that...
Pyramid documentation (https://docs.pylonsproject.org/projects/pyramid_mailer/en/latest/) says I can use Attachment rather than plain string. And indeed as soon as I do that I can set this Transfer-Content-Encoding to something like base64 (as suggested here: https://jeremytunnell.com/2009/01/04/really-hairy-problem-with-seemingly-random-crlf-and-spaces-inserted-in-emails/) but my message then shows as attachment, not as regular message...
There seems to be no way to add this Transfer-Content-Encoding to Message object... I tried to use Message.extra_headers = {'Transfer-Content-Encoding': 'base64'} but this did not help.
I'm totally out of ideas, would appreciate any help...
-- Edit --
Thanks to answer below from #Mess:
from pyramid_mailer.message import Attachment
my_message = "very long text, at least 75 characters long so Office 365 will break it and insert annoying '=' into message"
body_html = Attachment(data=my_message, transfer_encoding="base64", disposition='inline')
body_text = Attachment(data=my_message, transfer_encoding="base64", disposition='inline')
Then pass body_html and body_text to Message constructor.
This is "Content-Disposition" header you need to set to control how the content is available to the recipient.
Set it to "attachment" to let download the file, use "inline" to be able to include the content, for example a logo, directly to your email, etc:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
I hope it will point you to the right direction.
EDIT:
Using pyramid_mailer package it would be something like:
from pyramid_mailer.message import Attachment
attachment = Attachment(data=some_data, transfer_encoding="base64", disposition='inline')
I am trying to use templates which I have created on Sendgrid. What I want to do is, use the template that I have created on Sendgrid to send an email (with substituting the substitution tags).
Here is what I tried -
import sendgrid
sg = sendgrid.SendGridClient('<username>', '<password>')
message = sendgrid.Mail()
message.add_to('<to-email-address>')
message.set_subject('My Test Email')
message.add_substitution('customer_link', 'http://my-domain.com/customer-id')
message.add_filter('templates', 'enable', '1')
message.add_filter('templates', 'template_id', '<alphanumeric-template-id>')
message.add_from('<from-email-address>')
status, msg = sg.send(message)
However, this gives me an error that '{"errors":["Missing email body"],"message":"error"}'. I tried adding the following lines but still the same error -
message.set_html('')
message.set_text('')
So, the exact code I am using after using set_html is -
import sendgrid
sg = sendgrid.SendGridClient('<username>', '<password>')
message = sendgrid.Mail()
message.add_to('<to-email-address>')
message.set_subject('My Test Email')
message.add_substitution('customer_link', 'http://my-domain.com/customer-id')
message.add_filter('templates', 'enable', '1')
message.add_filter('templates', 'template_id', '<alphanumeric-template-id>')
message.add_from('<from-email-address>')
message.set_html('')
message.set_text('')
status, msg = sg.send(message)
What am I missing here? The python documentation for the library doesn't seem to contain anything.
Edit - One of the workaround that I found is derived from the answer below. I do set message.set_html(' ') and message.set_text(' '), that is basically, give a whitespace string. This is weird, though, but works for this case.
The official documentation it is indeed poor, since it just mentions an example. However, it provides you a link to its Github sendgrid-python repository in which you can find broader examples and the code itself (sometimes it is faster to look at this!).
Find here a full example listed in the Github page:
import sendgrid
sg = sendgrid.SendGridClient('YOUR_SENDGRID_USERNAME', 'YOUR_SENDGRID_PASSWORD')
message = sendgrid.Mail()
message.add_to('John Doe <john#email.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Doe John <doe#email.com>')
status, msg = sg.send(message)
#or
message = sendgrid.Mail(to='john#email.com', subject='Example', html='Body', text='Body', from_email='doe#email.com')
status, msg = sg.send(message)
However, in this case you are using a template and in Sendgrid's blog they mention:
Template Engine Demo – Q&A
Do I have to use the body and subject substitution tags in my
template? What If I don’t want to pass any substitution variables in
my email?
No. You can pass a blank value through with your API call and not
specify any variables.
although apparently it is not like this and you need to append some kind of html and text in your message instance.
So the workaround here is what you discovered: to set the html and text to a non-empty string:
message.set_html(' ')
message.set_text(' ')
I am trying to write a python script to send an email that uses html formatting and involves a lot of non-breaking spaces. However, when I run it, some of the   strings are interrupted by spaces that occur every 171 characters, as can be seen by this example:
#!/usr/bin/env python
import smtplib
import socket
from email.mime.text import MIMEText
emails = ["my#email.com"]
sender = "test#{0}".format(socket.gethostname())
message = "<html><head></head><body>"
for i in range(20):
message += " " * 50
message += "<br/>"
message += "</body>"
message = MIMEText(message, "html")
message["Subject"] = "Test"
message["From"] = sender
message["To"] = ", ".join(emails)
mailer = smtplib.SMTP("localhost")
mailer.sendmail(sender, emails, message.as_string())
mailer.quit()
The example should produce a blank email that consists of only spaces, but it ends up looking something like this:
  ;
&nb sp;
& nbsp;
&nbs p;
&n bsp;
Edit: In case it is important, I am running Ubuntu 15.04 with Postfix for the smtp client, and using python2.6.
I can replicate this in a way but my line breaks come every 999 characters. RFC 821 says maximum length of a line is 1000 characters including the line break so that's probably why.
This post gives a different way to send a html email in python, and i believe the mime type "multipart/alternative" is the correct way.
Sending HTML email using Python
I'm the developer of yagmail, a package that tries to make it easy to send emails.
You can use the following code:
import yagmail
yag = yagmail.SMTP('me#gmail.com', 'mypassword')
for i in range(20):
message += " " * 50
message += "<br/>"
yag.send(contents = message)
Note that by default it will send a HTML message, and that it also adds automatically the alternative part for non HTML browsers.
Also, note that omitting the subject will leave an empty subject, and without a to argument it will send it to self.
Furthermore, note that if you set yagmail up correctly, you can just login using yag.SMTP(), without having to have username & password in the script (while still being secure). Omitting the password will prompt a getpass.
Adding an attachment is as simple as pointing to a local file, e.g.:
yag.send(contents = [message, 'previously a lot of whitespace', '/local/path/file.zip']
Awesome isn't it? Thanks for the allowing me to show a nice use case for yagmail :)
If you have any feature requests, issues or ideas please let me know at github.