忽略具有不同标签的同线程电子邮件 [英] Ignore same-thread emails that have different labels
问题描述
我正在将日期和主题从特定的新电子邮件写入Google表格的新行.
I am writing the Date and Subject from specific new emails to a new row of a Google Sheet.
- 我使用过滤器将标签应用于新邮件项目.
- 该脚本处理那些带有标签的电子邮件
- 标签被删除
- 将应用新标签,以便下次不再处理这些电子邮件.
问题:当存在 myLabel 电子邮件时,脚本将处理同一线程(例如,相同的主题和发件人)中的所有电子邮件,而不管其标签(即使是收件箱和垃圾).
Problem: When there is a myLabel email, the script processes all emails in the same thread (eg same subject and sender) regardless of their label (even Inbox and Trash).
问题:如何仅处理新电子邮件,即带有标签 myLabel 的电子邮件-即使这些邮件的线程超出了 myLabel 文件夹?
Question: How to only process new emails i.e. ones with the label myLabel - even when the thread of those messages extends outside the myLabel folder?
我当前的脚本:
function fetchmaildata() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('mySheetName');
var label = GmailApp.getUserLabelByName('myLabel');
var threads = label.getThreads();
for (var i = 0; i < threads.length; i++)
{
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++)
{
var sub = messages[j].getSubject();
var dat = messages[j].getDate();
ss.appendRow([dat, sub])
}
threads[i].removeLabel(label);
threads[i].addLabel(newlabel);
}
}
我通过将for
循环更改为此目的来破解一种解决方案:
I hacked a solution for my purposes by changing my for
loop to this:
for (var j = messages.length-1; j > messages.length-2; j--)
这表示仅处理线程中的最新电子邮件,即使 myLabel 文件夹中不止一个线程的一封电子邮件也是如此.奇怪的是,脚本仍会更改所有 myLabel 电子邮件中的标签,但是只有最新的一个线程会被写入到电子表格中,因此它对我有用.
This says to process only the latest email in the thread, even when there is more than one email of a thread in the myLabel folder. Oddly, the script still changes the Labels of all the myLabel emails, but only the latest one of a thread gets written to the spreadsheet, so it works for me.
我不得不对代码进行另一处更改,因为上面的代码不能作为时间触发的计划任务运行.我以这种方式更改了代码,现在它可以按时间表运行了!!
I had to make another change to the code because the above code does not run as a time-triggered scheduled task. I changed the code in this way and it now runs on a time schedule !!
//var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss = SpreadsheetApp.openById("myGoogleSheetID");
推荐答案
由于在该线程中的单个消息中,标签可以在该线程上.您的代码只需加上 label->所有标签线程->所有线程消息,而不是仅访问具有给定标签的线程中的消息.那不是你的错,这是Gmail服务的局限性.您可以使用两种方法来纠正此行为:
A label can be on a thread due to being on a single message in said thread. Your code simply goes label -> all label threads -> all thread messages, rather than accessing only the messages in a thread with a given label. That's not really your fault - it's a limitation of the Gmail Service. There are two approaches that you can use to remedy this behavior:
REST API支持使用 Gmail.Users.Messages.list
和labelIds
可选参数.例如:
The REST API supports detailed querying of messages, including per-message label status, with Gmail.Users.Messages.list
and the labelIds
optional argument. For example:
// Get all messages (not threads) with this label:
function getMessageIdsWithLabel_(labelClass) {
const labelId = labelClass.getId();
const options = {
labelIds: [ labelId ],
// Only retrieve the id metadata from each message.
fields: "nextPageToken,messages/id"
};
const messages = [];
// Could be multiple pages of results.
do {
var search = Gmail.Users.Messages.list("me", options);
if (search.messages && search.messages.length)
Array.prototype.push.apply(messages, search.messages);
options.pageToken = search.nextPageToken;
} while (options.pageToken);
// Return an array of the messages' ids.
return messages.map(function (m) { return m.id; });
}
使用REST API后,您可能会使用其他方法,例如批消息标签调整:
Once using the REST API, there are other methods you might utilize, such as batch message label adjustment:
function removeLabelFromMessages_(messageIds, labelClass) {
const labelId = labelClass.getId();
const resource = {
ids: messageIds,
// addLabelIds: [ ... ],
removeLabelIds: [ labelId ]
};
// https://developers.google.com/gmail/api/v1/reference/users/messages/batchModify
Gmail.Users.Messages.batchModify(resource, "me");
}
结果:
function foo() {
const myLabel = /* get the Label somehow */;
const ids = getMessageIdsWithLabel_(myLabel);
ids.forEach(function (messageId) {
var msg = GmailApp.getMessageById(messageId);
/* do stuff with the message */
});
removeLabelFromMessages_(ids, myLabel);
}
推荐读物:
- Advanced Services
- Gmail Service
Messages#list
Message#batchModify
- Partial responses aka the 'fields' parameter
您还可以将每个消息ID存储在某个地方,并使用存储的ID来检查是否已经处理了给定消息.消息ID是唯一的.
You could also store each message ID somewhere, and use the stored IDs to check if you've already processed a given message. The message Ids are unique.
此示例使用本机JavaScript对象进行非常快速的查找(相对于将ID仅存储在数组中并需要使用Array#indexOf
).为了在脚本执行之间保持处理后的ID,它在活动工作簿或您选择的工作簿上使用工作表:
This example uses a native JavaScript object for extremely fast lookups (vs. simply storing the ids in an array and needing to use Array#indexOf
). To maintain the processed ids between script execution, it uses a sheet on either the active workbook, or a workbook of your choosing:
var MSG_HIST_NAME = "___processedMessages";
function getProcessedMessages(wb) {
// Read from a sheet on the given spreadsheet.
if (!wb) wb = SpreadsheetApp.getActive();
const sheet = wb.getSheetByName(MSG_HIST_NAME)
if (!sheet) {
try { wb.insertSheet(MSG_HIST_NAME).hideSheet(); }
catch (e) { }
// By definition, no processed messages.
return {};
}
const vals = sheet.getSheetValues(1, 1, sheet.getLastRow(), 1);
return vals.reduce(function (acc, row) {
// acc is our "accumulator", and row is an array with a single message id.
acc[ row[0] ] = true;
return acc;
}, {});
}
function setProcessedMessages(msgObject, wb) {
if (!wb) wb = SpreadsheetApp.getActive();
if (!msgObject) return;
var sheet = wb.getSheetByName(MSG_HIST_NAME);
if (!sheet) {
sheet = wb.insertSheet(MSG_HIST_NAME);
if (!sheet)
throw new Error("Unable to make sheet for storing data");
try { sheet.hideSheet(); }
catch (e) { }
}
// Convert the object into a serializable 2D array. Assumes we only care
// about the keys of the object, and not the values.
const data = Object.keys(msgObject).map(function (msgId) { return [msgId]; });
if (data.length) {
sheet.getDataRange().clearContent();
SpreadsheetApp.flush();
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
用法类似于:
function foo() {
const myLabel = /* get label somehow */;
const processed = getProcessedMessages();
myLabel.getThreads().forEach(function (thread) {
thread.getMessages().forEach(function (msg) {
var msgId = msg.getId();
if (processed[msgId])
return; // nothing to do for this message.
processed[msgId] = true;
// do stuff with this message
});
// do more stuff with the thread
});
setProcessedMessages(processed);
// do other stuff
}
推荐读物:
- Is checking an object for a key more efficient than searching an array for a string?
Array#reduce
Array#map
Array#forEach
这篇关于忽略具有不同标签的同线程电子邮件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!