Handling Bounce in Sendgrid smtp API in Python - python

I am sending marketing emails through sendgrid smtp api via python EmailMultiAlternatives. I want to know how can I handle bounces directly from there to mark particular emails as undeliverable.
The code snippet is:
def send1():
text_content = 'Hi this is the text version'
connection = get_connection(host=EMAIL_HOST,
port=EMAIL_PORT,
username=EMAIL_HOST_USER,
password=EMAIL_HOST_PASSWORD,
use_tls=EMAIL_USE_TLS)
connection.open()
subject = 'Inviting {0} to join the Business Network of SMEs'.format('surya')
html_content = template.format('Surya')
from_email = 'sp#abc.com'
to = 'abc#gmail.com'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], connection=connection)
msg.attach_alternative(html_content, "text/html")
msg.send()
connection.close()
Is it possible to get the response here only after msg.send() or is there some other way.

The best way to respond to events like blocks and bounces is to implement the event webhook.
You can also poll for the data via the bounces endpoint.

So, For those who might be looking for a solution:
I am using imaplib python package to scrape the inbox (of the mail id through which i am sending the emails) everyday for bounced emails and complaints to get those unwanted emails.
def bounce():
M = imaplib.IMAP4_SSL('imap.zoho.com')
M.login('email#emailcom', password)
M.select()
line = '(HEADER Subject "Bounce")'
typ, data = M.uid('search', line)
if typ != 'OK':
return
print(len(data[0].split()))
for i in data[0].split():
result, data = M.uid('fetch', i, '(RFC822)')
raw_email = data[0][1].decode('utf-8', 'ignore')
emg = email.message_from_string(raw_email)
w = get_first_text_block(emg)
emails = re.findall(r"[a-z0-9\.\-+_]+#[a-z0-9\.\-+_]+\.[a-z]+", str(w), re.I)
So Executing the code everyday or every hour will help you.

You can also use sendgrid. The below was written using version 6.4.1.
import sendgrid
sg = sendgrid.SendGridAPIClient(api_key="<your_key>")
payload = {
"limit": 100,
"offset": 0,
}
response = sg.client.suppression.bounces.get(query_params=payload)
query_params can be omitted to accept the defaults, there's also start_time and end_time available (need to be integer Unix times).

Related

Reading Gmail Email in Python

I am attempting to create a simple script to check my Gmail for emails with a certain title. When I run this program on Python 3.7.3 I receive this data: ('OK', [b'17']).
I need to access the body of the email within python. I am just not sure what to do with the data that I have.
Here is my current code:
import imaplib
import credentials
imap_ssl_host = 'imap.gmail.com'
imap_ssl_port = 993
username = credentials.email
password = credentials.passwd
server = imaplib.IMAP4_SSL(imap_ssl_host, imap_ssl_port)
server.login(username, password)
server.select('INBOX')
data = server.uid('search',None, '(SUBJECT "MY QUERY HERE!")')
print(data)
The result from running the code:
('OK', [b'17'])
I know it is a little rough, but I am still learning, so any advice you have to help me improve would be greatly appreciated!
You need to first list the contents of the selected mailbox and fetch one of the items. You could do something like this (also check the link suggested by #asynts)
imap.select('Inbox')
status, data = imap.search(None, 'ALL')
for num in data[0].split():
status, data = imap.fetch(num, '(RFC822)')
email_msg = data[0][1]
If you only want specific fields in the body, instead of parsing the RFC you could filter fields like:
status, data = imap.fetch(num, '(BODY[HEADER.FIELDS (SUBJECT DATE FROM TO)])')

Decoding message headers and bodies in python

I'm trying to program a simple mail client in Python which can both send and read messages from inbox in gmail. The sending part works well, but the reading part keeps giving me a bad result.
Here is the python code:
import imaplib
import base64
email_user = '<gmail_address>'
email_pass = '<password>'
M = imaplib.IMAP4_SSL('imap.gmail.com', 993)
M.login(email_user, email_pass)
M.select('inbox')
typ, message_numbers = M.search(None, 'ALL') # change variable name, and use new name in for loop
typ, data = M.fetch(b'1', '(RFC822)')
data1 = base64.b64decode(data[0][1])
print('Message \n%s' %data1)
M.close()
M.logout()
and the result is:
Message
b'\r\xe9b\xbd\xea\xdeu:1\xcaf\xa2\x97M;\x82f\xa2\x95\xca&E\xe7\x1e\x8a\xf7\x9do-\xb4\xd3f\xb5\xef\xdd\x1a\xd7Nt\xd3M4\xc2+aH\xc4\xcf\x89\xdc\xb5\xea\xfe\x9c\xb2\x9d\xb8\xd3\xae\xbd\xf2\x98\xdd2\x89\xf5\xd4W\x9b\xdbM}\xd3nv\xd7\x9d<\xd3C\xd2Mt^q\xe8\xafy\xd6\xf2\xdbM6i\xd7\xfc\xdbgp\x8a\xd8R13\xe2w\x8d\xa6\xaf^\xbb\xefo{\xf3\n\xdb\xeb}y\xe3\xdf<\xdb}\xf9\xdfM\x8c\xa2}u\x15\xe6\xf6\xd3_t\xdb\x9d\xb5\xe7O4\xd0\xf4\x93\x01\x10\x92y\xa9b\xd5\xaa\xecj\xc8Z\xdb\x9e\xad\xd7\x9e=\xf3\xcd\xb7\xdf\x97/\x9e\x89\xdev\n(\x82W\x9c\xa2k'
I appreciate any modified code and hint. Also I'm new to python so please keep your descriptions as simple as possible...
The message you receive in data[0][1] is not base64.
You want to do something like
from email import message_from_bytes
...
msg = message_from_bytes(data[0][1])
and then manipulate msg.

Receive replies from Gmail with smtplib - Python

Ok, I am working on a type of system so that I can start operations on my computer with sms messages. I can get it to send the initial message:
import smtplib
fromAdd = 'GmailFrom'
toAdd = 'SMSTo'
msg = 'Options \nH - Help \nT - Terminal'
username = 'GMail'
password = 'Pass'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username , password)
server.sendmail(fromAdd , toAdd , msg)
server.quit()
I just need to know how to wait for the reply or pull the reply from Gmail itself, then store it in a variable for later functions.
Instead of SMTP which is used for sending emails, you should use either POP3 or IMAP (the latter is preferable).
Example of using SMTP (the code is not mine, see the url below for more info):
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('myusername#gmail.com', '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
Shamelessly stolen from here
Uku's answer looks reasonable. However, as a pragmatist, I'm going to answer a question you didn't ask, and suggest a nicer IMAP and SMTP library.
I haven't used these myself in anything other then side projects so you'll need to do your own evaluation, but both are much nicer to use.
IMAP
https://github.com/martinrusev/imbox
SMTP:
http://tomekwojcik.github.io/envelopes/
I can suggest you to use this new lib https://github.com/charlierguo/gmail
A Pythonic interface to Google's GMail, with all the tools you'll
need. Search, read and send multipart emails, archive, mark as
read/unread, delete emails, and manage labels.
Usage
from gmail import Gmail
g = Gmail()
g.login(username, password)
#get all emails
mails = g.inbox().mail()
# or if you just want your unread mails
mails = g.inbox().mail(unread=True, from="youradress#gmail.com")
g.logout()

python email.parser: extract header from email

import sys, imaplib
from email.parser import HeaderParser
mail = imaplib.IMAP4_SSL(SERVER, PORT)
status, data = mail.search(None, 'ALL')
for msg_id in data[0].split():
status, message = mail.fetch(msg_id, '(RFC822)')
print message[0][1]
mail.close()
mail.logout()
I am trying to fetch emails from gmail via imap. All works fine, except that I am not able to extract header (subject, sender, date) from the message. In the above code, message[0][1] contains my email.
The only way I am able to get the header is to ask the imap server again, specifically for header:
status, message = mail.fetch(msg_id, '(BODY[HEADER.FIELDS (SUBJECT FROM)])')
parser = HeaderParser()
header = parser.parsestr(message[0][1])
print header
could somebody please advise how to do it?
I assume you've got the full email, not only the message body.
raw_message = """From: Santa Claus <santa#aintreal.no>
To: Some Dude <some#du.de>
Subject: I have lost your presents.
Dude, i am so sorry.
"""
import email
msg = email.message_from_string(raw_message)
print(msg['Subject']) # "I have lost your presents."
msg is a email.message.Message object.

Forwarding an email with python smtplib

I'm trying to put together a script that automatically forwards certain emails that match a specific criteria to another email.
I've got the downloading and parsing of messages using imaplib and email working, but I can't figure out how to forward an entire email to another address. Do I need to build a new message from scratch, or can I somehow modify the old one and re-send it?
Here's what I have so far (client is an imaplib.IMAP4 connection, and id is a message ID):
import smtplib, imaplib
smtp = smtplib.SMTP(host, smtp_port)
smtp.login(user, passw)
client = imaplib.IMAP4(host)
client.login(user, passw)
client.select('INBOX')
status, data = client.fetch(id, '(RFC822)')
email_body = data[0][1]
mail = email.message_from_string(email_body)
# ...Process message...
# This doesn't work
forward = email.message.Message()
forward.set_payload(mail.get_payload())
forward['From'] = 'source.email.address#domain.com'
forward['To'] = 'my.email.address#gmail.com'
smtp.sendmail(user, ['my.email.address#gmail.com'], forward.as_string())
I'm sure there's something slightly more complicated I need to be doing with regard to the MIME content of the message. Surely there's some simple way of just forwarding the entire message though?
# This doesn't work either, it just freezes...?
mail['From'] = 'source.email.address#domain.com'
mail['To'] = 'my.email.address#gmail.com'
smtp.sendmail(user, ['my.email.address#gmail.com'], mail.as_string())
I think the part you had wrong was how to replace the headers in the message, and the fact that you don't need to make a copy of the message, you can just operate directly on it after creating it from the raw data you fetched from the IMAP server.
You did omit some detail so here's my complete solution with all details spelled out. Note that I'm putting the SMTP connection in STARTTLS mode since I need that and note that I've separated the IMAP phase and the SMTP phase from each other. Maybe you thought that altering the message would somehow alter it on the IMAP server? If you did, this should show you clearly that that doesn't happen.
import smtplib, imaplib, email
imap_host = "mail.example.com"
smtp_host = "mail.example.com"
smtp_port = 587
user = "xyz"
passwd = "xyz"
msgid = 7
from_addr = "from.me#example.com"
to_addr = "to.you#example.com"
# open IMAP connection and fetch message with id msgid
# store message data in email_data
client = imaplib.IMAP4(imap_host)
client.login(user, passwd)
client.select('INBOX')
status, data = client.fetch(msgid, "(RFC822)")
email_data = data[0][1]
client.close()
client.logout()
# create a Message instance from the email data
message = email.message_from_string(email_data)
# replace headers (could do other processing here)
message.replace_header("From", from_addr)
message.replace_header("To", to_addr)
# open authenticated SMTP connection and send message with
# specified envelope from and to addresses
smtp = smtplib.SMTP(smtp_host, smtp_port)
smtp.starttls()
smtp.login(user, passwd)
smtp.sendmail(from_addr, to_addr, message.as_string())
smtp.quit()
Hope this helps even if this answer comes quite late.

Categories

Resources