Detect bounced emails in Python smtplib
Asked Answered
N

2

9

I'm trying to catch all emails that bounced when sending them via smtplib in Python. I looked at this similar post which suggested adding an exception catcher, but I noticed that my sendmail function doesn't throw any exceptions even for fake email addresses.

Here is my send_email function which uses smtplib.

def send_email(body, subject, recipients, sent_from="[email protected]"):
    msg = MIMEText(body)

    msg['Subject'] = subject
    msg['From'] = sent_from
    msg['To'] = ", ".join(recipients)

    s = smtplib.SMTP('mySmtpServer:Port')
    try:
       s.sendmail(msg['From'], recipients, msg.as_string())
    except SMTPResponseException as e:
        error_code = e.smtp_code
        error_message = e.smtp_error
        print("error_code: {}, error_message: {}".format(error_code, error_message))
    s.quit()

Sample call:

send_email("Body-Test", "Subject-Test", ["[email protected]"], "[email protected]")

Since I set the sender as myself, I am able to receive the email bounce report in my sender's inbox:

<[email protected]>: Host or domain name not found. Name service error
    for name=jfdlsaf.com type=A: Host not found

Final-Recipient: rfc822; [email protected]
Original-Recipient: rfc822;[email protected]
Action: failed
Status: 5.4.4
Diagnostic-Code: X-Postfix; Host or domain name not found. Name service error
    for name=jfdlsaf.com type=A: Host not found

Is there a way to get the bounce message through Python?

Newcomb answered 1/11, 2016 at 22:33 Comment(1)
Maybe use poplib to open your email box that your bounce reports would be sent to?Kordula
K
2
import poplib
from email import parser

#breaks with if this is left out for some reason (MAXLINE is set too low by default.)
poplib._MAXLINE=20480

pop_conn = poplib.POP3_SSL('your pop server',port)
pop_conn.user(username)
pop_conn.pass_(password)
#Get messages from server:
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]

# Concat message pieces:
messages = ["\n".join(mssg[1]) for mssg in messages]
#Parse message intom an email object:
messages = [parser.Parser().parsestr(mssg) for mssg in messages]
for message in messages:
    if "Undeliverable" in message['subject']:

        print message['subject']
        for part in message.walk():
            if part.get_content_type():
                body = str(part.get_payload(decode=True))

                bounced = re.findall('[a-z0-9-_\.]+@[a-z0-9-\.]+\.[a-z\.]{2,5}',body)
                if bounced:

                    bounced = str(bounced[0].replace(username,''))
                    if bounced == '':
                        break

                    print bounced 

Hope this helps. This will check the contents of the mailbox for any undeliverable reports and read the message to find the email address that bounced.It then prints the result

Kordula answered 23/9, 2017 at 21:21 Comment(0)
S
0

You can use this function for gmail, basically checking the inbox that whether there is any email from Mail Delivery Subsystem with particular Text in body

def is_email_bounced(email, app_password, email_in_question):
    obj = imaplib.IMAP4_SSL('imap.gmail.com',993)
    obj.login(email, app_password)
    obj.select() # Default Inbox

    typ, data = obj.search(None, f'(TEXT "{email_in_question}") (Subject "Delivery Status Notification (Failure)")')

    try: data.remove(b'')
    except: pass 

    if len(data) >= 1: return True 
    else: return False 

run this function after sending the email, and check whether email got bounced or not

e.g.

send_email("Body-Test", "Subject-Test", ["[email protected]"], "[email protected]")
if is_email_bounced("[email protected]", "app_password", "[email protected]"):
    print("Email Bounced")

Change email Subject "Delivery Status Notification (Failure)" as per your email service provider, for this code, I've considered email provider as gmail

Salmanazar answered 27/10, 2022 at 16:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.