How to tell if an email sent via Gmail REST API has bounced?

帅比萌擦擦* 提交于 2019-12-03 06:33:01

Messages that gets bounced when sent through the Gmail API gets a response from the mailer deamon (mailer-daemon@googlemail.com). You could continually check the user's messages to see if a new message from the daemon has been received.

Make sure to store the timestamp in seconds since your last check, so you don't get any nasty duplicates next time around.

query = from:mailer-daemon@googlemail.com after:<TIME_SINCE_EPOCH_IN_SECONDS>

GET https://www.googleapis.com/gmail/v1/users/me/messages?q=from%3Amailer-daemon%40googlemail.com+after%3A1437055051&access_token={YOUR_API_KEY}

Response:

{
 "messages": [
  {
   "id": "14e97f7ed03b7e88",
   "threadId": "14e97f7ea9b794a4"
  },
 ]
}

I got a bounce! Let's fetch the entire mail and decode it and get the Message-ID you were alluding too.

GET https://www.googleapis.com/gmail/v1/users/me/messages/14e97f7ed03b7e88?fields=payload%2Fbody%2Fdata&access_token={YOUR_API_KEY}

Response:

{
 "payload": {
  "body": {
   "data": "RGVsA0K..."
  }
 }
}

Converting the mail to regular base64 from its URL safe version (replace all "-" with "+" and "_" with "/"), and base64-decoding it we get:

atob("RGVsA0K...".replace(/\-/g, '+').replace(/\_/g, '/'));

Decoded mail:

"Delivery to the following recipient failed permanently:

     sadsadsadas@sadsads.asdsad

Technical details of permanent failure: 
DNS Error: Address resolution of sadsads.asdsad. failed: Domain name not found

----- Original message -----

.
.
.

Received: from 292824132082.apps.googleusercontent.com named unknown by
 gmailapi.google.com with HTTPREST; Thu, 16 Jul 2015 13:44:43 -0400
from: example@gmail.com
Date: Thu, 16 Jul 2015 13:44:43 -0400
Message-ID: <this_is_it@mail.gmail.com>
Subject: Subject Text
To: sadsadsadas@sadsads.asdsad
Content-Type: text/plain; charset=UTF-8

The actual message text goes here

Here we have the Message-ID! Let's get the bounced email!

query = rfc822msgid:<this_is_it@mail.gmail.com>;

GET https://www.googleapis.com/gmail/v1/users/me/messages?q=rfc822msgid%3A%3CCADsZLRzOs1wT4B5pgR7oHHdbjkQhuaCQQs8CEckhLwVw73QFEQ%40mail.gmail.com%3E&key={YOUR_API_KEY}

Response:

{
 "messages": [
  {
   "id": "14e97f7ea9b794a4", // <-- Here is the message that bounced!
   "threadId": "14e97f7ea9b794a4"
  }
 ],
}

When you send a message via

service.users().messages().send(userId, message).execute();

It will return a Message. You can use its threadId to check if you got any reply to that message.

Here's an easy way of checking if it bounced (give a 1 second delay after sending):

public static boolean isBounced(Gmail service, String threadId) throws IOException {
    List<Message> list = service.users().messages().list("me")
                        .setQ("from=mailer-daemon@googlemail.com")
                       .execute().getMessages();

    return list.stream().anyMatch(msg -> msg.getThreadId().equals(threadId));
}

This is how you can do it using backend technologies and trying to follow current documentation guides. Since it appears after: and before: tags only support a date not date + time. So whilst above EPOCH time may have worked on earlier code more work is required in current API. Some of this below yet to be added to a test project I have on github. But to give an idea:

We are asking it to look back for any bounced messages older than a day, then parsing headers to find received date - converting string to real date and comparing -20 minutes ago from results anything older not added to the listing

List verifyBounceList (Gmail service) {
        List foundResults=[]

        Date date = new Date()
        use (groovy.time.TimeCategory) {
            date= date -20.minute
        }
        Date yesterday = new Date()-1
        def bounceRecords = listMessagesMatchingQuery(service,'me','from:mailer-daemon@googlemail.com after:'+yesterday.format('YYYY/MM/dd'))
        bounceRecords?.each {
            Message message = getMessage(service,'me',it.id)
            String receivedDateString = message.getPayload().headers?.find{it.name=='Received'}.value.split(';')[1].trim()
            SimpleDateFormat df = new SimpleDateFormat('EEE, dd MMM yyyy HH:mm:ss z (Z)')
            Date receivedDate=df.parse(receivedDateString)
            if (receivedDate>date) {
                foundResults<<[bouncedRecord:it,mapRecord:message]
            }
        }
        return foundResults
    }

Message getMessage(Gmail service, String userId, String messageId) throws IOException {
    Message message = service.users().messages().get(userId, messageId).execute()
    ///System.out.println("Message snippet: " + message.getSnippet())
    return message
}
    /**
 * Simply does a query in given mailbox 
 * used to query for mail failures
 * @param service
 * @param userId
 * @param query
 * @return
 * @throws IOException
 */
List<Message> listMessagesMatchingQuery(Gmail service, String userId, String query) throws IOException {
    ListMessagesResponse response = service.users().messages().list(userId).setQ(query).execute()

    List<Message> messages = new ArrayList<Message>()
    while (response.getMessages() != null) {
        messages.addAll(response.getMessages())
        if (response.getNextPageToken() != null) {
            String pageToken = response.getNextPageToken()
            response = service.users().messages().list(userId).setQ(query).setPageToken(pageToken).execute()
        } else {
            break;
        }
    }
    for (Message message : messages) {
        //System.out.println(message.toPrettyString());
    }

    return messages;
}

Above is returned as a list iterating through returned results:

<g:if test="${instance.size()>0}"> 
        <h2>Bounces found : ${instance.size()}</h2><br/>
        <div class="errors">
        <g:each in="${instance}" var="failed">
            ${failed.bouncedRecord} --> ${failed.mapRecord?.id} ${failed.mapRecord?.getSnippet()} ${ }<br/>
            <g:each in="${failed.mapRecord.getPayload().headers}" var="a">
            ${a }<br/>---
            </g:each>
        </g:each>
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!