How can I get a user's name from their email? - google-apps-script

I'm trying to get a user's name from their email and we are all on the same business domain. I saw a post here detailing how to do it if you want to pull from the contacts list. The problem is when I try it myself, it knows that there is contact for me, but it returns all of the values as null. If I use another contact email, then it pulls the info just fine.
The link says that there should be another way to do it but you need admin privileges. I can get that, but all of the links to the usermanager documentation are broken. Also, searching usermanager doesn't come up with anything on Google's developer site.
var email = Session.getActiveUser().getEmail();
Browser.msgBox(email)
var self = ContactsApp.getContact(email);
var name = self.getFullName();

With admin rights you can use the directory api. Do not forget to enhable the admin api in your appscript (Ressources > Advanced Google services) and also in the Developers Console.
function getUserName(email){
var result = AdminDirectory.Users.get(email, {fields:'name'});
var fullname = result.name.fullName;
Logger.log(fullname);
return fullname;
}

If parsing from the text, you can use like below,
var email = "john.doe#email.com";
var name = email.substring(0, email.lastIndexOf("#"));
console.log( name ); // john.doe
Hopefully help

Related

Google App Script - How to monitor for new users

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

EWS Create Contacts converts email to Exchange distinguished name instead of SMTP

I have a C# Console application that uses EWS (Exchange Web Services) to impersonate a user and I create or update his current Contacts list.
In order to determine if I have to create or update his list, I first need to search his existing Contacts for a particular domain name like so:
private static IEnumerable<Contact> GetExistingContacts(ExchangeService service)
{
var domainToFilterOn = "#contoso.com";
SearchFilter sfSearch = new SearchFilter.ContainsSubstring(ContactSchema.EmailAddress1, domainToFilterOn);
FindItemsResults<Item> contacts = service.FindItems(WellKnownFolderName.Contacts, sfSearch, new ItemView(int.MaxValue));
var results = contacts.Cast<Contact>().ToList();
return results;
}
The problem I’m facing is that the method GetExistingContacts() yields 0 results for the given domain name which is wrong since I know I have a bunch of Contacts holding the #contoso.com domain name inside the EmailAddre1 field.
After a little bit of digging and testing, I finally figure out why the method wasn’t returning any results and the reason was because the email addresses are stored in the Exchange distinguished name instead of the SMTP format.
To further my investigation, I created a few new Contacts with fake/non-existing #contoso.com domain name like: test#contoso.com, gazou#contoso.com, etc.
To my surprise, the GetExistingContacts() method started to return these fake Contacts.
The conclusion is that whenever I create new Contacts that have resolvable email addresses, then these Contacts are stored using the Exchange distinguished name but when I create new Contacts that have non-resolvable email addresses, then these Contacts are stored as SMTP (which are returned by my GetExistingContacts() method).
How do I start fixing this?
Is my search method wrong? Is there another way to search inside the EmailAddress1 field?
Meanwhile, I managed to find a workaround using the .Load() method of the Contact object but this workaround seems ugly and costly in terms of execution time.
I basically get all Contacts, loop and call the Load() method, then add them to a List() and make a Linq query to filter the results. If my user has 800 Contacts, that takes a long time to Load() everyone of them.
Here’s the example:
private static IEnumerable<Contact> GetCurrentContacts(ExchangeService service)
{
var contacts = new List<Contact>();
var data = service.FindItems(WellKnownFolderName.Contacts, new ItemView(int.MaxValue));
foreach (var item in data.Items)
{
if (item is Contact)
{
item.Load();
contacts.Add(item as Contact);
}
}
var result = contacts.Where(x => x.EmailAddresses[EmailAddressKey.EmailAddress1].Address.Contains("#contoso.com")).ToList();
return result;
}
Needless to say, I don't think that is the correct approach although it works.
Another alternative I’ve tried was to force the RoutingType to SMTP thinking it would create the new Contact in the SMTP format as opposed to the Exchange distinguished name but unfortunately, the email address still gets stored in the Exchange distinguished name disregarding the fact that I forced the RoutingType like so:
var email = new EmailAddress();
email.Address = "goodemail#contoso.com";
email.RoutingType = "SMTP";
Contact contact = new Contact(service);
contact.EmailAddresses[EmailAddressKey.EmailAddress1] = email;
...
contact.Save();
If anyone can help me shed some light on this, that would be great!
Thanks in advance
If the SMTP address your trying to use for a Contact is visible (or resolvable) in the Global address list then the X500 address of Directory entry will be used to track the Contact to the Directory entry (this is by design). If you want to return the SMTP address instead of the X500 address when you retrieve the contacts all you need to do is make use you do a GetItem or Load in the Managed API on the contact or contacts in question if you have multiple contact use LoadPropertiesFromItems https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/
You can override this behavior by setting the extended properties on the contacts directly see https://social.technet.microsoft.com/Forums/exchange/en-US/2b375c56-bee1-4d88-b638-f95649ef964a/use-ews-create-a-contact-which-has-a-same-email-address-in-gal-it-will-show-up-with-x500-formatting?forum=exchangesvrdevelopment but I would recommended you stay with the default behavior you can make you code more efficient using batch Loads.
Thank you #Glen Scales, the LoadPropertiesForItems() helped.
This is the final result if anyone cares:
private static IEnumerable<Contact> GetExistingContacts(ExchangeService service)
{
var contacts = new List<Contact>();
var filterContactsOnDomain = "#contoso.com";
var data = service.FindItems(WellKnownFolderName.Contacts, new ItemView(int.MaxValue));
if (data.TotalCount > 0)
{
service.LoadPropertiesForItems(data, new PropertySet(ContactSchema.EmailAddress1));
foreach (var item in data.Items)
{
if (item is Contact)
{
contacts.Add(item as Contact);
}
}
}
var result = contacts.Where(x => x.EmailAddresses[EmailAddressKey.EmailAddress1].Address.ToLower().Contains(filterContactsOnDomain.ToLower())).ToList();
return result;
}
The only thing I dislike is having to get all Contacts (800 of them), load the EmailAddress1 field for all 800 of them, loop and add those 800 Contacts to a list and then, filter on that list...
I guess it would've been nice to have the ability to search (or filter) directly on an X500 email address format thus not having to fetch all 800 Contacts.
Oh well...
Thanks again #Glen Scales

Google Contacts Address is not retrieved by my Script (although it is filled)

I try to fetch the content of certain google contacts via Google Apps Script. First I identified the ID of the contact via a getId Function. My Script is this:
var id = 'id';
var contact = ContactsApp.getContactById(id);
var address = contact.getAddresses();
GmailApp.sendEmail("email", address, "");
The return I get via mail is "AddressField", allthough the certain contact definitely has an address.
In Addition I also tried the following script from the official reference (which returns the same thing):
// Logs the address for the 'Home Address' field for contact 'John Doe'.
// Can be used similarly for other fields that contain addresses.
var contacts = ContactsApp.getContactsByName('John Doe');
var homeAddress = contacts[0].getAddresses(ContactsApp.Field.HOME_ADDRESS);
Logger.log(homeAddress[0].getAddress());
Can anyone help me?
Thanks a lot in advance.
Best, Phil
This works for me :
function testContact(){
var contacts = ContactsApp.getContactsByName('some name in my contacts');
Logger.log(contacts[0].getFamilyName());// just to check it's the right one
var address = contacts[0].getAddresses();
for (var n=0;n<address.length;n++){
Logger.log(address[n].getLabel()+' : '+address[n].getAddress());// get all address fields for this contact + corresponding label
}
}

How do I getGivenName() of getActiveUser()

Each user on the domain initiates a simple script we run for leave entitlements but we want the welcome message to be "Hi First Name," however the script doesn't seem to be able to fetch getGivenName() from getActiveUser() for a standard user.
Is there a way?
As noted in comments, and in Documentation, the UserManager Service is only accessible by Domain Administrators.
Here's an alternative. Domain Users may have themselves in their own contacts, so how about a best-effort attempt at finding themselves there?
/**
* Get current user's name, by accessing their contacts.
*
* #returns {String} First name (GivenName) if available,
* else FullName, or login ID (userName)
* if record not found in contacts.
*/
function getOwnName(){
var email = Session.getEffectiveUser().getEmail();
var self = ContactsApp.getContact(email);
// If user has themselves in their contacts, return their name
if (self) {
// Prefer given name, if that's available
var name = self.getGivenName();
// But we will settle for the full name
if (!name) name = self.getFullName();
return name;
}
// If they don't have themselves in Contacts, return the bald userName.
else {
var userName = Session.getEffectiveUser().getUsername();
return userName;
}
}
In Apps Script, I was able to get this information using the About REST API: https://developers.google.com/drive/v2/reference/about/get
var aboutData = DriveApp.About.get();
var userEmail = aboutData["user"]["emailAddress"];
var userDisplayName = aboutData["user"]["displayName"];
You can get a user name but first you have to create a domain user using the provisioning api. You can enable the API by logging in to your admin account, and select Domain settings and the User settings tab to select the checkbox enabling the Provisioning API. Read more about it here
You can then use
user = user.getgivenName()
Since the UserManager Service is only available to a Domain Administrator, you could publish a service as the administrator, that serves user's Given Names, and invoke that from the user-run script using the UrlFetchApp.
The UserName Service
Refer to the Content Service Documentation for the background information this is based upon.
The service accepts a parameter, userName, which it uses to perform a lookup as the administrator.
Paste the following code into a script, then deploy the script as a web service. This must be done by a Domain Administrator, as the service access the UserManager Service, but the script must be made accessible by all users in the domain. (Since I'm not an admin in my domain, I cannot access the UserManager, so I've included a domain-user-invokable line for testing, calling the getOwnName() function I described in my first answer.)
Remember to invoke doGet() from the debugger to go through the authorization before accessing the published service.
/**
* When invoked as a Web Service running as Domain Administrator,
* returns the GivenName of the requested user.
*
* #param {String} userName= Should be set to Session.getEffectiveUser().getUsername().
*/
function doGet(request) {
//return ContentService.createTextOutput(getOwnName()); // for testing by non-admin user
var userName = request.parameters.userName;
var givenName = UserManager.getUser(userName).getGivenName();
return ContentService.createTextOutput(givenName);
}
Invoke service using UrlFetch
Refer to Using External APIs for an explanation of how to make use of the service written in the previous section. I'll show how to access the service from another script, but remember that you can also do this from web pages within your domain.
We will use UrlFetchApp.fetch() to get our service to return the user's first name as a String.
The service was written to accept one parameter, userName, and we append this to the url, in the form userName=<string>.
With the URL built, we fetch(), then retrieve the name from the response. While this example returns just the name, you may choose to change the service to return the complete "Hello User" string.
function testService() {
var domain = "my-google-domain.com";
var scriptId = "Script ID of service";
var url = "https://script.google.com/a/macros/"+domain+"/s/"+scriptId+"/exec?"
+ "userName="+Session.getEffectiveUser().getUsername();
var response = UrlFetchApp.fetch(url);
var myName = response.getContentText();
debugger; // pause in debugger
}
Another potential way of getting the display name on a gmail account is to find a Draft email in the GmailApp, and get the From header, which may have the full name. Some drafts might be setup with no display name in gmail, in which case the From header will only be the email address, but typically the From header is in the format:
Firstname Lastname <email#domain.com>
This code should get you the string above from the first gmail Draft: (note this will probably throw an exception if there are no drafts, so check that first.)
GmailApp.getDrafts()[0].getMessage().getHeader("From")
Ref: https://developers.google.com/apps-script/reference/gmail/gmail-message#getHeader(String)
Ref: https://www.ietf.org/rfc/rfc2822.txt

How to show contact photo using google apps script?

Hi I'm trying to show contact photo using google apps script,
this is what i have so far. so i connect successfully to contacts,
take first one, it's id, then i authenticate using OAuth, load
full contact profile, i even have a link to the photo, but it
won't show. I read somewhere that adding access token to link
would help but where to get this token from?
function doGet() {
var app = UiApp.createApplication();
var c = ContactsApp.getContacts();
c1 = c[0];
var options = OAuthApp.getAuth('contacts','http://www.google.com/m8/feeds');
var response = UrlFetchApp.fetch(c1.getId().replace(/base/,'full')+"?alt=json&v=3.0", options);
var object = Utilities.jsonParse(response.getContentText());
app.add(app.createImage(object.entry.link[0].href));
return app;
}
When i'm using UrlFetchApp.fetch (that includes options paramer that include authorisation) it loads the image data, but i don't know how to authorise app.createImage instead.
UiApp can't display private images. Star issue 912 http://code.google.com/p/google-apps-script-issues/issues/detail?id=912