Interaction between Python IMAPClient library and email package - python

I have a Django project where I am working on an email client. I've decided to use python's IMAPClient instead of standard library's imaplib for getting access to the messages. Currently, I don't make use of python's email package to encode/decode responses received from IMAPClient, and I have a feeling that I manually implement things that should be handled by email.
Example code for downloading attachment:
def download_attachment(server, msgid, index, encoding):
# index and encoding is known from previous analysis of bodystructure
file_content = f_fetch(server, msgid, index)
# the below code should be handled by email's message_from_bytes
# and subsequent get_payload(decode = True) function
if encoding == 'base64':
file_content = base64.b64decode(file_content)
elif ...
...
endif
#writing file_content to a folder
return
def f_fetch(server, msgid, index):
if not index:
index = '1'
response = server.fetch(msgid, 'BODY[' + index + ']')
key = ('BODY[' + index + ']').encode('utf-8')
if type(msgid) is str:
msgid = int(msgid)
return response[msgid][key]
So the question is, how should I rewrite this code to make use of email.
Specifically, what should I do with the response from IMAPClient to pass it to email's message_from_bytes() function?

If you wish to parse an email using the email package's message_from_bytes() function then you need to give it the entire, raw email body. To get this, fetch using the RFC822 selector like this:
fetch_data = server.fetch(msgid, ['RFC822'])
parsed = email.message_from_bytes(fetch_data[msgid][b'RFC822'])
If you're pulling individual message parts/attachments from the IMAP server, then the server is effectively doing the parsing work for you and you don't need to use the email package's parser.

Related

schedule email with sendgrid python

I am using python module of sendgrid (https://github.com/sendgrid/sendgrid-python) to send transactional emails. I need help with the syntax to send the email at a scheduled time.
The general sendgrid documentation asks to modify json as "{ “send_at”: 1409348513 }". I believe this json is not directly accessible in sendgrid-python. I need syntax for doing equivalent thing with the python library.
My current code is equivalent to the one copied below. It would be great if some one can suggest how to modify this code to schedule it at a particular time e.g. datetime.dateime.now() + datetime.timedelta(days=1)
import sendgrid
from sendgrid.helpers.mail import Email, Content, Substitution, Mail
import urllib2 as urllib
def send_email_custom():
sg = sendgrid.SendGridAPIClient(apikey=myApiKey)
from_email = Email(sendEmail)
to_email = Email(custEmail)
reply_to_email = Email(receiveEmail)
content = Content("text/html", "Introduction")
mail = Mail(from_email, subject="Hi!", to_email=to_email, content=content)
mail.personalizations[0].add_substitution(Substitution("_firstName_", firstName))
mail.set_template_id(templateId)
try:
response = sg.client.mail.send.post(request_body=mail.get())
except urllib.HTTPError as e:
print(e.read())
return False
if response.status_code >=200 and response.status_code < 300:
return True
else:
return False
send_at is a component of the personalizations Object, so you can define it at that level, allowing you to set distinct sending times for each recipient/personalization set.
If you don't need that, you can also set it at the top mail level.
Looks like you should be able to do that by following this example. You can specify all of the raw fields you need.
Exact code solution:
mail = Mail(from_email, subject="Hi!", to_email=to_email, content=content)
mail.send_at = SendAt(1461775053) # Time is specified in UNIX form.
# This takes advantage of Sendgrid's inbuilt email scheduler
# but you can't schedule more than 72 hours in advance.
This removes the need to deal with personalizations for send time.
Source: line 308 of https://github.com/sendgrid/sendgrid-python/blob/main/examples/helpers/mail_example.py
Posting this for those who Googled their way here in 2022 or later.

how to sign request tokens?

I am currently trying to write a script to send off a request token, I have the header, and the claimset, but I don't understand the signature! OAuth requires my private key to be encrypted with SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function), but the closest I could find was RSAES-PKCS1-v1_5 (has RSA, and the SHA-256 hash). I followed the example, and tweaked it, so I could get it set, but heres my dillema:
signature = ""
h = SHA.new (signature)
key = RSA.importKey(open('C:\Users\Documents\Library\KEY\My Project 905320c6324f.json').read())
cipher = PKCS1_v1_5.new(key)
ciphertext = cipher.encrypt(message+h.digest())
print(ciphertext)
I'm a bit lost, the JSON file I was given has both public key, and private, do I copy and paste the private key into the signature variable (it gave me a invalid syntax)? Or do I past the directory again? I am so lost, and way over my head haha. I am currently running Python 3.4, with pyCrypto for the signature.
Based on what you've said below about wanting to write a command system using gmail, I wrote a simple script to do this using IMAP. I think this is probably simpler than trying to use Google APIs for a single user, unless you were wanting to do that simply for the exercise.
import imaplib, logging
from time import sleep
USERNAME = 'YOUR_USERNAME_HERE' # For gmail, this is your full email address.
PASSWORD = 'YOUR_PASSWORD_HERE'
CHECK_DELAY = 60 # In seconds
LOGGING_FORMAT = '%(asctime)s %(message)s'
logging.basicConfig(filename='imapTest.log', format=LOGGING_FORMAT, level=logging.INFO)
logging.info("Connecting to IMAP server...")
imap = imaplib.IMAP4_SSL('imap.gmail.com')
imap.login(USERNAME, PASSWORD)
logging.info("Connected to IMAP server.")
def get_command_messages():
logging.info("Checking for new commands.")
imap.check()
# Search the inbox (server-side) for messages containing the subject 'COMMAND' and which are from you.
# Substitute USERNAME below for the sending email address if it differs.
typ, data = imap.search(None, '(FROM "%s" SUBJECT "COMMAND")' %(USERNAME))
return data[0]
def delete_messages(message_nums):
logging.info("Deleting old commands.")
for message in message_nums.split():
imap.store(message, '+FLAGS', '\\DELETED')
imap.expunge()
# Select the inbox
imap.select()
# Delete any messages left over that match commands, so we are starting 'clean'.
# This probably isn't the nicest way to do this, but saves checking the DATE header.
message_nums = get_command_messages()
delete_messages(message_nums)
try:
while True:
sleep(CHECK_DELAY)
# Get the message body and sent time. Use BODY.PEEK instead of BODY if you don't want to mark the message as read, but we're deleting it anyway below.
message_nums = get_command_messages()
if message_nums:
# search returns space-separated message IDs, but we need them comma-separated for fetch.
typ, messages = imap.fetch(message_nums.replace(' ', ','), '(BODY[TEXT])')
logging.info("Found %d commands" %(len(messages[0])))
for message in messages[0]:
# You now have the message body in the message variable.
# From here, you can check against it to perform commands, e.g:
if 'shutdown' in message:
print("I got a shutdown command!")
# Do stuff
delete_messages(message_nums)
finally:
try:
imap.close()
except:
pass
imap.logout()
If you're set on using the Gmail API, though, Google strongly encourage you to use their existing Python library rather than attempt to do full authentication etc. yourself as you appear to be. With that, it should - more or less - be a case of replacing the imap calls above with the relevant Gmail API ones.

Python 3 Reciving email problems

I'm writing a script to receive emails from my gmail email in python. I'm managing to download the raw email however I am then unable to access certain types of it, E.G BODY, TO, FROM etc.
import imaplib, email
msrvr = imaplib.IMAP4_SSL('imap.gmail.com', 993)
unm = 'stackoverflow#gmail.com'
pwd = 'lovetocode'
msrvr.login(unm,pwd)
stat,cnt = msrvr.select('Inbox')
stat, dta = msrvr.fetch(cnt[0], '(RFC822)')
b = email.message_from_string(str(dta))
print(b)
print(b['[To]'])
msrvr.close()
msrvr.logout()
Where am I going wrong?
You might find it easier to use native Python Google SDK's for working with their email:
https://developers.google.com/appengine/docs/python/mail/
The imaplib module you are using is will only give you a subset of all gmail features..
Here's some code that parses an email and prints some header fields:
msg = email.message_from_string(raw_email)
for field in ('From', 'Subject', 'Received', 'Message-ID'):
print '{0}: {1}'.format(field, msg[field])
For debugging, also print the raw parts of the Message object:
print msg.__dict__
(Note: I'm using Python2.7, but I believe there's not much difference.)

Email body text?

hi everyone I am using a script which involves:
import oauth2 as oauth
import oauth2.clients.imap as imaplib
import email
conn = imaplib.IMAP4_SSL('imap.googlemail.com')
conn.debug = 4
# This is the only thing in the API for impaplib.IMAP4_SSL that has
# changed. You now authenticate with the URL, consumer, and token.
conn.authenticate(url, consumer, token)
# Once authenticated everything from the impalib.IMAP4_SSL class will
# work as per usual without any modification to your code.
conn.select('[Gmail]/All Mail')
response, item_ids = conn.search(None, "SINCE", "01-Jan-2011")
item_ids = item_ids[0].split()
# Now iterate through this shit and retrieve all the email while parsing
# and storing into your whatever db.
for emailid in item_ids:
resp, data = conn.fetch(emailid, "(RFC822)")
email_body = data[0][1]
mail = email.message_from_string(email_body)
My current problem is that I can't seem to be able to retrieve the body of the mail instance. I am able to see the content of the email by printing it or mail.as_string() but then even with mail.keys() and mail.values() i am actually unable to see the mail's content (the main message).
What is wrong with this email lib API? (or rather what am I doing wrong)?
From email docs:
You can pass the parser a string or a file object, and the parser will
return to you the root Message instance of the object structure.
For simple, non-MIME messages the payload of this root object will
likely be a string containing the text of the message. For MIME
messages, the root object will return True from its is_multipart()
method, and the subparts can be accessed via the get_payload() and
walk() methods.
So use get_payload() or if the message is multipart then call walk() method and then use get_payload() on a desirable subpart.

How can I extract only the email body with Python using IMAP?

I am relatively new to programming and to python, but I think I have done ok so far. This is the code I have, and it works fine, except it gets the entire message in MIME format. I only want the text body of unread emails, but I can't quite figure it out how to strip out all of the formatting and header info. If I send a basic email using a smtp python script that I made it works fine, and only prints the body, but if I send the email using outlook it prints a bunch of extra garbage. Any help is very much appreciated.
client = imaplib.IMAP4_SSL(PopServer)
client.login(USER, PASSWORD)
client.select('INBOX')
status, email_ids = client.search(None, '(UNSEEN SUBJECT "%s")' % PrintSubject)
print email_ids
client.store(email_ids[0].replace(' ',','),'+FLAGS','\Seen')
for email in get_emails(email_ids):
get_emails()
def get_emails(email_ids):
data = []
for e_id in email_ids[0].split():
_, response = client.fetch(e_id, '(UID BODY[TEXT])')
data.append(response[0][1])
return data
Sounds like you're looking for the email package:
The email package provides a standard parser that understands most email document structures, including MIME documents. You can pass the parser a string or a file object, and the parser will return to you the root Message instance of the object structure. For simple, non-MIME messages the payload of this root object will likely be a string containing the text of the message. For MIME messages, the root object will return True from its is_multipart() method, and the subparts can be accessed via the get_payload() and walk() methods.

Categories