Google Apps Script Google People API get contacts from contact group - google-apps-script

I have a script (using Google Apps Script) that uses the Contacts API to pull the emails from a contactgroup, then sends an email to the group of contacts. Attempting to convert to the PeopleAPI and cannot seem to replicate the functionality. Here's the relevant script sample with the working ContactsAPI code included but commented out:
function sharereport() {
//var CGroup = ContactsApp.getContactGroup('group_name');
//var Dist = CGroup.getContacts();
var CGroup = People.ContactGroups.get('contactGroups/498ba6e40f63a476')
var Dist = People.People.getBatchGet('CGroup','people.email_addresses');
.
.
.
for (var i = 0; i < Dist.length; i++) {
var nextemail = Dist[i].getEmails();
var usethisemail = nextemail[0].getAddress();
Drive.Permissions.insert(
// #ts-ignore
{
'role': 'writer',
'type': 'user',
'value': usethisemail,
},
MyID,
{
'sendNotificationEmails': 'false',
});
MailString = MailString + ',' + usethisemail;
};
I'm sure I'm missing something really simple here to get the PeopleAPI to return an array of contacts that I can get the email addresses out of so I can populate the drive permissions and email to: field.

Since I did not found any methods getting them directly, I did use People.People.Connections.list and filtered the data till I get the emails. This should what your code will look like.
function sharereport() {
var CGroup = ContactsApp.getContactGroup('label1');
var Emails = CGroup.getContacts().map(function (contact) {
return contact.getEmailAddresses();
});
// Show emails of people belonging to the group label1
Logger.log(Emails);
var PGroup = People.People.Connections.list('people/me', {
personFields: 'emailAddresses,memberships'
});
// resource name of label1
var resourceName = 'contactGroups/7086c0fa8e7b006b';
var Emails2 = [];
PGroup.connections.forEach(function (person) {
person.memberships.forEach(function (membership) {
if (resourceName == membership.contactGroupMembership.contactGroupResourceName) {
var addresses = [];
person.emailAddresses.forEach(function (emailAddress){
// people can have multiple email addresses, add them all
addresses.push(emailAddress.value);
});
Emails2.push(addresses);
}
});
});
Logger.log(Emails2);
}
Behavior:
get all the people connections under you.
get all their memberships
check if their memberships resource name is equal to the one you want. (they can belong to multiple memberships)
loop all emails of that certain person (they can have multiple emails), then push into one array.
push that array into final array containing all emails of all people belonging to the resource name.
Output:
Logged both ContactsApp and People API results, below shows that they were the same.
Resources:
people.connections/list
advanced/people

Here's a more efficient way of doing what you want:
var group = People.ContactGroups.get('contactGroups/50e3b0650db163cc', {
maxMembers: 25000
});
Logger.log("group: " + group);
var group_contacts = People.People.getBatchGet({
resourceNames: group.memberResourceNames,
personFields: "emailAddresses"
});
Logger.log("emails: " + group_contacts.responses.map(x => {
var emailObjects = x.person.emailAddresses;
if (emailObjects != null) {
return emailObjects.map(eo => eo.value);
}
}));
First call gets all the group's members (resourcesNames AKA contactIds)
Second call gets all the members' email-addresses.
Then we just get the actual value of the email from the response (via map)

Related

Change Gmail label once attachment is uploaded to drive

How can you remove the existing label "Global Alcohol" & add a "Global Processed" label to the email messages which have had the attachments uploaded to Google Drive using the following code? I must give credit to Cooper who answered my first question & helped me get the uploads working.
function saveAttachmentInFolder(){
var folder = DriveApp.getFolderById('xxxxxxxxxxxxx');
var userId = "myemail#gmail.com";
var query = "label:Global Alcohol";
var res = Gmail.Users.Messages.list(userId, {q: query});//I assumed that this works
res.messages.forEach(function(m){
var attA=GmailApp.getMessageById(m.id).getAttachments();
attA.forEach(function(a){
var ts=Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "yyMMddHHmmss");
folder.createFile(a.copyBlob()).setName(a.getName()+ts);
});
});
}
I have read the API documentation & can see that you need to use the following code to modify the Labels. However I am stuck with how to integrate it into the function above.
function modifyMessage(userId, messageId, labelsToAdd, labelsToRemove, callback) {
var request = gapi.client.gmail.users.messages.modify({
'userId': userId,
'id': messageId,
'addLabelIds': labelsToAdd,
'removeLabelIds': labelsToRemove
});
request.execute(callback);
}
You were on the right track with the modification call, but the formatting is slightly off. The trick here is that you need to use the label IDs, so I wrote a new function getLabelsByName() that allows you to perform that lookup.
function saveAttachmentInFolder(){
var folder = DriveApp.getFolderById('xxxxxxxxxxxxx');
var userId = "myemail#gmail.com";
var query = "label:Global Alcohol";
var labels = getLabelsByName(userId, ["Global Alcohol", "Global Processed"]);
var res = Gmail.Users.Messages.list(userId, {q: query});//I assumed that this works
res.messages.forEach(function(m){
var attA=GmailApp.getMessageById(m.id).getAttachments();
attA.forEach(function(a){
var ts=Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "yyMMddHHmmss");
folder.createFile(a.copyBlob()).setName(a.getName()+ts);
});
// Remove the old label & add the new one
Gmail.Users.Messages.modify({
addLabelIds: [labels["Global Processed"].id],
removeLabelIds: [labels["Global Alcohol"].id]
}, userId, m.id);
});
}
/**
* Lookup any number of labels by their name using the advanced Gmail service.
* #param {String} userId - The user's email address or "me" to get your own
* #param {String[]} labelNames - An array of labels names to search for
* #returns {Label{}} - Map of labels identified by label name
* https://developers.google.com/gmail/api/v1/reference/users/labels
*/
function getLabelsByName(userId, labelNames) {
var response = Gmail.Users.Labels.list(userId);
var selectedLabels = {};
for (var i = 0; i < response.labels.length; i++) {
var label = response.labels[i];
if (labelNames.indexOf(label.name) != -1) {
selectedLabels[label.name] = label;
}
}
return selectedLabels;
}

Fetch the files of suspended users

Is there a method in google apps script to get the files of a suspended owner based on his/her email id? I am literally trying to implement transfer drive files to a new owner using google apps script, I am using this code to find the details of all the suspended users:
/**
* Fetches the suspended user details from the AdminDirectory.
*/
function fetchUser() {
// Set the constant options only once.
const options = {
domain: 'xyz.com',
orderBy: 'email',
query: 'isSuspended=true',
maxResults: 500,
fields: "nextPageToken,users"
};
// Could log the options here to ensure they are valid and in the right format.
const results = [];
do {
var search = AdminDirectory.Users.list(options);
// Update the page token in case we have more than 1 page of results.
options.pageToken = search.nextPageToken;
// Append this page of results to our collected results.
if(search.users && search.users.length)
Array.prototype.push.apply(results, search.users);
} while (options.pageToken);
//Logger.log(results);
for(var k = 0; k < results.length; k++){
var fullEmail = results[k].primaryEmail;
Logger.log(fullEmail);
fetchFiles(fullEmail);
}
}
/**
* Fetches the files of the suspended users based on their email.
*/
function fetchFiles(email){
var pageToken;
var filesList = Drive.Files.list({ // Invalid value error is thrown here, I am not sure if this the right way to use Drive API in google script
domain: 'xyz.com',
orderBy: 'email',
q: "'email' in owners",
maxResults: 500,
pageToken: pageToken
});
Logger.log('filesList:' +filesList);
}
I am trying to implement something like Transfer ownership of a file to another user in Google Apps Script, but is there some way by which I can fetch the files of the user from the details obtained from the above code which basically returns the following output:
[
{
orgUnitPath = /,
ipWhitelisted = false,
gender = {type = other},
creationTime = 2017-06-13T14:38:44.000Z,
isMailboxSetup = true,
isEnrolledIn2Sv = false,
kind = admin#directory#user,
suspensionReason = ADMIN,
isAdmin = false,
suspended = true,
emails = [{
address = john.seb#xyz.com,
primary = true
}],
lastLoginTime = 2017-09-12T00:27:00.000Z,
isDelegatedAdmin = false,
isEnforcedIn2Sv = false,
changePasswordAtNextLogin = false,
customerId = v12idsa,
name = {
givenName = John,
familyName = Seb,
fullName = John Seb
},
etag = "npJcgeAc7Xbfksfwer22344/sfasdfaffUDsfdsjfhsfhsdfw-ec",
id = 1033392392142423424,
primaryEmail = john.seb#xyz.com,
agreedToTerms = true,
includeInGlobalAddressList = true
},
...
]
I am trying to use the Drive API in the google apps script in order to access the files of suspended users using their email, but its throwing
"Invalid Value" error
for the line Drive.Files.list, I am not sure if this is the right way to use the DRIVE API in google script, is there any other way to use this api in google script?
In your function fetchFiles, you never use the input argument email. Instead, you query the Drive REST API for the literal text 'email' in owners. Since the text string 'email' is not a valid email address, you correctly receive the error message "Invalid value".
Rather than this code:
/**
* Fetches the files of the suspended users based on their email.
*/
function fetchFiles(email){
var pageToken;
var filesList = Drive.Files.list({
domain: 'jerseystem.org',
orderBy: 'email',
q: "'email' in owners",
maxResults: 500,
pageToken: pageToken
});
Logger.log('filesList:' +filesList);
}
You should perform the substitution first, to set up all constant options (as I mentioned and demonstrated in your other question), and then repeatedly query the Drive API for the files owned by the user with that email address:
function fetchAllFilesOwnedByEmail(email) {
const searchParams = {
corpora: 'some corpora',
orderBy: 'some ordering criteria',
q: "'" + email + "' in owners",
fields: 'nextPageToken,items(id,title,mimeType,userPermission)'
};
const results = [];
do {
var search = Drive.Files.list(searchParams);
if (search.items)
Array.prototype.push.apply(results, search.items);
searchParams.pageToken = search.nextPageToken;
} while (searchParams.pageToken);
return results;
}
You need to review the Drive REST API documentation, see Drive.Files.list and Search for Files at minimum. Don't forget to enable the Drive advanced service if you haven't.
Also note that while the above code will resolve the "Invalid Value" error you get, it won't necessarily make the user's files show up, even if you're executing the code as the G-Suite admin. Your research should have turned up at least these two related questions:
How can an Admin access the Google Drive contents of all the users in a particular domain?
Most efficient process for transferring ownership of all of a user's files using the Google Drive API

How can I present customer data from spreadsheet into form in app maker for update?

I have struggling to present available data for selected customer from spreadsheet into app maker form incase staff want to change it or update empty fields.
Client side code:
function getDetails() {
var props = app.currentPage.properties;
var page = app.pages.Search;
var Channel = app.datasources.Update.items;
var Customer = page.descendants.Sheets.value;
props.Loading = true;
props.Error = null;
google.script.run
.withFailureHandler(function(error) {
props.Loading = false;
props.Error = JSON.stringify(error);
console.error(error);
})
.withSuccessHandler(function(Channel) {
props.Loading = false;
page.Channel = Channel;
var items = [];
items = getChannels(props.SelectedSheet);
Channel.items.load(); // this line dosen't work and it doesn't load the data into form
if (Channel && Channel.length > 0) {
page.SelectedSheet = Channel[0];
} })
.getDetails(props.SelectedSheet);
}
Server side code:
function getDetails()(customer){
var spreadSheet = SpreadsheetApp.openById("***").getSheetByName('TRACKER');
var data=spreadSheet.getDataRange().getValues();
var channels = [];
var Name = customer;
var string1 = Name;
var array1 = string1.split(";"); // in here I extract row number belong to customer to get data
var destrow = [];
destrow.push(data[array1[0]][0],data[array1[0]][1],data[array1[0]][2],data[array1[0]][3],data[array1[0]][4],data[array1[0]][5]);
channels.push(destrow);
// return channels;
return channels.map(function(Channel){
return Channel;}); // return array of field data to presented in app maker form
}
Thank you for any answer or suggestion.
Cheers
In theory, this code should throw exception, since Channel is array and array doesn't have load method:
function getDetails() {
...
var Channel = app.datasources.Update.items;
...
// your first Channel variable is never used and is overridden with
// Channel callback parameter
.withSuccessHandler(function(Channel) {
// this line does nothing, since all App Maker objects are sealed
page.Channel = Channel;
// TypeError: load is not a function
Channel.items.load();
...
}
It is not clear from you code, what you are trying to do... Try to debug it and look into browser console more often (F12 or Ctrl + Shift + J).
Further reading:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal

Create a native filter in Gmail using Google Apps Script

I want to make a filter that is native to gmail for certain set of parameters.
Basically I use the google alias function a lot (the + sign after your email). I want to automate the process of creating a filter that reads the "To" line then looks for a "+". If the a "+" is found it will make a label of what is after the "+". Then it will create a dedicated/native filter that will: skip the inbox and apply the label of what is after the "+".
I have looked through the gmail scripts and have not found a way to make a native filter. I understand that this functionality might have just been implemented.
function getTo() {
// Log the To line of up to the first 50 emails in your Inbox
var email = Session.getActiveUser().getEmail();
Logger.log(email);
var threads = GmailApp.getInboxThreads(0, 50);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
Logger.log(messages[j].getTo()||email);
}
}
}
Any help would be great.
Solution:
// Creates a filter to put all email from ${toAddress} into
// Gmail label ${labelName}
function createToFilter(toAddress, labelName) {
// Lists all the filters for the user running the script, 'me'
var labels = Gmail.Users.Settings.Filters.list('me')
// Search through the existing filters for ${toAddress}
var label = true
labels.filter.forEach(function(l) {
if (l.criteria.to === toAddress) {
label = null
}
})
// If the filter does exist, return
if (label === null) return
else {
// Create the new label
GmailApp.createLabel(labelName)
// Lists all the labels for the user running the script, 'me'
var labelids = Gmail.Users.Labels.list('me')
// Search through the existing labels for ${labelName}
// this operation is still needed to get the label ID
var labelid = false
labelids.labels.forEach(function(a) {
if (a.name === labelName) {
labelid = a
}
})
Logger.log(labelid);
Logger.log(labelid.id);
// Create a new filter object (really just POD)
var filter = Gmail.newFilter()
// Make the filter activate when the to address is ${toAddress}
filter.criteria = Gmail.newFilterCriteria()
filter.criteria.to = toAddress
// Make the filter remove the label id of ${"INBOX"}
filter.action = Gmail.newFilterAction()
filter.action.removeLabelIds = ["INBOX"];
// Make the filter apply the label id of ${labelName}
filter.action.addLabelIds = [labelid.id];
// Add the filter to the user's ('me') settings
Gmail.Users.Settings.Filters.create(filter, 'me')
}
}
Thanks, Ender
The functionality seems to exist, but requires enabling the full Gmail API.
For your Apps script, follow the directions at https://developers.google.com/apps-script/guides/services/advanced to enable Advanced Google Services.
When I first tried using the Gmail.Users.Settings.Filters.list('me') call I got an authorization error which gave me a link to enable the Gmail API in the Developer Console which just amounted to flipping a switch.
Once you have this enabled, you can write a script to make filters, like
//
// Creates a filter to put all email from ${toAddress} into
// Gmail label ${labelName}
//
function createToFilter (toAddress, labelName) {
// Lists all the labels for the user running the script, 'me'
var labels = Gmail.Users.Labels.list('me')
// Search through the existing labels for ${labelName}
var label = null
labels.labels.forEach(function (l) {
if (l.name === labelName) {
label = l
}
})
// If the label doesn't exist, return
if (label === null) return
// Create a new filter object (really just POD)
var filter = Gmail.newFilter()
// Make the filter activate when the to address is ${toAddress}
filter.criteria = Gmail.newFilterCriteria()
filter.criteria.to = toAddress
// Make the filter apply the label id of ${labelName}
filter.action = Gmail.newFilterAction()
filter.action.addLabelIds = [label.id]
// Add the filter to the user's ('me') settings
Gmail.Users.Settings.Filters.create(filter, 'me')
}
function main () {
createToFilter('youruser+foo#gmail.com', 'Aliases/foo')
}
I wasn't able to figure out how to create a filter that retroactively labels messages automatically since Google doesn't seem to expose that in their API.

Google Apps Script Class GmailApp Batch Operations?

I've been fooling around with GAS for a month or so now, and I've become fairly familiar with using batch operations to read/write to/from spreadsheets (e.g. getValues(), setValues()). However, I'm currently writing a script that pulls a sizable amount of data out of Gmail using class GmailApp, my code is running very slowly (and even timing out), and I can't seem to figure out how to use batch operations for what I'm trying to do. Here's my code thus far (with the email address and name changed):
function fetchEmails(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var threads = GmailApp.search('in: label:searchedLabel');
var messages = new Array();
function Email(message){
this.date = new Date(message.getDate());
this.body = message.getBody();
}
for(var i=0;i<threads.length;i++){
for(var j=0;j<threads[i].getMessageCount();j++){
if(threads[i].getMessages()[j].getFrom()=="firstName lastName <email#domain.com>"){
var message = new Email(threads[i].getMessages()[j]);
messages.push(message);
}
}
}
}
As you can see, I'm querying my email for all threads with the given label,
making an object constructor for a custom Email object (which will have the body and date of an email as properties). Then I'm looping through each thread and when a given email matches the sender I'm looking for, I create an instance of the Email object for that email and place that Email object into an array. The goal is that in the end I'll have an array of Email objects that are all from my desired sender. However as you've probably noticed the code calls Google's APIs way too often, but I can't seem to figure out batch operations for interfacing with Gmail. Any ideas? Thanks so much.
I think you are looking for GmailApp.getMessagesForThreads().
function fetchEmails(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var threads = GmailApp.search('label:searchedLabel');
var messages = new Array();
function Email(message){
this.date = new Date(message.getDate());
this.body = message.getBody();
}
var gmailMessages = GmailApp.getMessagesForThreads(threads);
for(var i=0;i<thread.length;i++){
var messagesForThread = gmailMessages[i];
for(var j=0;j<messagesForThread.length;j++){
if(messagesForThread[j].getFrom()=="firstName lastName <email#domain.com>"){
var message = new Email(messagesForThread[j]);
messages.push(message);
}
}
}
}
Of course you can also write this a little more concisely (sorry, I can't turn up an opportunity to educate about the wonders of JavaScript):
function fetchEmails(){
var messages = Array.prototype.concat.apply([], GmailApp.getMessagesForThreads(
GmailApp.search('label:searchedLabel')).map(function(messagesForThread) {
return messagesForThread.filter(function(message) {
return message.getFrom() == "firstName lastName <email#domain.com>";
}).map(function(message) {
return { date: new Date(message.getDate()), body: message.getBody() };
});}));
}
This makes a grand total of 2 calls to Gmail, so it's going to be fast.
In fact, if you integrate the 'from' part into the search as was suggested above, all you need is:
function fetchEmails(){
var messages = Array.prototype.concat.apply([], GmailApp.getMessagesForThreads(
GmailApp.search('label:searchedLabel from:email#domain.com')).map(
function(messagesForThread) {
return messagesForThread.map(function(message) {
return { date: new Date(message.getDate()), body: message.getBody() };
});}));
}
Finally, since you don't actually care about the thread structure, you can just concat the arrays before the map, which leads to this:
function fetchEmails(){
var messages = GmailApp.getMessagesForThreads(
GmailApp.search('label:searchedLabel from:email#domain.com'))
.reduce(function(a, b){ return a.concat(b); })
.map(function(message) {
return { date: new Date(message.getDate()), body: message.getBody() };
});
}
(I left in the earlier samples in case you do care about the thread structure and you were just giving a minimal example).