【问题标题】:How to send a draft email using google apps script如何使用谷歌应用脚​​本发送电子邮件草稿
【发布时间】:2015-01-28 04:07:22
【问题描述】:

我正在使用 Google 应用程序脚本,并希望创建一个脚本,该脚本从草稿中提取邮件并在其标签为“send-tomorrow”的情况下发送它们。 查找带有特定标签的草稿非常简单:

 var threads = GmailApp.search('in:draft label:send-tomorrow');

但是我没有看到发送消息的 API! 我看到的唯一选择是: - 打开消息 - 提取正文/附件/标题/from/to/cc/bcc - 使用上述参数发送新消息 - 销毁之前的草稿

这看起来很烦人,我不确定嵌入图像、多个附件等是否能很好地工作......

有什么提示吗?

【问题讨论】:

    标签: google-apps-script gmail gmail-api


    【解决方案1】:

    我看到的唯一选项是: - 打开消息 - 提取正文/附件/标题/from/to/cc/bcc - 使用上述参数发送新消息 - 销毁以前的草稿

    这是 Amit Agarawa 的 this blog 的确切主题。他的脚本就像你描述的那样,但不处理内联图像。对于这些,您可以修改来自this article 的代码。

    但是你是对的 - 如果你不能只发送愚蠢的东西,那么即使有草稿消息又有什么意义?!

    我们可以使用 Google Apps 脚本中的 GMail API Users.drafts: send 发送草稿。以下独立脚本执行此操作,并处理必要的授权。

    脚本

    完整的脚本可在this gist 中找到。

    /*
     * Send all drafts labeled "send-tomorrow".
     */
    function sendDayOldDrafts() {
      var threads = GmailApp.search('in:draft label:send-tomorrow');
    
      for (var i=0; i<threads.length; i++) {
        var msgId = threads[0].getMessages()[0].getId();
        sendDraftMsg( msgId );
      }
    }
    
    
    /**
     * Sends a draft message that matches the given message ID.
     * Throws if unsuccessful.
     * See https://developers.google.com/gmail/api/v1/reference/users/drafts/send.
     *
     * @param {String}     messageId   Immutable Gmail Message ID to send
     *
     * @returns {Object}               Response object if successful, see
     *                                 https://developers.google.com/gmail/api/v1/reference/users/drafts/send#response
     */
    function sendDraftMsg( msgId ) {
      // Get draft message.
      var draftMsg = getDraftMsg(msgId,"json");
      if (!getDraftMsg(msgId)) throw new Error( "Unable to get draft with msgId '"+msgId+"'" );
    
      // see https://developers.google.com/gmail/api/v1/reference/users/drafts/send
      var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts/send'
      var headers = {
        Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
      };
      var params = {
        method: "post",
        contentType: "application/json",
        headers: headers,
        muteHttpExceptions: true,
        payload: JSON.stringify(draftMsg)
      };
      var check = UrlFetchApp.getRequest(url, params)
      var response = UrlFetchApp.fetch(url, params);
    
      var result = response.getResponseCode();
      if (result == '200') {  // OK
        return JSON.parse(response.getContentText());
      }
      else {
        // This is only needed when muteHttpExceptions == true
        var err = JSON.parse(response.getContentText());
        throw new Error( 'Error (' + result + ") " + err.error.message );
      }
    }
    
    
    /**
     * Gets the current user's draft messages.
     * Throws if unsuccessful.
     * See https://developers.google.com/gmail/api/v1/reference/users/drafts/list.
     *
     * @returns {Object[]}             If successful, returns an array of 
     *                                 Users.drafts resources.
     */
    function getDrafts() {
      var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts';
      var headers = {
        Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
      };
      var params = {
        headers: headers,
        muteHttpExceptions: true
      };
      var check = UrlFetchApp.getRequest(url, params)
      var response = UrlFetchApp.fetch(url, params);
    
      var result = response.getResponseCode();
      if (result == '200') {  // OK
        return JSON.parse(response.getContentText()).drafts;
      }
      else {
        // This is only needed when muteHttpExceptions == true
        var error = JSON.parse(response.getContentText());
        throw new Error( 'Error (' + result + ") " + error.message );
      }
    }
    
    /**
     * Gets the draft message ID that corresponds to a given Gmail Message ID.
     *
     * @param {String}     messageId   Immutable Gmail Message ID to search for
     *
     * @returns {String}               Immutable Gmail Draft ID, or null if not found
     */
    function getDraftId( messageId ) {
      if (messageId) {
        var drafts = getDrafts();
    
        for (var i=0; i<drafts.length; i++) {
          if (drafts[i].message.id === messageId) {
            return drafts[i].id;
          }
        }
      }
    
      // Didn't find the requested message
      return null;
    }
    
    
    /**
     * Gets the draft message content that corresponds to a given Gmail Message ID.
     * Throws if unsuccessful.
     * See https://developers.google.com/gmail/api/v1/reference/users/drafts/get.
     *
     * @param {String}     messageId   Immutable Gmail Message ID to search for
     * @param {String}     optFormat   Optional format; "object" (default) or "json"
     *
     * @returns {Object or String}     If successful, returns a Users.drafts resource.
     */
    function getDraftMsg( messageId, optFormat ) {
      var draftId = getDraftId( messageId );
    
      var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts'+"/"+draftId;
      var headers = {
        Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
      };
      var params = {
        headers: headers,
        muteHttpExceptions: true
      };
      var check = UrlFetchApp.getRequest(url, params)
      var response = UrlFetchApp.fetch(url, params);
    
      var result = response.getResponseCode();
      if (result == '200') {  // OK
        if (optFormat && optFormat == "JSON") {
          return response.getContentText();
        }
        else {
          return JSON.parse(response.getContentText());
        }
      }
      else {
        // This is only needed when muteHttpExceptions == true
        var error = JSON.parse(response.getContentText());
        throw new Error( 'Error (' + result + ") " + error.message );
      }
    }
    

    授权

    要使用 Google 的 API,我们需要为当前用户提供 OAuth2 令牌 - 就像我们为高级服务所做的那样。这是使用ScriptApp.getOAuthToken() 完成的。

    将代码复制到您自己的脚本后,打开 Resources -> Advanced Google Services,打开 Google Developers Console 的链接,并为您的项目启用 Gmail API。

    只要脚本包含至少一个需要用户权限的 GMailApp 方法,就会为 OAuthToken 正确设置身份验证范围。在这个例子中,这是由GmailApp.search() in sendDayOldDrafts() 处理的;但为了保险起见,您可以使用 API 直接在函数中包含不可访问的函数调用。

    【讨论】:

    • 已更新 - 功能齐全(哇!)
    • 非常非常非常(等等...)干得好。已经是昨天了,但现在很完美!非常感谢我的朋友 ;-) 再次 +1
    【解决方案2】:

    我使用GmailMessage.forward 方法做到了。

    它适用于上传图片和附件,但我必须设置主题以避免前缀“Fwd:”和用户名,因为它只向收件人显示用户电子邮件。

    我没有找到处理草稿的方法,所以我只删除了标签以防止再次发送。

    脚本:

    function getUserFullName(){
      var email = Session.getActiveUser().getEmail();
      var contact = ContactsApp.getContact(email);
      return contact.getFullName();
    }
    
    function testSendTomorrow(){
      var threads = GmailApp.search('in:draft label:send-tomorrow');
    
      if(threads.length == 0){
        return;
      }
    
      var labelSendTomorrow = GmailApp.getUserLabelByName("send-tomorrow");
    
      for(var i = 0; i < threads.length; i++){
        var messages = threads[i].getMessages();
        for(var j = 0; j < messages.length; j++){
          var mssg = messages[j];
          if(mssg.isDraft()){
            mssg.forward(mssg.getTo(), {
              cc: mssg.getCc(),
              bcc: mssg.getBcc(),
              subject: mssg.getSubject(),
              name: getUserFullName()
            });
          }
        }
        threads[i].removeLabel(labelSendTomorrow);
      }
    }
    

    【讨论】:

    • 非常聪明的方法! @Mogsdad 的解决方案仍然更完整,但您的解决方案肯定更简单(即使有些限制)
    • 更新:今天可以添加线程[i].moveToTrash();并将草稿副本发送到 trach(您仍然会在已发送的邮件中保留另一份)
    【解决方案3】:

    您可以搜索所有草稿,然后发送该特定草稿没问题。

    function sendMessage(id){
      GmailApp.getDrafts().forEach(function (draft) {
        mes = draft.getMessage()
        if (mes.getId() == id) {
          draft.send()
        }
      })
    }
    

    【讨论】:

      【解决方案4】:

      首先,GmailDraft 现在有一个可以直接调用的send() 函数。见:https://developers.google.com/apps-script/reference/gmail/gmail-draft#send()

      他们的代码示例:

      var draft = GmailApp.getDrafts()[0]; // The first draft message in the drafts folder
      var msg = draft.send(); // Send it
      Logger.log(msg.getDate()); // Should be approximately the current timestamp
      

      其次,现在谷歌已经发布了预定发送,可能甚至不需要它。

      1. 单击Send 旁边的箭头

      1. 选择您的首选发送时间

      【讨论】:

        【解决方案5】:

        更简单的替代方法是使用 gmail api 而不是 gmailApp:

        function sendtestDraft(draftId){
        
        
          var request = Gmail.Users.Drafts.send({id : draftId},'me');
        
        
          Logger.log(request);
        
        }
        

        上面的函数示例在https://script.google.com 的gs 脚本中使用。 它需要 DraftId(而不是消息 Id)并且草稿将被发送。图片和附件都OK! 信息:https://developers.google.com/gmail/api/v1/reference/users/drafts/send

        【讨论】:

        • 我还认为@mogsdad 的上述解决方案使用了 API,我错了吗?
        • 此解决方案是否消耗API quota?多少钱?
        • send() 中的iduserId。请查看您提供的链接中的 API。
        • @evan 不,你错了。我的例子确实有效。 userId 是“me” - 来自文档: userId:“用户的电子邮件地址。特殊值 me 可用于指示经过身份验证的用户”。 id 是“草稿的不可变 ID”。 - 请阅读我提供的链接中的 API。
        • @user2677034 - 你完全正确。我重新查看了该链接,它确实将 draftId 作为输入。我一定是打开了另一个窗口并感到困惑。很好的发现!
        【解决方案6】:

        我是新来的,没有足够的“声誉”来发表评论,所以无法评论 Mogsdad 的原始答案,所以我不得不创建一个新答案:

        我已经调整了 Mogsdad 的解决方案,使其也支持回复/转发现有线程,而不仅仅是全新的消息。

        要在现有线程上使用它,您应该首先创建回复/转发,然后才标记线程。我的代码还支持多个标签并设置这些标签。

        我为它创建了一个新的要点,将 Mogsdad 的分叉,在这里:https://gist.github.com/hadasfester/81bfc5668cb7b666b4fd6eeb6db804c3

        我仍然需要在文档中添加一些屏幕截图链接,否则这是可以使用的,我自己一直在使用它。希望对您有用。

        也在这里内联:

        /**
         * This script allows you to mark threads/drafts with a predetermined label and have them get sent the next time your trigger
         * sets off. 
         * 
         * Setup instructions:
         * 1. Make a copy of this script (File -> Make a copy)
         * 2. Follow the "Authorization" instructions on https://stackoverflow.com/a/27215474. (If later during setup/testing you get
         *    another permissions approval dialog, approve there as well).
         * 2. I created two default labels, you can edit/add your own. See "TODO(user):" below. After that, to create them in gmail,
         *    choose "setUpLabel" function above and click the play button (TODO: screenshot). Refresh your gmail tab, you should see
         *    the new labels.
         * 3. Click the clock icon above (TODO: screenshot) and set time triggers, e.g. like so: (TODO: screenshot)
         * 4. I recommend also setting up error notifications: (TODO: screenshot).
         * 
         * Testing setup:
         * When you're first setting this up, if you want to test it, create a couple 
         * of drafts and label them. Then, in this 
         * script, select "sendWeekStartDrafts" or "sendTomorrowDrafts" in the function dropdown 
         * and press play. This manually triggers the script (instead of relying on the 
         * timer) so you can see how it works. 
         *
         * Usage instructions:
         * 1. To get a draft sent out on the next trigger, mark your draft with the label you chose.
         *    NOTE: If your draft is a reply to a thread, make sure you first create the draft and only then set the label on the
         *    thread, not the other way around.
         * That's it! Upon trigger your draft will be sent and the label will get removed from the thread.
         * 
         * Some credits and explanation of differences/improvements from other existing solutions:
         * 1. This script was adapted from https://stackoverflow.com/a/27215474 to also support replying existing threads, not only
         *    sending brand new messages.
         * 2. Other solutions I've run into are based on creating a new message, copying it field-by-field, and sending the new one,
         *    but those have many issues, some of which are that they also don't handle replies and forwards very elegantly.
         *
         * Enjoy!
         **/
        
        var TOMORROW_LABEL = '!send-tomorrow';
        var WEEK_START_LABEL = '!send-week-start';
        // TODO(user): add more labels here.
        
        /**
         * Set up the label for delayed send!
         **/
        function setUpLabels() {
          GmailApp.createLabel(TOMORROW_LABEL);
          GmailApp.createLabel(WEEK_START_LABEL);
          // TODO(user): add more labels here.
        }
        
        function sendTomorrowDrafts() {
          sendLabeledDrafts(TOMORROW_LABEL);
        }
        
        function sendWeekStartDrafts() {
          sendLabeledDrafts(WEEK_START_LABEL);
        }
        
        // TODO(user): add more sendXDrafts() functions for your additional labels here.
        
        /*
         * Send all drafts labeled $MY_LABEL.
         * @param {String}     label   The label for which to send drafts.
         */
        function sendLabeledDrafts(label) {
          var threads = GmailApp.search('in:draft label:' + label);
        
          for (var i=0; i<threads.length; i++) {
            var thread = threads[i];
            var messages = thread.getMessages();
            var success = false;
            for (var j=messages.length-1; j>=0; j--) {
              var msgId = messages[j].getId();
              if (sendDraftMsg( msgId )) {
                success = true;
              }
            }
            if (!success) { throw Error( "Failed sending msg" ) };
            if (success) {
              var myLabel = GmailApp.getUserLabelByName(label);
              thread.removeLabel(myLabel);
            }
          }
        }
        
        /**
         * Sends a draft message that matches the given message ID.
         * See https://developers.google.com/gmail/api/v1/reference/users/drafts/send.
         *
         * @param {String}     messageId   Immutable Gmail Message ID to send
         *
         * @returns {Object}               Response object if successful, see
         *                                 https://developers.google.com/gmail/api/v1/reference/users/drafts/send#response
         */
        function sendDraftMsg( msgId ) {
          // Get draft message.
          var draftMsg = getDraftMsg(msgId,"json");
          if (!getDraftMsg(msgId)) return null;
          
          // see https://developers.google.com/gmail/api/v1/reference/users/drafts/send
          var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts/send'
          var headers = {
            Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
          };
          var params = {
            method: "post",
            contentType: "application/json",
            headers: headers,
            muteHttpExceptions: true,
            payload: JSON.stringify(draftMsg)
          };
          var check = UrlFetchApp.getRequest(url, params)
          var response = UrlFetchApp.fetch(url, params);
        
          var result = response.getResponseCode();
          if (result == '200') {  // OK
            return JSON.parse(response.getContentText());
          }
          else {
            // This is only needed when muteHttpExceptions == true
            return null;
          }
        }
        
        
        /**
         * Gets the current user's draft messages.
         * Throws if unsuccessful.
         * See https://developers.google.com/gmail/api/v1/reference/users/drafts/list.
         *
         * @returns {Object[]}             If successful, returns an array of 
         *                                 Users.drafts resources.
         */
        function getDrafts() {
          var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts';
          var headers = {
            Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
          };
          var params = {
            headers: headers,
            muteHttpExceptions: true
          };
          var check = UrlFetchApp.getRequest(url, params)
          var response = UrlFetchApp.fetch(url, params);
        
          var result = response.getResponseCode();
          if (result == '200') {  // OK
            return JSON.parse(response.getContentText()).drafts;
          }
          else {
            // This is only needed when muteHttpExceptions == true
            var error = JSON.parse(response.getContentText());
            throw new Error( 'Error (' + result + ") " + error.message );
          }
        }
        
        /**
         * Gets the draft message ID that corresponds to a given Gmail Message ID.
         *
         * @param {String}     messageId   Immutable Gmail Message ID to search for
         *
         * @returns {String}               Immutable Gmail Draft ID, or null if not found
         */
        function getDraftId( messageId ) {
          if (messageId) {
            var drafts = getDrafts();
        
            for (var i=0; i<drafts.length; i++) {
              if (drafts[i].message.id === messageId) {
                return drafts[i].id;
              }
            }
          }
        
          // Didn't find the requested message
          return null;
        }
        
        
        /**
         * Gets the draft message content that corresponds to a given Gmail Message ID.
         * See https://developers.google.com/gmail/api/v1/reference/users/drafts/get.
         *
         * @param {String}     messageId   Immutable Gmail Message ID to search for
         * @param {String}     optFormat   Optional format; "object" (default) or "json"
         *
         * @returns {Object or String}     If successful, returns a Users.drafts resource.
         */
        function getDraftMsg( messageId, optFormat ) {
          var draftId = getDraftId( messageId );
        
          var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts'+"/"+draftId;
          var headers = {
            Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
          };
          var params = {
            headers: headers,
            muteHttpExceptions: true
          };
          var check = UrlFetchApp.getRequest(url, params)
          var response = UrlFetchApp.fetch(url, params);
        
          var result = response.getResponseCode();
          if (result == '200') {  // OK
            if (optFormat && optFormat == "JSON") {
              return response.getContentText();
            }
            else {
              return JSON.parse(response.getContentText());
            }
          }
          else {
            // This is only needed when muteHttpExceptions == true
            return null;
          }
        }

        【讨论】:

          猜你喜欢
          • 2017-05-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-22
          • 1970-01-01
          • 2013-11-15
          相关资源
          最近更新 更多