gpt4 book ai didi

email - 需要帮助优化标记电子邮件的谷歌应用程序脚本

转载 作者:行者123 更新时间:2023-12-01 19:36:41 28 4
gpt4 key购买 nike

Gmail 存在一个问题,即对话标签未应用于到达对话线程的新邮件。 issue details here

我们found一个 Google Apps 脚本,用于修复 Gmail 收件箱中单个邮件上的标签以解决此问题。脚本如下:

function relabeller() {
var labels = GmailApp.getUserLabels();


for (var i = 0; i < labels.length; i++) {
Logger.log("label: " + i + " " + labels[i].getName());

var threads = labels[i].getThreads(0,100);
for (var j = 1; threads.length > 0; j++) {
Logger.log( (j - 1) * 100 + threads.length);
labels[i].addToThreads(threads);
threads = labels[i].getThreads(j*100, 100);
}
}
}

但是,由于 Google Apps 脚本的 5 分钟执行时间限制,此脚本在包含 20,000 多条消息的电子邮件箱中超时。

任何人都可以提出一种优化此脚本的方法以使其不会超时吗?

最佳答案

好的,我已经为此工作了几天,因为我对 Gmail 标记/不标记对话中邮件的奇怪方式感到非常沮丧。

事实上,我很惊讶标签不会自动应用于对话中的新消息。这根本没有反射(reflect)在 Gmail 用户界面中。无法查看线程并确定标签仅适用于线程中的某些消息,并且您无法在 UI 中为单个消息添加标签。在我处理下面的脚本时,我注意到您甚至无法以编程方式向单个消息添加标签。因此,目前的行为确实没有任何理由。

随着我的咆哮,我有一些关于剧本的笔记。

  • 我将 Saqib 的代码与 Serge 的代码结合起来。
  • 该脚本有两个部分:重新标记所有附加了用户标签的线程的初始运行,以及标记最近电子邮件的维护运行(当前回顾 4 天)。单次运行期间仅执行一个部分。初始运行完成后,将仅运行维护部分。您可以将触发器设置为每天运行一次,或多或少,具体取决于您的需要。
  • 初始运行在 4 分钟后停止,以避免被 5 分钟脚本时间限制终止。它设置一个触发器在 4 分钟后再次运行(这两个时间都可以使用脚本中的常量进行更改)。触发器在下次运行时被删除。
  • 维护部分没有运行时检查。如果您在过去 4 天内收到大量电子邮件,则维护部分可能会达到脚本时间限制。我可能可以在这里更改脚本以提高效率,但到目前为止它对我有用,所以我并没有真正有动力改进它。
  • 初始运行中有一个 try/catch 语句,试图捕获 Gmail 的“写入配额错误”并正常退出(即写入当前进度以便以后可以再次获取),但我不知道它是否有效,因为我无法让错误发生。
  • 当达到时间限制以及初始运行完成时,您会收到一封电子邮件。
  • 出于某种原因,即使在使用 Logger.clear() 命令时,日志也不总是在运行之间完全清除。因此,它通过电子邮件发送给用户的状态日志不仅仅是最近的运行信息。我不知道为什么会出现这种情况。

  • 我已经用它在大约半小时内(包括等待时间)处理了 20,000 封电子邮件。我实际上运行了两次,所以它一天处理了 40,000 封电子邮件。我想 10,000 的 Gmail 读/写限制不是这里应用的(可能一次将标签应用于 100 个线程算作单个写入事件而不是 100?)。根据它发送的状态电子邮件,它在 4 分钟的运行中通过了大约 5,000 个线程。

    抱歉排长队。我责怪宽屏显示器。让我知道你的想法!
    function relabelGmail() {

    var startTime= (new Date()).getTime(); // Time at start of script
    var BATCH=100; // total number of threads to apply label to at once.
    var LOOKBACKDAYS=4; // Days to look back for maintenance section of script. Should be at least 2
    var MAX_RUN_TIME=4*60*1000; // Time in ms for max execution. 4 minutes is a good start.
    var WAIT_TIME=4*60*1000; // Time in ms to wait before starting the script again.
    Logger.clear();



    // ScriptProperties.deleteAllProperties(); return; // Uncomment this line and run once to start over completely

    if(ScriptProperties.getKeys().length==0){ // this is to create keys on the first run
    ScriptProperties.setProperties({'itemsProcessed':0, 'initFinished':false, 'lastrun':'20000101', 'itemsProcessedToday':0,
    'currentLabel':'null-label-NOTREAL', 'currentLabelStart':0, 'autoTrig':0, 'autoTrigID':'0'});
    }

    var itemsP = Number(ScriptProperties.getProperty('itemsProcessed')); // total counter
    var initTemp = ScriptProperties.getProperty('initFinished'); // keeps track of when initial run is finished.
    var initF = (initTemp.toLowerCase() == 'true'); // Make it boolean

    var lastR = ScriptProperties.getProperty('lastrun'); // String of date corresponding to itemsProcessedToday in format yyyymmdd
    var itemsPT = Number(ScriptProperties.getProperty('itemsProcessedToday')); // daily counter
    var currentL = ScriptProperties.getProperty('currentLabel'); // Label currently being processed
    var currentLS = Number(ScriptProperties.getProperty('currentLabelStart')); // Thread number to start on

    var autoT = Number(ScriptProperties.getProperty('autoTrig')); // Number to say whether the last run made an automatic trigger
    var autoTID = ScriptProperties.getProperty('autoTrigID'); // Unique ID of last written auto trigger

    // First thing: google terminates scripts after 5 minutes.
    // If 4 minutes have passed, this script will terminate, write some data,
    // and create a trigger to re-schedule itself to start again in a few minutes.
    // If an auto trigger was created last run, it is deleted here.
    if (autoT) {
    var allTriggers = ScriptApp.getProjectTriggers();

    // Loop over all triggers. If trigger isn't found, then it must have ben deleted.
    for(var i=0; i < allTriggers.length; i++) {
    if (allTriggers[i].getUniqueId() == autoTID) {
    // Found the trigger and now delete it
    ScriptApp.deleteTrigger(allTriggers[i]);
    break;
    }
    }
    autoT = 0;
    autoTID = '0';
    }

    var today = dateToStr_();
    if (today == lastR) { // If new day, reset daily counter
    // Don't do anything
    } else {
    itemsPT = 0;
    }

    if (!initF) { // Don't do any of this if the initial run has been completed
    var labels = GmailApp.getUserLabels();

    // Find position of last label attempted
    var curLnum=0;
    for ( ; curLnum < labels.length; curLnum++) {
    if (labels[curLnum].getName() == currentL) {break};
    }
    if (curLnum == labels.length) { // If label isn't found, start over at the beginning
    curLnum = 0;
    currentLS = 0;
    itemsP=0;
    currentL=labels[0].getName();
    }

    // Now start working through the labels until the quota is hit.
    // Use a try/catch to stop execution if your quota has been hit.
    // Google can actually automatically email you, but we need to clean up a bit before terminating the script so it can properly pick up again tomorrow.
    try {
    for (var i = curLnum; i < labels.length; i++) {
    currentL = labels[i].getName(); // Next label
    Logger.log('label: ' + i + ' ' + currentL);

    var threads = labels[i].getThreads(currentLS,BATCH);

    for (var j = Math.floor(currentLS/BATCH); threads.length > 0; j++) {
    var currTime = (new Date()).getTime();
    if (currTime-startTime > MAX_RUN_TIME) {

    // Make the auto-trigger
    autoT = 1; // So the auto trigger gets deleted next time.

    var autoTrigger = ScriptApp.newTrigger('relabelGmail')
    .timeBased()
    .at(new Date(currTime+WAIT_TIME))
    .create();

    autoTID = autoTrigger.getUniqueId();

    // Now write all the values.
    ScriptProperties.setProperties({'itemsProcessed':itemsP, 'initFinished':initF, 'lastrun':today, 'itemsProcessedToday':itemsPT,
    'currentLabel':currentL, 'currentLabelStart':currentLS, 'autoTrig':autoT, 'autoTrigID':autoTID});

    // Send an email
    var emailAddress = Session.getActiveUser().getEmail();
    GmailApp.sendEmail(emailAddress, 'Relabel job in progress', 'Your Gmail Relabeller has halted to avoid termination due to excess ' +
    'run time. It will run again in ' + WAIT_TIME/1000/60 + ' minutes.\n\n' + itemsP + ' threads have been processed. ' + itemsPT +
    ' have been processed today.\n\nSee the log below for more information:\n\n' + Logger.getLog());
    return;
    } else {
    // keep on going
    var len = threads.length;
    Logger.log( j * BATCH + len);

    labels[i].addToThreads(threads);

    currentLS = currentLS + len;
    itemsP = itemsP + len;
    itemsPT = itemsPT + len;
    threads = labels[i].getThreads( (j+1) * BATCH, BATCH);
    }
    }

    currentLS = 0; // Reset LS counter
    }

    initF = true; // Initial run is done

    } catch (e) { // Clean up and send off a notice.
    // Write current values back to ScriptProperties
    ScriptProperties.setProperties({'itemsProcessed':itemsP, 'initFinished':initF, 'lastrun':today, 'itemsProcessedToday':itemsPT,
    'currentLabel':currentL, 'currentLabelStart':currentLS, 'autoTrig':autoT, 'autoTrigID':autoTID});

    var emailAddress = Session.getActiveUser().getEmail();
    var errorDate = new Date();
    GmailApp.sendEmail(emailAddress, 'Error "' + e.name + '" in Google Apps Script', 'Your Gmail Relabeller has failed in the following stack:\n\n' +
    e.stack + '\nThis may be due to reaching your daily Gmail read/write quota. \nThe error message is: ' +
    e.message + '\nThe error occurred at the following date and time: ' + errorDate + '\n\nThus far, ' +
    itemsP + ' threads have been processed. ' + itemsPT + ' have been processed today. \nSee the log below for more information:' +
    '\n\n' + Logger.getLog());
    return;
    }

    // Write current values back to ScriptProperties. Send completion email.
    ScriptProperties.setProperties({'itemsProcessed':itemsP, 'initFinished':initF, 'lastrun':today, 'itemsProcessedToday':itemsPT,
    'currentLabel':currentL, 'currentLabelStart':currentLS, 'autoTrig':autoT, 'autoTrigNumber':autoTID});

    var emailAddress = Session.getActiveUser().getEmail();
    GmailApp.sendEmail(emailAddress, 'Relabel job completed', 'Your Gmail Relabeller has finished its initial run.\n' +
    'If you continue to run the script, it will skip the initial run and instead relabel ' +
    'all emails from the previous ' + LOOKBACKDAYS + ' days.\n\n' + itemsP + ' threads were processed. ' + itemsPT +
    ' were processed today. \nSee the log below for more information:' + '\n\n' + Logger.getLog());

    return; // Don't run the maintenance section after initial run finish

    } // End initial run section statement


    // Below is the 'maintenance' section that will be run when the initial run is finished. It finds all new threads
    // (as defined by LOOKBACKDAYS) and applies any existing labels to all messages in each thread. Note that this
    // won't miss older threads that are labeled by the user because all messages in a thread get the label
    // when the label action is first performed. If another message is then sent or received in that thread,
    // then this maintenance section will find it because it will be deemed a "new" thread at that point.
    // You may need to search further back the first time you run this if it took more than 3 days to finish
    // the initial run. For general maintenance, though, 4 days should be plenty.

    // Note that I have not implemented a script-run-time check for this section.

    var threads = GmailApp.search('newer_than:' + LOOKBACKDAYS + 'd', 0, BATCH); //
    var len = threads.length;

    for (var i=0; len > 0; i++) {

    for (var t = 0; t < len; t++) {
    var labels = threads[t].getLabels();

    for (var l = 0; l < labels.length; l++) { // Add each label to the thread
    labels[l].addToThread(threads[t]);
    }
    }

    itemsP = itemsP + len;
    itemsPT = itemsPT + len;

    threads = GmailApp.search('newer_than:' + LOOKBACKDAYS + 'd', (i+1) * BATCH, BATCH);
    len = threads.length;
    }
    // Write the property data
    ScriptProperties.setProperties({'itemsProcessed':itemsP, 'initFinished':initF, 'lastrun':today, 'itemsProcessedToday':itemsPT,
    'currentLabel':currentL, 'currentLabelStart':currentLS, 'autoTrig':autoT, 'autoTrigID':autoTID});
    }


    // Takes a date object and turns it into a string of form yyyymmdd
    function dateToStr_(dateObj) { //takes in a date object, but uses current date if not a date

    if (!(dateObj instanceof Date)) {
    dateObj = new Date();
    }

    var dd = dateObj.getDate();
    var mm = dateObj.getMonth()+1; //January is 0!
    var yyyy = dateObj.getFullYear();

    if(dd<10){dd='0'+dd};
    if(mm<10){mm='0'+mm};
    dateStr = ''+yyyy+mm+dd;

    return dateStr;

    }

    编辑:2017 年 3 月 24 日
    我想我应该打开通知什么的,因为我从来没有看到来自 user29020 的问题。如果有人有同样的问题,我会这样做:我通过设置每天晚上凌晨 1 点到 2 点之间运行的每日触发器将其作为维护功能运行。

    附加说明:似乎在过去一年左右的某个时间点,标记对 Gmail 的调用已显着放缓。现在每个线程大约需要 0.2 秒,所以我预计最初运行 20k 封电子邮件至少需要运行 20 次左右才能完成。这也意味着,如果您通常每天收到超过 100-200 封电子邮件,维护部分也可能开始花费太长时间并开始失败。现在有很多电子邮件,但我敢打赌,有些人会收到这么多电子邮件,而且与我第一次写邮件时失败所需的每天 1000 封左右的电子邮件相比,您收到邮件的可能性似乎更大。脚本。

    无论如何,一种缓解措施是将 LOOKBACKDAYS 减少到小于 4,但我不建议将其小于 2。

    关于email - 需要帮助优化标记电子邮件的谷歌应用程序脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15234882/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com