【问题标题】:How to tell if an email sent via Gmail REST API has bounced?如何判断通过 Gmail REST API 发送的电子邮件是否被退回?
【发布时间】:2019-03-05 23:55:00
【问题描述】:

我正在通过 Gmail API 发送电子邮件,想知道邮件何时退回。我该怎么做?

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

X-Failed-Recipients: zzzzzzzzzasdfasdfadfa@gmail.com

但是,似乎并不总是有一个标头指示被退回的原始 messageID。

我在想下面的计划,但有很多漏洞,我想我一定是在接近这个错误。

  1. 通过 Gmail API 发送电子邮件(发送到失败的电子邮件)--->成功通过
  2. 接收退回的电子邮件收件箱
  3. 扫描电子邮件以查找包含退回标头的电子邮件
  4. 尝试找出被退回的原始电子邮件。

问题

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

我的其他一些想法是:

  • 搜索主题中带有“Undeliverable”字符串的电子邮件?
  • 不使用 gmail rest api 发送,因为退回跟踪是不可行的。也许改用 SMTP api?

【问题讨论】:

    标签: gmail gmail-api


    【解决方案1】:

    通过 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"
      },
     ]
    }
    

    我跳了!让我们获取整个邮件并对其进行解码并获取您所暗示的 Message-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(将所有“-”替换为“+”,将“_”替换为“/”),并对它进行 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"
      }
     ],
    }
    

    【讨论】:

    • 关于这可能有点过时/过时的人的信息,并且并没有真正涵盖除了 java 脚本之外您将如何处理它。 gmail api 因为已经建立了一个页面developers.google.com/gmail/api/v1/reference/users/messages/…,它在附近做了同样的事情-
    • 我在几秒钟内尝试了 Epoch,但它不起作用。它没有返回任何数据作为响应。
    【解决方案2】:

    当您通过以下方式发送消息时

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

    【讨论】:

      【解决方案3】:

      这是您可以使用后端技术并尝试遵循当前文档指南的方法。因为它出现在:和之前:标签只支持日期而不是日期+时间。因此,虽然在 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>
      

      【讨论】:

        猜你喜欢
        • 2018-07-21
        • 2020-01-14
        • 1970-01-01
        • 2019-01-24
        • 2018-09-13
        • 2016-03-13
        • 2018-02-23
        • 2021-01-24
        • 2020-01-06
        相关资源
        最近更新 更多