Labeling Gmail message (not the whole thread) with Google Apps Script - google-apps-script

Is it possible to search to messages with the label 'Apps script queue' and give just these specific messages (not the whole thread) a new label?
When I use GmailApp.search('label:Apps script queue') I get the requested messages but when I assign a new label to these messages, all the other messages of the thread (on other places in the mailbox) will get the same label. And that is not what I want.

This code does not return an error while adding a label to a specific message in a thread and if you use thread list method you'll see that it is only placed in the specific messageID(treated separately). But once your UI(Gmail site) is in conversation mode, it will be viewable in both labels.
function searchMail(){
var threads = GmailApp.search("SOME SEARCH");
Logger.log(threads.length);
listLabel('me');
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
Logger.log(messages.length);
for (var j = 0; j < messages.length; j++){
if (messages[j].isInInbox()){
Logger.log('me' + 'id msg: ' + messages[j].getId());
//Add label to the first reply
addLabel('me',messages[1].getId());
}
else{
Logger.log('me' + 'id msg: ' + messages[j].getId() +" not in inbox");
}
}
}
}
function addLabel(userId, messageId){
var resource = {addLabelIds: ["Label_6"]}
Gmail.Users.Messages.modify(resource, userId, messageId);
}

In Gmail, labels are applied to a thread and cannot be applied to a single email message of a thread.
You can however apply stars / colors to individual messages.

This is an old thread, but for anybody who might be reading it like me, maybe this will save you some time:
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:Apps-script-queue'}).messages || [];
// 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, "Apps script queue");
labelMessage(messageToRelabel.id, "New label");
});
}
Not asked for by the OP, but may be helpful for others who are trying to do "advanced filtering / labeling" using the 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;
}
}
The above lets you filter on header info, e.g. I use it to check whether X-Uniform-Type-Identifier is equal to com.apple.mail-note to automatically flag old Apple Notes for deletion.

Related

Is there any way to see the names of a website's network requests with Google apps scripts?

So here's what I'm trying to do:
There's a website called Torah Anytime (https://www.torahanytime.com/) which publishes audio files (I guess you can call them podcasts, the website refers to them as shiurim, shiur being hebrew for song, or in this case, audio) on a daily basis. I would like to create a script that downloads the audio of specific speakers and then emails those files to me. The way I'm accomplishing this is with Google Apps Scripts. Torah Anytime allows you to follow specific speakers and to get email notifications when a speaker you're following puts out a new podcast. Here is the code that I have so far:
function main() {
var emails = getemails();
for (var i = 0; i < emails.length; i++) {
var email = emails[i].getMessages();
if (email[0].getFrom() == "TorahAnytime Following <following#torahanytime.com>"){
var title = getTitle(email);
var shiurID = getShiurID(email);
var downloadLink = "https://dl.torahanytime.com/audio/" + shiurID;
var shiur = downloadShiur(downloadLink);
shiur.setName(title);
var emailSent = emailShiur(shiur);
if (emailSent) {email[0].moveToTrash();
Logger.log("Email moved to Trash");}
}
}
}
function getemails() {
var label = GmailApp.getUserLabelByName("TA Speeches");
return label.getThreads();
}
function getTitle(email) {
body = email[0].getPlainBody();
var begIndex = body.indexOf("from") + 4;
var endIndex = body.indexOf("on ");
var title = body.substring(begIndex, endIndex).toLowerCase().replaceAll(" ", "-").replace(/(\r\n|\n|\r)/gm, "-");
begIndex = body.indexOf("called ") + 7;
endIndex = body.indexOf(" [");
title += "-" + body.substring(begIndex, endIndex).toLowerCase().replaceAll(" ", "-").replace(/(\r\n|\n|\r)/gm, "-") + ".mp3";
return title;
}
function getShiurID(email) {
body = email[0].getPlainBody();
var begIndex = body.indexOf("[") + 1;
var endIndex = body.indexOf("]");
var link = body.substring(begIndex, endIndex).replaceAll("?v", "?a");
console.log(link);
var mainLink = UrlFetchApp.fetch(link);
//here I somehow need to get the link being used to stream that particular audio file
}
function getIDName(email) {
body = email[0].getPlainBody();
var begIndex = body.indexOf("ID ") + 3;
var endIndex = body.indexOf(" and");
return body.substring(begIndex, endIndex);
}
function downloadShiur(downloadLink) {
var audio = UrlFetchApp.fetch(downloadLink);
return audio.getBlob().getAs('audio/mp3');
}
function emailShiur(shiur) {
const maxFileSize = 26214400;
if (shiur.getBytes().length <= maxFileSize) {
MailApp.sendEmail("[Email addressed removed]", "TA Shiur (File)", "Enjoy!", {
attachments: [shiur],
name: 'Automatic Emailer Script'
});
return true;
} else {
MailApp.sendEmail("[Email addressed removed]", "TA Shiur (File)", "Error, File too large to email", {
name: 'Automatic Emailer Script'
});
return false;
}
}
My issue is that the URL to download the file is not in the HTML, so I don't know how to get to it using GAS. If you use chrome's dev-tools, you can see the URL right there in the network tab Example of output I see that I want to get. Does anyone know of any way that I can get the information that I see in chrome's dev-tools network tab (the name's of the URLs being received) using GAS? Thank you!

Getting past permissions for a file through the API/Apps Script

I'm trying to create a list of files stored in my Google Drive and also a list of their current and previous permissions. Specifically, I want to create a list of files in my Google Drive which at any point in the past have had the 'Anyone with a link can view/edit (etc)' permission set.
I have created a Google Apps Script to do this and I can iterate through all the files OK and I can get files which currently have that permission set, but I can't see a way to get the history of the file's permissions.
I have found and activated the revisions list API: https://developers.google.com/drive/api/v2/reference/revisions/list
This gets revisions but I can't see anywhere that it lists the sharing history of a revision.
Is what I'm attempting to do possible?
It's definitely possible using the Drive Activity API. You can use the Quickstart for Google Apps Script to view all the activity of an item (file or folder) or done by a User. In this case I modified the Quickstart to show the Permissions changes of a given Drive Id.
function listDriveActivity() {
var request = {
itemName: "items/1bFQvSJ8pMdss4jInrrg7bxdae3dKgu-tJqC1A2TktMs", //Id of the file
pageSize: 10};
var response = DriveActivity.Activity.query(request);
var activities = response.activities;
if (activities && activities.length > 0) {
Logger.log('Recent activity:');
for (var i = 0; i < activities.length; i++) {
var activity = activities[i];
var time = getTimeInfo(activity);
var action = getActionInfo(activity.primaryActionDetail);
var actors = activity.actors.map(getActorInfo);
var targets = activity.targets.map(getTargetInfo);
if (action == "permissionChange"){ //Only show permissionChange activity
Logger.log(
'%s: %s, %s, %s', time, truncated(actors), action,
truncated(targets));
}
}
} else {
Logger.log('No activity.');
}
}
/** Returns a string representation of the first elements in a list. */
function truncated(array, opt_limit) {
var limit = opt_limit || 2;
var contents = array.slice(0, limit).join(', ');
var more = array.length > limit ? ', ...' : '';
return '[' + contents + more + ']';
}
/** Returns the name of a set property in an object, or else "unknown". */
function getOneOf(object) {
for (var key in object) {
return key;
}
return 'unknown';
}
/** Returns a time associated with an activity. */
function getTimeInfo(activity) {
if ('timestamp' in activity) {
return activity.timestamp;
}
if ('timeRange' in activity) {
return activity.timeRange.endTime;
}
return 'unknown';
}
/** Returns the type of action. */
function getActionInfo(actionDetail) {
return getOneOf(actionDetail);
}
/** Returns user information, or the type of user if not a known user. */
function getUserInfo(user) {
if ('knownUser' in user) {
var knownUser = user.knownUser;
var isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
/** Returns actor information, or the type of actor if not a user. */
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user)
}
return getOneOf(actor);
}
/** Returns the type of a target and an associated title. */
function getTargetInfo(target) {
if ('driveItem' in target) {
var title = target.driveItem.title || 'unknown';
return 'driveItem:"' + title + '"';
}
if ('drive' in target) {
var title = target.drive.title || 'unknown';
return 'drive:"' + title + '"';
}
if ('fileComment' in target) {
var parent = target.fileComment.parent || {};
var title = parent.title || 'unknown';
return 'fileComment:"' + title + '"';
}
return getOneOf(target) + ':unknown';
}
Remember to enable the Drive Activity API in Resources > Advanced Google Services
In my example this returns the logs:
You can also look deeper into the Permissions by using the permissionChange Parameters in the query.
If you have a business/enterprise/edu account the admin audit logs will tell you this for 6 months of data. Or it will at least tell you when a permission was changed from x to y.
Can't think of a method for personal.

Unable to get this "tagging unanswered email" script to work

The following is a script that I found online to tag all the unanswered emails. It works for one of my gmail accounts, however when I shared it to another account and run it, it returns nothing every single time, even though there are unanswered emails within that time range. I then tried copy and paste the codes into a new project, however still wouldn't work.
Anyone has any ideas?
Thanks in advance!
/*
* This script goes through your Gmail Inbox and finds recent emails where you
* were the last respondent. It applies a nice label to them, so you can
* see them in Priority Inbox or do something else.
*
* To remove and ignore an email thread, just remove the unrespondedLabel and
* apply the ignoreLabel.
*
* This is most effective when paired with a time-based script trigger.
*
* For installation instructions, read this blog post:
* http://jonathan-kim.com/2013/Gmail-No-Response/
*/
// Edit these to your liking.
var unrespondedLabel = 'No Response',
ignoreLabel = 'Ignore No Response',
minDays = 0.125,
maxDays = 5;
function main() {
processUnresponded();
cleanUp();
}
function processUnresponded() {
var threads = GmailApp.search('is:sent from:me -in:chats older_than:' + minDays + 'd newer_than:' + maxDays + 'd'),
numUpdated = 0,
minDaysAgo = new Date();
minDaysAgo.setDate(minDaysAgo.getDate() - minDays);
// Filter threads where I was the last respondent.
for (var i = 0; i < threads.length; i++) {
var thread = threads[i],
messages = thread.getMessages(),
lastMessage = messages[messages.length - 1],
lastFrom = lastMessage.getFrom(),
lastMessageIsOld = lastMessage.getDate().getTime() < minDaysAgo.getTime();
if (isFromMe(lastFrom) && lastMessageIsOld && !threadHasLabel(thread, ignoreLabel)) {
markUnresponded(thread);
numUpdated++;
}
}
Logger.log('Updated ' + numUpdated + ' messages.');
}
function isFromMe(fromAddress) {
var addresses = getEmailAddresses();
for (i = 0; i < addresses.length; i++) {
var address = addresses[i],
r = RegExp(address, 'i');
if (r.test(fromAddress)) {
return true;
}
}
return false;
}
function getEmailAddresses() {
var me = Session.getActiveUser().getEmail(),
emails = GmailApp.getAliases();
emails.push(me);
return emails;
}
function threadHasLabel(thread, labelName) {
var labels = thread.getLabels();
for (i = 0; i < labels.length; i++) {
var label = labels[i];
if (label.getName() == labelName) {
return true;
}
}
return false;
}
function markUnresponded(thread) {
var label = getLabel(unrespondedLabel);
label.addToThread(thread);
}
function getLabel(labelName) {
var label = GmailApp.getUserLabelByName(labelName);
if (label) {
Logger.log('Label exists.');
} else {
Logger.log('Label does not exist. Creating it.');
label = GmailApp.createLabel(labelName);
}
return label;
}
function cleanUp() {
var label = getLabel(unrespondedLabel),
iLabel = getLabel(ignoreLabel),
threads = label.getThreads(),
numExpired = 0,
twoWeeksAgo = new Date();
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - maxDays);
if (!threads.length) {
Logger.log('No threads with that label');
return;
} else {
Logger.log('Processing ' + threads.length + ' threads.');
}
for (i = 0; i < threads.length; i++) {
var thread = threads[i],
lastMessageDate = thread.getLastMessageDate();
// Remove all labels from expired threads.
if (lastMessageDate.getTime() < twoWeeksAgo.getTime()) {
numExpired++;
Logger.log('Thread expired');
label.removeFromThread(thread);
iLabel.removeFromThread(thread);
} else {
Logger.log('Thread not expired');
}
}
Logger.log(numExpired + ' unresponded messages expired.');
}
The Gmail search operator "older_than" does not support decimals, so you cannot use "0.125" in this case. Make sure you use an integer number/day. The script will not return errors, but the search will not work. More info about the Gmail search operators at https://support.google.com/mail/answer/7190?hl=en

Google Script for Gmail not consistent

I have a filter that adds the "unprocessed" label on all incoming emails.
Then a Google Script searches every minute for any email threads that have the "unprocessed" label, processes the messages, and conditionally apply a label to the corresponding thread.
I don't know what I have done wrong, but only SOME of the processed threads get the label. And it works randomly... For example only 3 out of 6 threads got the label, or 1 out of 3.
I have to re-apply the "unprocessed" label, and just run the script again to fix them.
function processGmail() {
var startTime = new Date().getTime();
var mailerRegex = /X-Mailer:(.*)/g;
var scannerLabel = GmailApp.getUserLabelByName("Scanner");
var unprocessedLabel = GmailApp.getUserLabelByName("unprocessed");
var countMessages = 0;
GmailApp.search("label:unprocessed").forEach(
function(emailThread) {
emailThread.getMessages().forEach(
function(message) {
var raw = message.getRawContent();
var result;
var doReturn = false;
while((matches = mailerRegex.exec(raw)) !== null) {
if (matches.some(function(match){return match.indexOf('Canon MFP') >= 0;})) {
emailThread.addLabel(scannerLabel);
emailThread.moveToArchive();
doReturn = true;
break;
}
}
emailThread.removeLabel(unprocessedLabel);
++countMessages;
if (doReturn) {
return;
}
}
);
}
);
var endTime = new Date().getTime();
Logger.log("Processed " + countMessages + " in " + (endTime-startTime) + "ms.");
}
Turns out the bug was Javascript related.
I had forgotten that the regex.exec needs to be looped until a null is returned, only then it will start a-new for a new input.
The fix was removing break :)

Getting list of child OU in Apps Script

Google Directory provide API to get all organizational units from domain.
I'm able to get OU inside "/" but I want to get all child OU.
There is function with some arguments:
AdminDirectory.Orgunits.list(String costumerId, Object optionalArg)
This function accept "type" argument as mentioned in official documentation, but when I put argument "orgUnitPath" it will respond with "Bad request"
Here is my full code
var page = AdminDirectory.Orgunits.list('my_costumer',{
orgUnithPath: '/Kiosks',
type: 'all'});
var units = page.organizationUnits;
if (units) {
for (var i = 0; i < units.length; i++) {
var unit = units[i];
Logger.log(unit);
}
} else {
Logger.log('No units found.');
}
Thank you very much
You mistyped 'my_customer' and orgUnitPath. Also, organizationalUnits is going to be an array of objects that you will want to get specific properties of such as orgUnit.name, not log directly.
function listSubOUs() {
var page = AdminDirectory.Orgunits.list('my_customer', {
orgUnitPath: '/',
type: 'all'
});
var orgUnits = page.organizationUnits;
if (orgUnits) {
for (var i = 0; i < orgUnits.length; i++) {
var orgUnit = orgUnits[i];
Logger.log('%s (%s)', orgUnit.name, orgUnit.orgUnitPath, orgUnit.description);
}
} else {
Logger.log('No OUs found.');
}
}