GmailApp - 将标签添加到特定邮件,而不是线程

IT技术 javascript google-apps-script gmail gmail-api
2021-03-18 07:47:11

我正在构建一个脚本,以每天自动将我的电子邮件从一个标签移动到下一个标签(以便存储标记为“5 天”的消息明天将自动标记为“4 天”,然后是“3 天”之后的一天,以此类推)。

问题在于它将标签应用于整个线程,而不仅仅是消息 - 如果您关闭了对话视图并且将各种电子邮件分开在不同的标签下,这将是有问题的。

这是我的脚本(它有一个每日触发器):

function moveUp(previousLabel, newLabel) {
  var threads = GmailApp.getUserLabelByName(previousLabel).getThreads()
  var numThreads = threads.length
  if (numThreads>0) {
    for(var i = 0; i < numThreads; i++) {

      if(GmailApp.getUserLabelByName(previousLabel).getThreads().length>0) {
        var lastThread = GmailApp.getUserLabelByName(previousLabel).getThreads()[0]
        GmailApp.getUserLabelByName(newLabel).addToThread(lastThread.markUnread());
        GmailApp.getUserLabelByName(previousLabel).removeFromThread(lastThread)
        Utilities.sleep(200)
      } else {
        {break;}
      }
    }
  }
}

function myFunction() {
  var threads = GmailApp.getUserLabelByName("-To Do/1 Day").getThreads()
  var numThreads = threads.length
  if (numThreads>0) {
    for(var i = 0; i < numThreads; i++) {

      if(GmailApp.getUserLabelByName("-To Do/1 Day").getThreads().length>0) {
        var lastThread = GmailApp.getUserLabelByName("-To Do/1 Day").getThreads()[0]
        lastThread.moveToInbox().markUnread();
        GmailApp.getUserLabelByName("-To Do/1 Day").removeFromThread(lastThread)
        Utilities.sleep(200)
      } else {
        {break;}
      }
    }
  }
    moveUp("-To Do/2 Days", "-To Do/1 Day")
    moveUp("-To Do/3 Days", "-To Do/2 Days")
    moveUp("-To Do/4 Days", "-To Do/3 Days")
    moveUp("-To Do/5 Days", "-To Do/4 Days")
}

那么有谁知道如何仅将标签应用于特定消息?还是实现相同结果的解决方法?

谢谢

2个回答

您可以使用GMail API执行此操作您需要为您的项目启用 API。在编辑器中,选择Resources > Advanced Google services,然后单击“这些服务也必须在Google Developers Console 中启用”。在那里,启用 GMail API。

修改消息

使用示例:

modifyMessage('me', messageId, ['stack','overflow']);       // add two labels
modifyMessage('me', messageId, null, ['stack','overflow']); // remove two labels

TL;DR完整代码在这个 gist 中可用,下面的隐藏代码片段中可用

/**
 * Modify the Labels a Message is associated with.
 * Throws if unsuccessful.
 * see https://developers.google.com/gmail/api/v1/reference/users/messages/modify
 *
 * @param  {String} userId           User's email address. The special value 'me'
 *                                   can be used to indicate the authenticated user.
 * @param  {String} messageId        ID of Message to modify.
 * @param  {String[]} labelsToAdd    Array of Label names to add.
 * @param  {String[]} labelsToRemove Array of Label names to remove.
 *
 * @returns {Object}                 Users.messages resource, see reference. 
 */
function modifyMessage(userId, messageId, labelsToAdd, labelsToRemove) {
  labelsToAdd = labelsToAdd || [];
  labelsToRemove = labelsToRemove || [];
  // see https://developers.google.com/gmail/api/v1/reference/users/messages/modify
  var url = 'https://www.googleapis.com/gmail/v1/users/${userId}/messages/${id}/modify'
            .replace("${userId}","me")
            .replace("${id}", messageId );

GMail API 是一种 RESTful API,与使用内置 GMailApp 服务相比,它可以更灵活、更详细地访问线程、消息、标签、草稿和历史记录。在我们的脚本中,我们通过 URL 访问 API - 在这里,我们首先在modifyMessage 方法的基本 URL 中填写详细信息。

  var headers = {
    Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
  };

除了为我们的项目启用 API 之外,每个请求都需要用户身份验证,这是通过在命令头中嵌入一​​个Bearer Token来完成的

接下来,我们将构建其余的命令参数。

  var addLabelIds = [];
  for (var i=0; i<labelsToAdd.length; i++) {
    addLabelIds[i] = getLabelId( labelsToAdd[i] );
  }
  var removeLabelIds = [];
  for (i=0; i<labelsToRemove.length; i++) {
    removeLabelIds[i] = getLabelId( labelsToRemove[i], false );
  }
  var request = {
    'addLabelIds': addLabelIds,
    'removeLabelIds': removeLabelIds
  };

操作modify调用规范,POST有效负载包含标签 ID 列表。这些 ID 无法通过内置 GMail 服务获得,因此我们还将为此使用 GMail API - 这getLabelId()是另一个与此类似的功能,专门用于按名称检索标签 ID,并根据需要创建新标签。请参阅该代码的要点或片段。

  var params = {
    method: "post",
    contentType: "application/json",
    headers: headers,
    payload: JSON.stringify(request),
    muteHttpExceptions: true
  };
  //var check = UrlFetchApp.getRequest(url, params); // for debugging
  var response = UrlFetchApp.fetch(url, params);

最后,我们params为命令组装了对象,其属性包含headers我们的身份验证令牌和payload命令参数,stringify-d 用于传输。muteHttpExceptions: true在失败的情况下设置静噪系统生成的异常,以便我们可以自己处理它们。

我已经注释掉了一个调用UrlFetchApp.getRequest()- 在发送命令之前验证命令的内容非常方便。

调用将UrlFetchApp.fetch()我们的命令传输到在我们建立的 URL 上运行的服务。

  var result = response.getResponseCode();
  if (result == '200') {  // OK
    return JSON.parse(response.getContentText());
  }

当收到响应时,我们首先检查result命令的 ,它是HTTP 状态代码的形式A200 OK告诉我们我们成功了,所以我们继续从响应中提取我们的结果。JSON.parse()消息中JSON 字符串转换为 JavaScript 对象,我们将其返回给调用者。

  else {
    // This is only needed when muteHttpExceptions == true
    var err = JSON.parse(response.getContentText());
    throw new Error( 'Error (' + result + ") " + err.error.message );
  }
}

但是,如果我们遇到错误,我们会抛出自己的异常。根据命令的详细信息,我们可能希望以不同方式处理特定错误。例如,在这个modify命令中,如果我们提供一个不存在的标签 ID,我们将收到一个错误:

错误:错误(400)无效标签:“无效标签”

优秀的工作在这里!
2021-04-27 07:47:11

这是一个旧线程,但对于任何可能像我一样阅读它的人来说,也许这会为您节省一些时间。我喜欢@Mogsdad 的回答,并从中学到了很多。但是,此版本使用高级 Gmail 服务(Google 脚本中的完整 GMail API),因此重量更轻一些。

function getLabelMap() {
  var allLabels = Gmail.Users.Labels.list('me');
  var labelMap = [];

  for (var label of allLabels.labels) {
    labelMap[label.name] = label.id;
  }
  
  return labelMap;
}

var labelMap = getLabelMap();

function getLabel(labelName) {
  return labelMap[labelName];
}

function labelMessage(messageID, labelName) {
  var labelID = getLabel(labelName);
  var labelRequest = {addLabelIds: [labelID]};
  var subject = GmailApp.getMessageById(messageID).getSubject();

  if (labelID != null) {
    Logger.log("Labelling as %s: %s", labelName, subject);
    Gmail.Users.Messages.modify(labelRequest, 'me', messageID);
  } else {
    Logger.log("Label not found: %s", labelName);
  }
}

function unlabelMessage(messageID, labelName) {
  var labelID = getLabel(labelName);
  var labelRequest = {removeLabelIds: [labelID]};
  var subject = GmailApp.getMessageById(messageID).getSubject();

  if (labelID != null) {
    Logger.log("Removing label %s: %s", labelName, subject);
    Gmail.Users.Messages.modify(labelRequest, 'me', messageID);
  } else {
    Logger.log("Label not found: %s", labelName);
  }
}

function reLabel () {

  var messagesToRelabel = Gmail.Users.Messages.list('me', {'q':'label:Old-label'}).messages || []; // feel free to add date criteria using regular GMail keywords

  // Loop through each message (not by thread), using the Advanced Gmail Service (full GMail API in a Google Script).
  messagesToRelabel.forEach(function (messageToRelabel){
    unlabelMessage(messageToRelabel.id, "Old label");
    labelMessage(messageToRelabel.id, "New label");
  });

}

OP 没有要求,但可能对尝试使用 GMail API 进行“高级过滤/标记”的其他人有所帮助:

function getMessageHeader(messageID, headerField) {
  var messageInfo = Gmail.Users.Messages.get('me', messageID, {'format':'METADATA', 'metadataHeaders':[headerField]});

  if (messageInfo.payload.headers) {
    return messageInfo.payload.headers[0].value;
  } else {
    return null;
  }
}

上面让你过滤标题信息,例如我用它来检查是否X-Uniform-Type-Identifier等于com.apple.mail-note自动标记旧的 Apple Notes 以进行删除。