Fetch an email with imaplib but do not mark it as SEEN - python

I want to parse some emails from a user 's inbox but when I do:
typ, msg_data = imap_conn.fetch(uid, '(RFC822)')
It marks the email as SEEN or read. This is not the desired functionality. Do you know how can I keep the email at its previous stare either SEEN or NOT SEEN?

You might also set read_only to true when selecting the folder:
imap_conn.select('Inbox', readonly=True)

The following should work:
typ, msg_data = imap_conn.fetch(uid, '(BODY.PEEK[HEADER])')
or BODY.PEEK[TEXT], etc.

You can use (RFC822.PEEK) as the "message-parts" argument, according to RFC 1730 (I have not verified which servers actually implement that correctly, but it doesn't seem hard for them to).

You may use imap_tools package:
https://pypi.org/project/imap-tools/
from imap_tools import MailBox, Q
# get list of email subjects from INBOX folder
with MailBox('imap.mail.com').login('test#mail.com', 'password') as mailbox:
# mark_seen=False - not mark emails as seen on fetch
subjects = [msg.subject for msg in mailbox.fetch(mark_seen=False)]

Related

How to mark an email as seen after reading it with 'mark_seen = False' in imap_tools python?

So first of all this is my code:
with MailBox('imap.gmail.com').login('username', 'password', 'INBOX') as mailbox:
for msg in mailbox.fetch(AND(mark_seen=False, from_="some_domain")):
prt_msg_text = msg.text
if 'some_text' in prt_msg_text:
*here I want to mark the email as seen, if a specific text is in the msg*
So I want to read the email first without marking it as seen and if a specific condition is true, I want to mark this specific email as seen. Is that possible to do?
mark_seen is fetch's argument.
When you fix this error, use mark_seen arg and mailbox.flag method
I am new to it aswell, but I'm good at googling.
from imap_tools import MailBox, A
with MailBox('imap.gmail.com').login('username', 'pwd', 'INBOX') as mailbox:
uids = []
for msg in mailbox.fetch(A(from_="#some.domain"), mark_seen=False):
body = msg.text or msg.html
if 'some_text' in body:
uids.append(msg.uid)
mailbox.flag(uids, imap_tools.MailMessageFlags.SEEN, True)
*mark_seen is fetch's argument.

How to send a draft email that I've prepared in several to several contacts in gmail one by one?

I have prepared a draft email in Gmail that I need to send to several people but Gmail does not allow me to. The draft email has text, an image in it, and formatting.
I'd ideally want to send the draft to my list of contacts one by one just changing who it's addressed to. I can put the list of contacts in one column of excel and the name in another.
I could also just make the draft start with "Dear sir/madam" and send the same draft to my contacts without any modification.
If the body of my email was just text I guess I'd just use SMTP, but with all the formatting, and image in it I don't know what to do.
What is the easiest way to do this? Would using Selenium make sense? Anything else that's better?
Thanks
You could use a JavaMail API to send email.
For formatting you can use the html formatting inside your code, which serves like your draft.
Read the contacts from a file and replace the variable in "Dear" $varName.
And to trigger it multiple times you could use java.util.Timer class.
Hope it helps.
Here's what worked for me to send an email using python. I turned my email content into html and saved in a file and named it emailcontent. Then I used it in the following code:
import smtplib
from email.message import EmailMessage
from emailcontent import emailcontent
from config import email, password
email_list = ["user#gmail.com", "user2#yahoo.ca",
"user3#gmx.us", "user4#u.ca"]
for useremail in email_list:
msg = EmailMessage()
msg['Subject'] = 'Test'
msg['From'] = email
msg['To'] = useremail
msg.set_content('fallback')
msg.add_alternative(emailcontent, subtype='html')
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
smtp.login(email, password)
smtp.send_message(msg)

How to get sender mailbox from email.message.Message object?

First I connect to my own mailbox using such code:
import email
import imaplib
imap_server = imaplib.IMAP4_SSL('imap.gmail.com', 993)
imap_server.login("myemail", "mypassword")
imap_server.select('inbox')
Then I get the last email on it in the way like:
_, data = imap_server.search(None, 'ALL')
id_list = data[0].split()
_, data = imap_server.fetch(id_list[-1], '(RFC822)')
rawmail = data[0][1]
prev_mail = email.message_from_bytes(rawmail)
And after that, I need to get sender email from prev_mail variable.
I tried it myself a little bit, and figured out that prev_mail['From'] is usually sth like this: "John Snow" <johnsnow#gmail.com>, so that means I can just grab the <*> part and use it.
But the question is: will it always be like this so I could use my method? And are there any better ways to do this rather than mine?
EDIT:
There are multiple variants of From: headers. Python 3.8 has a good parser for this in the email.header module. But perhaps the Return-Path: header would actually be closer to what you really want? - tripleee
Thank you very much, tripleee, that's exactly what I needed!

IMAP get sender name and body text?

I am using this code:
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(myusername, mypassword)
mail.list()
# Out: list of "folders" aka labels in gmail.
mail.select("inbox") # connect to inbox.
result, data = mail.search(None, "ALL")
ids = data[0] # data is a list.
id_list = ids.split() # ids is a space separated string
latest_email_id = id_list[-1] # get the latest
result, data = mail.fetch(latest_email_id, "(RFC822)") # fetch the email body (RFC822) for the given ID
raw_email = data[0][1] # here's the body, which is raw text of the whole email
# including headers and alternate payloads
print raw_email
and it works, except, when I print raw_email it returns a bunch of extra information, how can I, parse, per say, the extra information and get just the From and body text?
Python's email package is probably a good place to start.
import email
msg = email.message_from_string(raw_email)
print msg['From']
print msg.get_payload(decode=True)
That should do ask you ask, though when an email has multiple parts (attachments, text and HTML versions of the body, etc.) things are a bit more complicated.
In that case, msg.is_multipart() will return True and msg.get_payload() will return a list instead of a string. There's a lot more information in the email.message documentation.
Alternately, rather than parsing the raw RFC822-formatted message - which could be very large, if the email contains attachments - you could just ask the IMAP server for the information you want. Changing your mail.fetch line to:
mail.fetch(latest_email_id, "(BODY[HEADER.FIELDS (FROM)])")
Would just request (and return) the From line of the email from the server. Likewise setting the second parameter to "(UID BODY[TEXT])" would return the body of the email. RFC2060 has a list of parameters that should be valid here.
IMAP high level lib: https://github.com/ikvk/imap_tools (I am author)
from imap_tools import MailBox, A
with MailBox('imap.mail.com').login('test#mail.com', 'password', 'INBOX') as mailbox:
for msg in mailbox.fetch(A(all=True)):
sender = msg.from_
body = msg.text or msg.html
Alternatively, you can use Red Box (I'm the author):
from redbox import EmailBox
# Create email box instance
box = EmailBox(
host="imap.example.com",
port=993,
username="me#example.com",
password="<PASSWORD>"
)
# Select an email folder
inbox = box["INBOX"]
# Search and process messages
for msg in inbox.search(all=True):
# Process the message
print(msg.from_)
print(msg.to)
print(msg.subject)
print(msg.text_body)
print(msg.html_body)
Some relevant links in the documentations:
More about querying
More about manipulating the message
More about configuring the email box
To install:
pip install redbox
Links:
Source code
Documentation

Get sender email address with Python IMAP

I have this python IMAP script, but my problem is that, every time I want to get the sender's email address, (From), I always get the sender's first name followed by their email address:
Example:
Souleiman Benhida <souleb#gmail.com>
How can i just extract the email address (souleb#gmail.com)
I did this before, in PHP:
$headerinfo = imap_headerinfo($connection, $count)
or die("Couldn't get header for message " . $count . " : " . imap_last_error());
$from = $headerinfo->fromaddress;
But, in python I can only get the full name w/address, how can I get the address alone? I currently use this:
typ, data = M.fetch(num, '(RFC822)')
mail = email.message_from_string(data[0][1])
headers = HeaderParser().parsestr(data[0][1])
message = parse_message(mail) #body
org = headers['From']
Thanks!
Just one more step, using email.utils:
email.utils.parseaddr(address)
Parse address – which should be the value of some address-containing field such as To or Cc – into its constituent realname and email address parts. Returns a tuple of that information, unless the parse fails, in which case a 2-tuple of ('', '') is returned.
Note: originally referenced rfc822, which is now deprecated.
to = email.utils.parseaddr(msg['cc'])
This works for me.
My external lib https://github.com/ikvk/imap_tools
let you work with mail instead read IMAP specifications.
from imap_tools import MailBox, A
# get all emails from INBOX folder
with MailBox('imap.mail.com').login('test#mail.com', 'pwd', 'INBOX') as mailbox:
for msg in mailbox.fetch(A(all=True)):
print(msg.date, msg.from_, msg.to, len(msg.text or msg.html))
msg.from_, msg.to - parsed addresses, like: 'Sender#ya.ru'
I didn't like the existing solutions so I decided to make a sister library for my email sender called Red Box.
Here is how to search and process emails including getting the from address:
from redbox import EmailBox
# Create email box instance
box = EmailBox(
host="imap.example.com",
port=993,
username="me#example.com",
password="<PASSWORD>"
)
# Select an email folder
inbox = box["INBOX"]
# Search and process messages
for msg in inbox.search(unseen=True):
# Process the message
print(msg.from_)
print(msg.to)
print(msg.subject)
print(msg.text_body)
print(msg.html_body)
# Flag the email as read/seen
msg.read()
I also wrote extensive documentation for it. It also has query language that fully supports nested logical operations.

Categories