Hi im using Advanced Services in Google Apps Script.
Im trying to add a number to the users profile.
var userValue = 'test#company.com';
var phoneValue = 017236233;
var users = AdminDirectory.Users.get(userValue);
for (var i = 0; i < users.length; i++) {
AdminDirectory.Users.update(users[i].phones[].primary, phoneValue);
}
The last part is the one i am not certain on. It fails with "Syntax error. (line 22, file "Code")"
Looking at the autocomplete on the update method you have to give it a User resource and a userKey (the user primary e-mail).
So this line of code should be:
AdminDirectory.Users.update(userResource, userPrimaryEmail);
Since you just want to add a phone your User resource can only contain this:
var userResource = {
phones:[{
value: phoneValue
}]
}
However be aware that this would update the whole list of phones and overwrite older values.
Also, note that the get method your are using doesn't return a list of User resources but a single User resource. You could use that same resource, update it and send it back.
So what you're looking would be:
var userPrimaryEmail = 'test#company.com';
var phoneValue = 017236233;
var user = AdminDirectory.Users.get(userPrimaryEmail);
// If user has no phones add a 'phones' empty list to the user resource
if (! user.phones){
user.phones = [];
}
user.phones.push(
{
value: phoneValue,
type: "mobile" // Could be 'home' or 'work' of whatever is allowed
}
)
AdminDirectory.Users.update(user, userPrimaryEmail);
A syntax error means that the code you wrote isn't valid. More information on syntax errors is available on the Apps Script troubleshooting guide.
Related
I wondered if anyone could point me in the right direction here?
I want to monitor the Google Workspace estate, and when a new user has been created send them an email. I’ve looked through the APIs but nothing is jumping out at me. But I know there are 3rd party tools out there that do this, so there’s got to be something I have missed?
I just created this script in Google Apps Script which gets and prints the list of all the users that were created today.
You can use this as a guide and keep testing with it. To accomplish this I used the Reports API to get the admin logs and get the list of all the users that were created today.
function myFunction() {
var userKey = 'all';
var applicationName = 'admin';
var optionalArgs = {
eventName:'CREATE_USER',
startTime: "2022-03-23T12:00:00.000Z",
fields : "items.events.parameters.value"
};
var rep = AdminReports.Activities.list(userKey,applicationName,optionalArgs);
const A = (JSON.parse(rep));
var totalUsers = Object.keys(A.items).length;
for(var i=0; i<totalUsers; i++)
{
var userEmail = A.items[i].events[0].parameters[0].value;
Logger.log(userEmail);
}
}
You would just need to change the startTime value according to the date you need to use and implement the part of sending the email now that you have all the email addresses.
References
API method: activities.list
Apps Script reference: Reports API
I'd like to completely undo any of Gmails built in category labels. This was my attempt.
function removeBuiltInLabels() {
var updatesLabel = GmailApp.getUserLabelByName("updates");
var socialLabel = GmailApp.getUserLabelByName("social");
var forumsLabel = GmailApp.getUserLabelByName("forums");
var promotionsLabel = GmailApp.getUserLabelByName("promotions");
var inboxThreads = GmailApp.search('in:inbox');
for (var i = 0; i < inboxThreads.length; i++) {
updatesLabel.removeFromThreads(inboxThreads[i]);
socialLabel.removeFromThreads(inboxThreads[i]);
forumsLabel.removeFromThreads(inboxThreads[i]);
promotionsLabel.removeFromThreads(inboxThreads[i]);
}
}
However, this throws....
TypeError: Cannot call method "removeFromThreads" of null.
It seems you can't access the built in labels in this way even though you can successfully search for label:updates in the Gmail search box and get the correct results.
The question...
How do you access the built in Gmail Category labels in Google Apps Script and remove them from an email/thread/threads?
Thanks.
'INBOX' and other system labels like 'CATEGORY_SOCIAL' can be removed using Advanced Gmail Service. In the Script Editor, go to Resources -> Advanced Google services and enable the Gmail service.
More details about naming conventions for system labels in Gmail can be found here Gmail API - Managing Labels
Retrieve the threads labeled with 'CATEGORY_SOCIAL' by calling the list() method of the threads collection:
var threads = Gmail.Users.Threads.list("me", {labels: ["CATEGORY_SOCIAL"]});
var threads = threads.threads;
var nextPageToken = threads.nextPageToken;
Note that you are going to need to store the 'nextPageToken' to iterate over the entire collection of threads. See this answer.
When you get all thread ids, you can call the 'modify()' method of the Threads collection on them:
threads.forEach(function(thread){
var resource = {
"addLabelIds": [],
"removeLabelIds":["CATEGORY_SOCIAL"]
};
Gmail.Users.Threads.modify(resource, "me", threadId);
});
If you have lots of threads in your inbox, you may still need to call the 'modify()' method several times and save state between calls.
Anton's answer is great. I marked it as accepted because it lead directly to the version I'm using.
This function lets you define any valid gmail search to isolate messages and enables batch removal labels.
function removeLabelsFromMessages(query, labelsToRemove) {
var foundThreads = Gmail.Users.Threads.list('me', {'q': query}).threads
if (foundThreads) {
foundThreads.forEach(function (thread) {
Gmail.Users.Threads.modify({removeLabelIds: labelsToRemove}, 'me', thread.id);
});
}
}
I call it via the one minute script trigger like this.
function ProcessInbox() {
removeLabelsFromMessages(
'label:updates OR label:social OR label:forums OR label:promotions',
['CATEGORY_UPDATES', 'CATEGORY_SOCIAL', 'CATEGORY_FORUMS', 'CATEGORY_PROMOTIONS']
)
<...other_stuff_to_process...>
}
Hy,
I have a server sending me several log mails by day and I want to automaticly label this mails.
I can't touch the server configuration to adapt the mail subject, so the work must be done by "receiver".
The Subject is still same so gmail merge them in a thread of 100, but I want to split them by date. So One Date, one thread. In addition, I want label them whith a nested label: "Server1" -> "Date"
I've only found a way to add label to the thread in globality and no way to split them.
Is it even possible?
After a new look on my issue, perhaps add the date at the message subject can split threads.
Like:
function AddLogSubjectADate() {
var threads = GmailApp.search('from:sender#server.com has:nouserlabels');
threads.forEach(function(messages){
messages.getMessages().forEach(function(msg){
var date = msg.getDate();
var date_of_mail = Utilities.formatDate(date, "GMT+1", "yyyy/MM/dd")
var subj = msg.getSubject()
var newsubj = subj + date_of_mail
//A way to modify subject
});
});
}
But I didn't find a way to change the subject.
Post Scriptum
I don't think it's relevant, but here is my previous work. but it add label to the thread. Like I said I haven't find a way to split threads.
function AddLogLabelbyDate() {
var today = new Date();
var tomorrow = new Date();
var yesterday = new Date();
tomorrow.setDate(today.getDate()+1);
yesterday.setDate(today.getDate()-1);
var date_today = Utilities.formatDate(today, "GMT+1", "yyyy/MM/dd")
var date_tomorrow = Utilities.formatDate(tomorrow, "GMT+1", "yyyy/MM/dd")
var date_yesterday = Utilities.formatDate(yesterday, "GMT+1", "yyyy/MM/dd")
var threads = GmailApp.search('from:sender#server.com has:nouserlabels before:'+ date_tomorrow +' after:'+ date_yesterday +'');
label.addToThreads(threads);
}
Per the API documentation, Gmail follows some rules about thread grouping:
In order to be part of a thread, a message or draft must meet the following criteria:1. The requested threadId must be specified on the Message or Draft.Message you supply with your request.2. The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.3. The Subject headers must match.
So, you can prevent the automatic grouping into a given conversation thread by modifying any of those 3 parameters.
Alternately, you can apply per-message conversation labels, though this will not really help you if you use "Conversation View" UI.
Both of these methods require the use of the Gmail REST API, for which Apps Script provides an "advanced service" client library. The native GmailApp does not provide a method for per-message thread alteration, or for manipulating messages in the manner needed.
Thread Separation
If you wanted to disable the conversation grouping, in theory you could do this:
Message#get to obtain a full message representation
Modify one of the properties Gmail uses to perform thread grouping
Message#insert or import to create the new message on the server
Message#delete to remove the original
Message#get to get the inserted message metadata, after Gmail has given it a threadId.
Get the remaining messages that should share that new threadId, modify them appropriately, and insert.
Repeat.
I haven't tested that approach, hence my "in theory" comment.
Per-message labeling
The relevant API methods include Gmail.User.Labels.list, Gmail.User.Messages.list, Gmail.User.Messages.modify, and Gmail.User.Messages.batchModify. You'll probably want to use the list and messages.batchModify methods, since you seem to have a large number of messages for which you'd like to make alterations. Note, there are non-trivial rate limits in place, so working in small batches is liable to be most resource-efficient.
This is likely to be the simplest method to implement, since you don't have to actually create or delete messages - just search for messages that should have a given label, add (or create and add) it to them, and remove any non-desired labels. To start you off, here are some minimal examples that show how to work with the Gmail REST API. I expect you will need to refer to the API documentation when you use this information to construct your actual script.
An example Labels#list:
function getLabelsWithName(labelName) {
const search = Gmail.Users.Labels.list("me");
if (!search.labels || !search.labels.length)
return [];
const matches = search.labels.filter(function (label) {
// Return true to include the label, false to omit it.
return label.name === labelName;
});
return matches;
}
An example Messages#list:
function getPartialMessagesWithLabel(labelResource) {
const options = {
labelIds: [ labelResource.id ],
fields: "nextPageToken,messages(id,threadId,labelIds,internalDate)"
};
const results = [];
// Messages#list is paginated, so we must page through them to obtain all results.
do {
var search = Gmail.Users.Messages.list("me", options);
options.pageToken = search.nextPageToken;
if (search.messages && search.messages.length)
Array.prototype.push.apply(results, search.messages);
} while (options.pageToken);
return results;
}
An example Messages#batchModify:
function batchAddLabels(messageArray, labels) {
if (!messageArray || !messageArray.length || !messageArray[0].id)
throw new Error("Missing array of messages to update");
if (!labels || !labels.length || !labels[0].id)
throw new Error("Missing array of label resources to add to the given messages");
const requestMetaData = {
"addLabelIds": labels.map(function (label) { return label.id; }),
"ids": messageArray.map(function (msg) { return msg.id; }) // max 1000 per request!
};
Gmail.Users.Messages.batchModify(requestMetaData, "me");
}
Additional Resources:
Message Searches
"fields" parameter
I am trying to make a small Google Script that would automatically add Google Tasks to the "My List" TaskList after searching my GMail emails.
Everything goes fine except for adding a link to the email from which the Task is generated from. Trying to follow the API documentation doesn't really help.
This is the code for the actual task generator function:
function addTask(taskListId, myTitle, myEmailLink) {
var task = Tasks.newTask(); // effectively same as "= {}".
task.title = myTitle
task.notes = 'blank';
task.links = [{}]
task.links[0].description = 'Link to corresponding email';
task.links[0].type = 'email';
task.links[0].link = 'myEmailLink';
task = Tasks.Tasks.insert(task, taskListId);
}
Any ideas why the task I receive back has no links?
As others have noted, according to the Google Tasks API Documentation the links collection is unfortunately read-only.
As a potential work around, it appears you can add links to the notes section of a task, and the links are then directly clickable from the tasks pane in GMail.
Picture: Task with clickable link
Your function can be modified to put the link in the notes section as follows:
function addTask(taskListId, myTitle, myEmailLink) {
var task = Tasks.newTask(); // effectively same as "= {}".
task.title = myTitle
task.notes = 'link: ' + myEmailLink;
task = Tasks.Tasks.insert(task, taskListId);
}
Combining this with the getPermalink() function on the GmailApp threads object allows for grabbing a deep link to the email you are looking for.
Picture: Task with permalink to email
I'm working on a set of scripts that do some of the things you're talking about in addition to a few other things: https://github.com/tedsteinmann/gmailAutoUpdate
In my solution I have a function that grabs the GMail threads with a particular label (in my case #Task) and then creates a task setting the subject to thread.getFirstMessageSubject() and the notes to thread.getPermalink()
The entire function looks like this:
function processPending_() {
var label_pending = GmailApp.getUserLabelByName(LABEL_PENDING);
var label_done = GmailApp.getUserLabelByName(LABEL_DONE);
// The threads currently assigned to the 'pending' label
var threads = label_pending.getThreads();
// Process each one in turn, assuming there's only a single
// message in each thread
for (var t in threads) {
var thread = threads[t];
// Grab the task data
var taskTitle = thread.getFirstMessageSubject();
var taskNote = 'Email: ' + thread.getPermalink();
// Insert the task
addTask_(taskTitle, taskNote, getTasklistId_(TASKLIST));
// Set to 'done' by exchanging labels
thread.removeLabel(label_pending);
thread.addLabel(label_done);
}
// Increment the processed tasks count
Logger.log('Processed %s tasks', threads.length);
}
Per the Google Tasks API Documentation:
links[] list
Collection of links. This collection is read-only.
You cannot set these links by modifying a Task resource, i.e your code
task.links = [{}]
task.links[0].description = 'Link to corresponding email';
task.links[0].type = 'email';
task.links[0].link = 'myEmailLink';
is simply ignored by the server.
TaskLinks are, to my knowledge, unusable and non-configurable outside of the Googleplex. They may as well not exist to API users.
The only way I've been able to generate a Task that has one is by using the Gmail UI and selecting "Add to Tasks". The resulting task then includes this snippet in the last line of the Task item:
I'm trying to pull the student information from a google classroom roster. Here is what I have so far:
function studentRoster() {
var optionalArgs = {
pageSize: 2
};
var getStudents = Classroom.Courses.Students.list("757828465",optionalArgs).students;
Logger.log(getStudents);
}
Sandy's answer below helped solve part of my problem and I get this as a log (names, id's, emails and such changed):
[16-01-05 17:44:04:734 PST] [{profile={photoUrl=https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg, emailAddress=jsdoe#fjuhsd.org, name={givenName=John, familyName=Doe, fullName=John Doe}, id=108117124004883828162}, courseId=757828465, userId=108117124004883828162}, {profile={photoUrl=https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg, emailAddress=jhdoe#fjuhsd.org, name={givenName=Jane, familyName=Doe, fullName=Jane Doe}, id=115613162385930536688}, courseId=757828465, userId=115613162385930536688}]
So my question now is: How do I extract only certain pieces of this information (like full name and email)?
The end result will be pushing it to a google sheet.
The Collection sections under the Classroom API Reference can help clarify some of the object chaining required to access the fields you want.
The following code will generate a list of student names, their email, and associated course ID.
function listStudents() {
var optionalArgs = {
pageSize: 0 // Max output
};
var response = Classroom.Courses.Students.list(xxxxxxxxx; // Put CourseID here
var students = response.students;
if(students && students.length > 0){
for( i = 0; i < students.length; i++){
var student = students[i];
// fullName is normally prefixed with s/ use of .substring removes first two characters
Logger.log('%s %s %s', student.profile.name.fullName.substring(2), student.profile.emailAddress, student.courseId );
}
} else {
Logger.log('No students in this course');
}
}
Collection courses.students
The Quickstart example is using the list method, and the list method takes optional query parameters. One of them being pageSize.
Query parameters for List
You are using the get method.
Documentation - Get method
The only options that the get method has is for the Path. And there is only one setting, the id.
So, that object literal named optionalArgs, you don't need for what you are doing. optionalArgs is an object because it has curly braces, and has elements that are "key/value" pairs. It's "literal", because it's typed into the code, as opposed to building the object with code.
If you did a search, (open the search dialog with Ctrl + F), and searched for optionalArgs, you'll see that it's not being used anywhere. So, the optionalArgs object with the pageSize property is not needed when using the get method.