When I use the script below, sourced from the web, it only works for one (Cam1) of the two labels . The labels within Gmail are associated with the emails and they are older than 1 day.
Why is this script only working as written on one label?
Im new at this so please keep it simple! Thanks
function oldEmailDeletion() {
//Age of email threads that will be deleted (i.e. older_than: # days)
var daysAgo = 1;
//Expiration date variable
var expirationDate = new Date();
//Set the older_than date. Any email older than this date will be deleted
expirationDate.setDate(expirationDate.getDate()-daysAgo);
//Labels associated with emails to be included in deletion
var labels = [
'Cam1',
'Cam2'
];
//Loop through each email label found in the "labels" variable array
for(var i = 0; i < labels.length; i++){
//Retrieve label information based on value in "labels" variable array
var label = GmailApp.getUserLabelByName(labels[i].toString());
//Access all email threads associated with the retrieved label
var emailThreads = label.getThreads(); //getThreads(###,###) if a specific range of email threads to retrieve
//Loop through each email thread set to "emailThreads" variable
for(var j = 0; j < emailThreads.length; j++){
//If an email thread is older than the expiration date, then delete
if(emailThreads[j].getLastMessageDate() < expirationDate){
emailThreads[j].moveToTrash();
}
}
}
}
Try it this way:
function oldEmailDeletion() {
var dt=new Date();
var exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
var labels=['Cam1','Cam2'];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getLastMessageDate().valueOf()<exp){
emailThreads[j].moveToTrash();
}
}
}
}
If you actually want to delete the messages delete the message older than one day then this approach should work for you. And you will need to Enable the Gmail API.
function oldEmailDeletion() {
const dt=new Date();
const exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
const labels=['Cam1','Cam2'];
var idA=[];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getMessageCount()>0) {
var msgs=emailThreads[j].getMessages();
for(var k=0;i<msgs.length;j++) {
var msg=msgs[k];
if(new Date(msg.getDate()).valuefOf()<exp) {
idA.push(msg.getId());
}
}
}
}
}
if(idA.length>0) {
var request={"ids":idA};
Gmail.Users.Messages.batchDelete(request, "me");
}
}
If you just want to move them to the trash. Then this approach should work for you.
function oldEmailTrash() {
const dt=new Date();
const exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
const labels=['Cam1','Cam2'];
var idA=[];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getMessageCount()>0) {
var msgs=emailThreads[j].getMessages();
for(var k=0;i<msgs.length;j++) {
var msg=msgs[k];
if(new Date(msg.getDate()).valueOf()<exp) {
idA.push(msg.getId());
}
}
}
}
}
if(idA.length>0) {
idA.forEach(function(msg){msg.moveToTrash();});
}
}
Thanks Cooper. I tried for days but lacked the skills to debug your scripts. I found and modified the script below and it works.
First I created a new filter in gmail that labeled all cam emails 'Clean' then set the script below on a trigger. This was the only way I was able to achieve my goal.
Thanks again!
function gmailCleaningRobot() {
var delayDays = 1; // will only impact emails more than 24h old
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays); // what was the date at that time?
// creating an array containing all the search strings matching the emails we want to be treated automatically
var searches = [
'label:Clean older_than:1d' //with label clean and older than 1d
//'"is now available on Spotify" from:spotify is:unread', // Spotify new album notification
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE' // any other search string
];
// creating an array containing all the threads matching the searches above
var threads = [];
for (var i = 0; i < searches.length; i++) {
var tmp_threads = GmailApp.search(searches[i], 0, 500); // I limit the search to 500 results but you can adjust this one
var threads = threads.concat(tmp_threads);
}
// we archive all the threads if they're older than the time limit we set in delayDays
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate)
{
threads[i].moveToTrash();
}
}
}
Related
I have some production code that I am repurposing. Currently, it processes everything based on the gmail label and puts a specified portion into a spreadsheet. This will not change (though it could if that was the best way to solve this problem).
I now need to process a subset of those messages and put a portion of the message body into a spreadsheet.
The body of the text is as follows:
This is an email alarm.
Room Lights OFF.
(Other examples might say "Room Lights ON", "ALARM", or "OK".
I only need to get the portion of the body after "Room Lights".
Also included in this gmail label are emails that do not mention "Room Lights" at all. These should be ignored and not added to the spreadsheet.
Example:
This is an email alarm.
Network has returned.
or
This is an email alarm.
Generator is OFF.
Question: How can I edit my code so that only "Room Lights" emails are added to the spreadsheet?
Example code:
function lightFunction() {
newMonth();
var label = GmailApp.getUserLabelByName("Lights");
var label2 = GmailApp.getUserLabelByName("Processed");
var threads = label.getThreads();
var data = new Array();
var newData = new Array();
var alarmKeys = "This is an email alarm.";
var keys = alarmKeys.split(",");
var range = "A2:B";
// get all the email threads matching label
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
// archive thread
// label2.addToThread(threads[i]);
// label.removeFromThread(threads[i]);
// get each individual email from the threads
for (var j = 0; j < messages.length; j++) {
var bodyText = messages[j].getPlainBody();
for (k in keys) {
bodyText = bodyText.replace(keys[k], "");
}
bodyText = bodyText.trim();
var date = messages[j].getDate(); // date/time
var lines = [date, bodyText];
// Turn the first element in the array into a date element, format it, and put it back
lines[0] = Utilities.formatDate(new Date(lines[0]), "America/Phoenix", "M/d/yy HH:mm:ss");
// Put the array to a new item in the data array for further processing
if (curMonth == (new Date(lines[0]).getMonth())) {
data.push(lines);
}
}
}
checkAdd(data, range);
}
ALTERNATE METHOD
You could also tweak the part with data.push(lines); with regex using a match method in one line, something like this:
lines[1].match(/Room Lights|OK|ALARM/gm) ? data.push(lines) : null;
You may also check this sample regex test.
In my understanding here is the flow:
Read emails under labels Lights / Processed
Check each email that may match the following texts:
"Room Lights OFF", "Room Lights ON", "ALARM", or "OK"
Only add the matched emails on the spreadsheet
Tweaked Script
function lightFunction() {
newMonth();
var curMonth = new Date().getMonth()
var label = GmailApp.getUserLabelByName("Lights");
var label2 = GmailApp.getUserLabelByName("Processed");
var threads = label.getThreads();
var data = new Array();
var newData = new Array();
var alarmKeys = "This is an email alarm.";
var keys = alarmKeys.split(",");
var range = "A2:B";
// get all the email threads matching label
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
// archive thread
// label2.addToThread(threads[i]);
// label.removeFromThread(threads[i]);
// get each individual email from the threads
for (var j = 0; j < messages.length; j++) {
var bodyText = messages[j].getPlainBody();
for (k in keys) {
bodyText = bodyText.replace(keys[k], "");
}
bodyText = bodyText.trim();
var date = messages[j].getDate(); // date/time
var lines = [date, bodyText];
// Turn the first element in the array into a date element, format it, and put it back
lines[0] = Utilities.formatDate(new Date(lines[0]), "America/Phoenix", "M/d/yy HH:mm:ss");
// Put the array to a new item in the data array for further processing
if (curMonth == (new Date(lines[0]).getMonth())) {
//Tweaked part
lines[1].match(/Room Lights|OK|ALARM/gm) ? data.push(lines) : null;
//Tweaked part
}
}
}
checkAdd(data, range);
}
Demonstration
Sample emails
Log test of the data variable
References
Match
Regex
Conditional (ternary) operator
Although I'm not sure whether I could correctly understand your showing script, if your data is used in checkAdd(data, range); and data is put to the Spreadsheet, and you want to filter data by Question: How can I edit my code so that only "Room Lights" emails are added to the spreadsheet?, how about the following modification?
From:
var bodyText = messages[j].getPlainBody();
To:
var bodyText = messages[j].getPlainBody();
if (!bodyText.includes("Room Lights")) continue;
Or, if you want to check multiple texts, how about the following modification?
var bodyText = messages[j].getPlainBody();
var searchTexts = ["Room Lights", "sample",,,]; // Please set search texts you want.
if (!searchTexts.some(e => bodyText.includes(e))) continue;
Reference:
includes()
I'm a beginner working with Google Apps Script to pull data from a Google Doc, and I need some help...
I have a Google Doc that has a ton of cooking recipes. I'd like to write a function that randomly selects 4 recipes, and emails me the ingredients so I know what to shop for that week. All my recipes titles are 'Heading 3', with the ingredients as bullet points below them. I'm fully open to modifying the formatting if need be.
I think I have a way to identify all text that is of type 'Heading 3', but I need a way to store them in an array as text, to then randomly select 4 of them. I can't seem to solve this...
function onOpen() {
var ui = DocumentApp.getUi();
ui.createMenu('Generate Weekly Shopping List')
.addItem('Send Email', 'generateMenu')
.addToUi();
}
function generateMenu() {
var ps = DocumentApp.getActiveDocument().getBody()
var searchType = DocumentApp.ElementType.PARAGRAPH;
var searchHeading = DocumentApp.ParagraphHeading.HEADING3;
var searchResult = null;
while (searchResult = ps.findElement(searchType, searchResult)) {
var par = searchResult.getElement().asParagraph();
if (par.getHeading() == searchHeading) {
// Found one, update Logger.log and stop.
var h = searchResult.getElement().asText().getText();
return h;
//how do I store this back into an array...then randomly select 4?
}
// Get the email address of the active user - that's you.
var email = Session.getActiveUser().getEmail();
// Send yourself an email with a link to the document.
GmailApp.sendEmail(email, "Shopping List For The Week", "Here is the shopping list:" + h);
}
}
The first function generates an array of objects from your document
function generateObj() {
var body = DocumentApp.getActiveDocument().getBody()
var children=body.getNumChildren();
//var html='';
var rObj={rA:[]}
for(var i=0;i<children;i++) {
var child=body.getChild(i);
if(child.getType()==DocumentApp.ElementType.PARAGRAPH && child.asParagraph().getHeading()==DocumentApp.ParagraphHeading.HEADING3 && child.asParagraph().getText().length>0) {
//html+='<br />' + child.asParagraph().getText();
var prop=child.asParagraph().getText();
rObj.rA.push(prop);
rObj[prop]=[];
var n=1;
while(body.getChild(i+n).getType()==DocumentApp.ElementType.LIST_ITEM) {
//html+='<br />'+body.getChild(i+n).asListItem().getText();
rObj[prop].push(body.getChild(i+n).asListItem().getText());
n++;
}
i+=n-1;
}
}
//DocumentApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), 'Results')
return rObj;
}
//defaults to 4 selections
function pikn(n=4) {
var rObj=generateObj();//get array of objects
var rA=[];
var t=rObj.rA.slice();//copy array to temp array
for(var i=0;i<n;i++) {
var idx=Math.floor(Math.random()*t.length);//pick random index
rA.push(t[idx]);//save title of recipe
t.splice(idx,1);//remove that index
}
var s='';
//loop through selected recipes which are also object properties
rA.forEach(function(r,i){
var items=rObj[r];
s+='\n' + r;
items.forEach(function(item,j){s+='\n' + item});//loop through recipe items and collect everything in s as simple text to insert into standard body
});
GmailApp.sendEmail("Your email address","Random Recipes",s);
}
Requires Chrome V8
Is there a way to filter out all emails that came from a mailing list within Gmail or Google Apps Script using a search query. I know you can filter out a specific email address using list:info#example.com. But I want a catch-all type of query or even a query to catch-all from a specific domain such as list:#example.com. However, this does not work. Any ideas? Any help is greatly appreciated, thank you!
This function will trash all messages from all inbox thread that are not in the list.
function emailFilter() {
var list=['a#company.com','b#company.com','c#company.com','d#company.com','e#company.com'];
var threads=GmailApp.getInboxThreads();
var token=null;
for(var i=0;i<threads.length;i++) {
if(threads[i].getMessageCount()) {
var messages=threads[i].getMessages();
for(var j=0;j<messages.length;j++) {
if(list.indexOf(messages[j].getFrom()==-1)) {
messages[j].moveToTrash();
}
}
}
}
}
I haven't tested it because I keep my inbox empty all of the time. You might want to replace 'moveToTrash()' to 'star()' for testing
What I could understand from your question and your comments, you need to filter the emails in a user's inbox that he has received, which don't only contain a certain label, but also a certain domain. If I understood well this code can help you:
function checkLabels() {
// Get the threads from the label you want
var label = GmailApp.getUserLabelByName("Label Test List");
var threadArr = label.getThreads();
// Init variable for later use
var emailDomain = '';
// Iterate over all the threads
for (var i = 0; i < threadArr.length; i++) {
// for each message in a thread, do something
threadArr[i].getMessages().forEach(function(message){
// Let's get the domains from the the users the messages were from
// example: list:#example.com -> Result: example.com
emailDomain = message.getFrom().split('<').pop().split('>')[0].split('#')[1];
// if emailDomain is equal to example.com, then do something
if(emailDomain === 'example.com'){
Logger.log(message.getFrom());
}
});
}
}
Using the Class GmailApp I got a certain label with the .getUserLabels() method and iterate through the threads thanks to the .getInboxThreads method. With a second loop and the .getMessages() you can get all the messages in a thread and for knowing the one who sent them, just use the .getFrom() method.
Docs
For more info check:
Gmail Service.
Class GmailMessage.
Class GmailThread.
So I was able to avoid replying to emails that come from a mailing list address by using the getRawContent() method and then searching that string for "Mailing-list:". So far the script is working like a charm.
function autoReply() {
var interval = 5; // if the script runs every 5 minutes; change otherwise
var date = new Date();
var day = date.getDay();
var hour = date.getHours();
var noReply = ["email1#example.com",
"email2#example.com"];
var replyMessage = "Hello!\n\nYou have reached me during non-business hours. I will respond by 9 AM next business day.\n\nIf you have any Compass.com related questions, check out Compass Academy! Learn about Compass' tools and get your questions answered at academy.compass.com.\n\nBest,\n\nShamir Wehbe";
var noReplyId = [];
if ([6,0].indexOf(day) > -1 || (hour < 9) || (hour >= 17)) {
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('from:#example.com is:inbox after:' + timeFrom);
var label = GmailApp.getUserLabelByName("autoReplied");
var repliedThreads = GmailApp.search('label:autoReplied newer_than:4d');
// loop through emails from the last 4 days that have already been replied to
for (var i = 0; i < repliedThreads.length; i++) {
var repliedThreadsId = repliedThreads[i].getMessages()[0].getId();
noReplyId.push(repliedThreadsId);
}
for (var i = 0; i < threads.length; i++) {
var message = threads[i].getMessages()[0];
var messagesFrom = message.getFrom();
var email = messagesFrom.substring(messagesFrom.lastIndexOf("<") + 1, messagesFrom.lastIndexOf(">"));
var threadsId = message.getId();
var rawMessage = message.getRawContent();
var searchForList = rawMessage.search("Mailing-list:");
var searchForUnsubscribe = rawMessage.search("Unsubscribe now");
// if the message is unread, not on the no reply list, hasn't already been replied to, doesn't come from a mailing list, and not a marketing email then auto reply
if (message.isUnread() && noReply.indexOf(email) == -1 && noReplyId.indexOf(threadsId) == -1 && searchForList === -1 && searchForUnsubscribe === -1){
message.reply(replyMessage);
threads[i].addLabel(label);
}
}
}
}
I am writing the Date and Subject from specific new emails to a new row of a Google Sheet.
I apply a label to the new mail items with a filter.
the script processes those labeled emails
the label is removed
A new label is applied, so that these emails won't be processed next time.
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).
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?
My current script:
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);
}
}
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--)
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");
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:
The (enable-before-use "advanced service") Gmail REST API
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; });
}
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");
}
Result:
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);
}
Recommended Reading:
Advanced Services
Gmail Service
Messages#list
Message#batchModify
Partial responses aka the 'fields' parameter
Tracked Processing
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.
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);
}
}
Usage would be something like:
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
}
Recommended Reading:
Is checking an object for a key more efficient than searching an array for a string?
Array#reduce
Array#map
Array#forEach
I used to have a working apps script that deleted old promotional emails in my Gmail account.
It used the code
var label = GmailApp.getUserLabelByName("Promotions");
to get the label and then it iterated through label.getThreads() to decide whether each thread was old enough to delete.
However, Google have now changed Gmail so the auto categorisations are now under the Categories section in the UI, rather than within the list of labels so the above now returns null.
How can I fix my code to retrieve the Promotions category? I have tried Categories/Promotions too but it also comes up null.
Gmail categories can easily be searched.
Here is a small code that looks for every promotion mail. The result is an array of threads, you can add Label to each of them so that your old script will be happy again ;-)
var threads = GmailApp.search('category:promotions');// check the category Gmail added to the thread
documentation here
Here is a script I use to delete old promotional emails as well as some other categories and custom labels.
function auto_delete_email(){
delete_Label ("Cameras",30);
delete_Label ("Travel",365);
delete_Category ("Social",90);
delete_Category ("Finance",365*3);
delete_Category ("Forums",90);
delete_Category ("Promos",365*3);
}
function delete_Label(mailLabel,delayDays) {
var label = GmailApp.getUserLabelByName(mailLabel);
if (!label) {return false;}
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var threads = label.getThreads();
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate){
threads[i].moveToTrash();
}
}
function delete_Category(mailCategory,delayDays) {
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var threads = GmailApp.search('category:' + mailCategory);
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate){
threads[i].moveToTrash();
}
}
}
You can also expand the search criteria to use (d, m, y) like "older_than:1y" which mimics the search box functionality in the Gmail app. This may be helpful as I've had significant issues getting the date comparison to work smoothly.
For example:
function delete_old_Category() {
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()- 180);
var threads = GmailApp.search("category:promos older_than:6m",0, 100);
for (var i = 0; i < threads.length; i++) {
threads[i].moveToTrash(); {
}
}
}