Scripts Newbie Questions Regarding Contacts App and "People"

931 views
Skip to first unread message

David White

unread,
Sep 28, 2023, 1:10:40 PM9/28/23
to Google Apps Script Community
I appreciate any pointers you might have...

I am trying to get the Google Contacts email address for a given name using script. In a really dumb way, I have been successful using the following:

var address = ContactsApp.getContactsByName("name goes here")[0].getEmailAddresses()[0];

But I have read that ContactsApp is deprecated and that I should use the "People" api instead. So I have read a bit about that and even tried a short script that is exactly what I saw in the migrating to People document but when I run it (as exactly cut/pasted from the example) it fails!

const people = People.People.Connections.list('people/me', {
      personFields
: 'names,emailAddresses'
   
});
   
const contact = people['connections'].find((connection) => {
     
return connection['emailAddresses'].some((emailAddress) => emailAddress['value'] === email);
   
});
   
// Prints the contact.
    console
.log('Contact: %s', JSON.stringify(contact, null, 2));

I get the error:

TypeError: Cannot read properties of undefined (reading 'some')

So I know I am doing something quite wrong in the 1st place.

Also, this seems to get a contact by email address and I want to get it by name. So I know I need to change it somehow. But this seemed a good starting point.

Right now I am stumped but able to use the deprecated ContactsApp with success. However, for how long...?

Thanks

David White

unread,
Sep 28, 2023, 1:20:30 PM9/28/23
to Google Apps Script Community
BTW - the People code I used that failed was, more correctly, this:

return connection['emailAddresses'].some((emailAddress) => emailAddress['value'] === "known good email address goes here" );

Thanks

Tanaike

unread,
Sep 28, 2023, 10:52:24 PM9/28/23
to Google Apps Script Community
I believe your goal is as follows.

- You want to retrieve the email address from a user name using People API with Google Apps Script.

In this case, how about the following sample script?

---
const searchName = "###"; // Please set the search name you want.

const people = People.People.Connections.list('people/me', { personFields: 'names,emailAddresses' });
const email = people.connections.find(({ names }) => names ? names.some(o => ["familyName", "givenName", "displayName"].some(e => o[e].includes(searchName))) : false);
if (!email) return [];
const emailAddresses = email.emailAddresses.map(({ value }) => value);

console.log(emailAddresses);
---

In this case, the search name is searched from "familyName", "givenName", "displayName". If you want to change this, please modify the script.

If I misunderstood your question, I apologize.

David White

unread,
Sep 29, 2023, 1:11:07 PM9/29/23
to Google Apps Script Community
Thanks Tanaike! You understood my request perfectly. I should point out, in case it makes any difference, that I do NOT have Workspace, only the free Google Contacts (if that makes any difference).

I cannot yet say I fully understand the find command being issued but I will work on it. Meanwhile, I have tried to execute the suggested code with searchName set (individually) to each of the values shown for a valid contact in my Google Contacts:

familyName
givenName
displayName

Sadly, every time I try to run it, I get:

TypeError: Cannot read properties of undefined (reading 'includes')

Since I am not yet fully up-to-speed on the query, I am not quite sure how to interpret this error - except that some intermediate result appears to be null/undefined.

While I understand that the People mechanism is potentially more powerful than the Contacts API, I sure don't understand why the API doesn't include some "helper" functions for commonly needed queries. Perhaps I feel this way only because I am such a newbie here.

Cheers

Tanaike

unread,
Sep 29, 2023, 8:36:26 PM9/29/23
to Google Apps Script Community
Thank you for replying. I apologize for the inconvenience. From your error message of "TypeError: Cannot read properties of undefined (reading 'includes')", I guess that in your situation, there might be contact without using one of "familyName", "givenName", "displayName" or all of them. So, please modify it as follows. And, test it again.

From


const email = people.connections.find(({ names }) => names ? names.some(o => ["familyName", "givenName", "displayName"].some(e => o[e].includes(searchName))) : false);

To

const email = people.connections.find(({ names }) => names ? names.some(o => ["familyName", "givenName", "displayName"].some(e => o[e] && o[e].includes(searchName))) : false);


David White

unread,
Sep 29, 2023, 9:36:23 PM9/29/23
to Google Apps Script Community
OMG! Thanks again Tanaike!

You are most certainly correct that some of my Google Contacts lack any email address whatsoever. And, at least in my initial tests, this new code appears to work perfectly. Since I remain in the dark (and now even a bit more in the dark due to the somewhat more complex query), this is a real miracle for me!

Thanks so very much and I will try to decipher this tomorrow. Best!

Tanaike

unread,
Sep 30, 2023, 3:44:18 AM9/30/23
to Google Apps Script Community
Thank you for replying. If this modification didn't resolve your current issue, I'm worried that it might be required to confirm your values for correctly replicating it.

David White

unread,
Sep 30, 2023, 11:53:47 AM9/30/23
to Google Apps Script Community
Well, the function is now working. Thanks again for all of your time and help. I will start working on understanding the code soon.

David White

unread,
Sep 30, 2023, 1:30:40 PM9/30/23
to Google Apps Script Community
Tanaike,

I am sorry to keep bothering you. But perhaps you know/understand this odd behavior I am seeing as I try to test my code executing in the script editor...

I have discovered that Google Contacts does not appear to validate that something entered into an email address field for a contact is at least in a valid email address format. I discovered this by accidentally mis-editing one of my test contacts and entered a "." instead of an "@". So I have modified my script to validate the returned value from your query to at least ensure it is in a valid format.

And then to test my validation routine, I went into Google Contacts and purposefully changed a test contact's valid email to become invalid by swapping a "." for the existing "@". When I ran my test this worked as expected and my validation routine caught the issue.

However, after I went back into Google Contacts to put the email address back into proper format and saved the contact, I re-ran my test and now your query no longer returns anything for that contact's email address. Indeed, the code:

const email = people.connections.find(({ names }) => names ? names.some(o => ["familyName", "givenName", "displayName"].some(e => o[e] && o[e].includes(searchName))) : false);

leaves email as undefined. And as I modify other test contacts, the same ensues.

Have you any idea what could be causing this odd behavior? All looks fine in Google Contacts and my original code using ContactsApp continues to work as expected.

Thanks again for all of your help.

Tanaike

unread,
Sep 30, 2023, 9:32:19 PM9/30/23
to Google Apps Script Community
Thank you for replying. I'm glad your issue was resolved.

About your new question, I have to apologize for my poor English skill. Unfortunately, I cannot understand your new question. But, I would like to support you. When I could correctly understand your new question, I would like to think of a solution. I apologize that I cannot resolve your various questions soon. I would like to study English more.

David White

unread,
Oct 1, 2023, 1:28:31 PM10/1/23
to Google Apps Script Community
Thanks Tanaike. Your English seems quite good and perhaps I just lack the skill to be clear here. Let me try again:

1. Your code worked during my first few tests.
2. I then made changes to some contacts in Google Contacts online at contacts.google.com.
3. Ever since I made those changes, your code is returning nothing for the contacts changed in #2.

But:

4. Contacts that I have not changed still work in your code.
5. My old code using the ContactsApp API still works on all of the contacts.

I do not know but it seems as if the list of contact information being returned from this code is NOT being updated for some reason:

People.People.Connections.list('people/me', { personFields: 'names,emailAddresses' });

and I wonder if there is some issue in sync of this data between Google Contacts online and my script.

I hope this is more clear. Thanks so much!

David White

unread,
Oct 1, 2023, 2:04:36 PM10/1/23
to Google Apps Script Community
I can confirm that after having made the changes to some contacts in Google Contacts online, if I run the following code, the changed contacts are NOT shown in the output at all:

var people = People.People.Connections.list('people/me', { personFields: 'names,emailAddresses' });
  people.connections.forEach((person) => console.log(person));

They clearly were there before as your code worked fine on them. But now they are not in the list at all! But I can clearly see them at contacts.google.com and via the ContactsApp api.

The only changes I made to those contacts were their email addresses.

This is very strange indeed.

David White

unread,
Oct 1, 2023, 8:12:14 PM10/1/23
to Google Apps Script Community
Tanaike,

I was doing some reading and noticed that there is this interface:

People.People.searchContacts()

And I noticed here that there is a need to "warm up" the cache that is being searched by issuing an empty query and giving some time for the "warm-up" to complete. So this gets me thinking that there could be a problem with an out-of-date cache that I earlier postulated might be a "sync problem".

I then saw here that the searchContacts() method:
  • Provides a list of contacts in the authenticated user's grouped contacts that matches the search query. The query matches on a contact's names, nickNames, emailAddresses, phoneNumbers, and organizations fields that are from the CONTACT source.
and this seems to be possibly a more direct path to getting what I want (the email address for a given contact name).

So with the following, it seems I can get what I want:

let found = People.People.searchContacts({ "query": "", "readMask": "names,emailAddresses" });
Utilities.sleep(5000);
var emailAddress = undefined;
let query = name;
let found = People.People.searchContacts({"query": name, "readMask": "names,emailAddresses"});

if(found.results)
{
  emailAddress = found.results[0].person.emailAddresses[0].value;
}

console.log(name + ":" + emailAddress);

Do you see any downside to doing things this way? This code will be triggered on a 5 minute timer so warming-up the cache for 5+ seconds does not seem a real problem.

Perhaps there is a way to "warm-up" the cache and still use your method?

Thanks
On Saturday, September 30, 2023 at 6:32:19 PM UTC-7 Tanaike wrote:

Tanaike

unread,
Oct 1, 2023, 9:26:46 PM10/1/23
to Google Apps Script Community
Thank you for replying. Although I'm not sure whether I could correctly understand your reply, when I modify the contact (change the name), when I retrieve the values with `People.People.Connections.list`, soon, I can confirm that the name is changed. But, I'm glad your issue was resolved.

Tanaike

unread,
Oct 1, 2023, 9:35:03 PM10/1/23
to Google Apps Script Community
As an additional test, even when a new email address is added or an email address is removed, when I retrieve the values with `People.People.Connections.list`, soon, I can soon confirm that the contact data is changed.

For example, I'm worried that your issue might depend on the number of contacts. In your situation, how many contacts do you have? In my test environment, 3 contacts were used.

David White

unread,
Oct 2, 2023, 1:38:45 PM10/2/23
to Google Apps Script Community
In my case, I have 139 contacts.

I changed email address, not name.

The 1st time I changed email address, I was able to see the change.

But when I changed it a second time, I was not able to see the change (even though it showed in Contacts).

So I am quite sure that there is some sort of caching issue affecting my testing. I have tested the exact, same, scenario using the searchContacts() and issuing an empty query before any "real" queries seems to avoid this problem.

I have no real idea what is happening inside the convenience function searchContacts(). Earlier I wondered why there was not something like this method. I now understand that I simply had not found it yet.

I think I am on solid ground now and appreciate all of your time and help. It has been an education for me.

Tanaike

unread,
Oct 2, 2023, 8:55:35 PM10/2/23
to Google Apps Script Community
Thank you for replying. Unfortunately, I cannot replicate your situation. I apologize for this. But, I would like to support you. When I could correctly replicate your situation, I would like to think of a solution. I apologize that I cannot resolve your various questions soon. I would like to study more.

David White

unread,
Oct 3, 2023, 3:11:07 PM10/3/23
to Google Apps Script Community
You have been a great help to me. Thanks!

I too am confused about what I am seeing and why. But it seems that using searchContacts() as documented is doing the job I require. Best!
Reply all
Reply to author
Forward
0 new messages