如何判断通过Gmail REST API发送的电子邮件是否已退回?

时间:2015-05-22 03:08:25

标签: gmail gmail-api

我通过Gmail API发送电子邮件,想知道邮件何时反弹。我怎么能这样做?

根据我的理解,退回的电子邮件通常包含某种标题,表示反弹,例如:

X-Failed-Recipients: zzzzzzzzzasdfasdfadfa@gmail.com

然而,似乎并不总是一个标题,表明它被弹回的原始消息ID。

我在想下面的计划,但是我认为我必须接近这个错误的洞。

  1. 通过Gmail API发送电子邮件(发送到失败的电子邮件)--->它顺利通过
  2. 接收电子邮件退回电子邮件收件箱
  3. 扫描包含退回标题的电子邮件的电子邮件
  4. 尝试找出被退回的原始电子邮件。
  5. 问题

    • Gmail api会返回Gmail邮件ID,而非实际邮件ID
    • 必须持续监控/轮询收件箱以查看是否有退回的电子邮件
    • 是否可以通过标题进行搜索?
    • 每个电子邮件提供商似乎都有不同的退回标题
    • 标题可能不表示原始消息ID

    我还有其他一些想法:

    • 使用字符串" Undeliverable"搜索电子邮件在这个主题?
    • 不使用gmail rest api进行发送,因为跳出跟踪是不可行的。也许使用SMTP api而不是?

3 个答案:

答案 0 :(得分:13)

通过Gmail API发送时被退回的邮件会收到邮件守护程序(mailer-daemon@googlemail.com)的回复。您可以不断检查用户的消息,以查看是否已收到来自守护程序的新消息。

确保自上次检查后以秒为单位存储时间戳,因此下次不会收到任何令人讨厌的副本。

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}

<强>响应:

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

我反弹了!让我们获取整个邮件并对其进行解码并获取您正在暗示的消息ID。

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

<强>响应:

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

将邮件从其URL安全版本转换为常规base64(替换所有&#34; - &#34;使用&#34; +&#34;和&#34; _&#34;替换为&#34; /&#34;),以及我们得到的base64解码:

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

已解码的邮件:

"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

这里我们有Message-ID!让我们收到退回的电子邮件!

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}

<强>响应:

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

答案 1 :(得分:1)

通过

发送消息时
service.users().messages().send(userId, message).execute();

它将返回Message。您可以使用其threadId来检查您是否收到该邮件的任何回复。

这是一种检查它是否反弹的简单方法(发送后延迟1秒):

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));
}

答案 2 :(得分:0)

这是使用后端技术并尝试遵循当前文档指南的方法。由于它出现在:之前:之前:标签仅支持日期而非日期+时间。因此,虽然EPOCH时间可能已经在早期的代码上工作,但是在当前的API中需要更多的工作。下面的一些内容尚未添加到github上的测试项目中。但是要提出一个想法:

我们要求它回顾任何超过一天的退回邮件,然后解析标题以查找收到的日期 - 将字符串转换为实际日期,并将-20分钟之前的结果与未添加到列表中的旧结果进行比较

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;
}

上面以迭代返回结果的列表的形式返回:

<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>