Check if contact exists under google contacts with "ContactsApp.getContact" - google-apps-script

I'm noob regarding scripting so keep that in mind. :-)
I want my script to read from google sheet and and check if that contact exist under google contacts and if not to create one.
Contacts are checked by email and have label "Client". I can't get if statement to confirm if contact exist or not. If i remove If for checking contacts it will create contact for every single entry so i think that that part is fine, but i need to fix part how to check if contact already exists so it wouldn't create duplicated entry.
function addClinet() {
var ss = SpreadsheetApp.openById('XXXX');
var sheetNew = ss.getSheetByName('NewClient');
var Avals = sheetNew.getRange('B1:B').getValues();
var lastRow = Avals.filter(String).length;
for (var i = 2 ; i <= lastRow; i++){
var nameID = sheetNew.getRange(i, 2).getValue();
var emailID = sheetNew.getRange(i, 8).getValue();
var mobID = sheetNew.getRange(i, 9).getValue();
var firstName = nameID.split(' ').slice(0, -1).join(' ');
var lastName = nameID.split(' ').slice(-1).join(' ');
var regex = new RegExp (/^\w/);
var firstChar = regex.exec(mobID);
var contacts = ContactsApp.getContact(emailID);
if (contacts == null){
if (firstChar == 8){
var mobID = 'xxx' + mobID;
}
var contact = ContactsApp.createContact(firstName,lastName, emailID);
var contacts = ContactsApp.getContact(emailID);
contact.addPhone(ContactsApp.Field.WORK_PHONE, mobID);
var group = ContactsApp.getContactGroup("Clients");
group.addContact(contact);
}
}
}
Thx

I wouldn't use the ContactsApp.getContact([email]) function -- For whatever reason Google Apps Script's contacts search by email is excruciatingly slow. Since it sounds like you have a number of contacts that you are sorting through at any given time, I would recommend you use the same -- instead of searching for the email address through Google Apps Script (this takes about 16-20 seconds PER CONTACT)
Using the following function you will be able to create one large JSON object of all of your contacts, with their email addresses as the key so you can quickly test whether an email address is present in your contacts (this takes about 11 seconds for around 5000 contacts:
function emailsasJSON() {
var emailjson = {}
var myContacts = ContactsApp.getContactGroup('Clients').getContacts();
for (var i = 0; i < myContacts.length; i++) {
var emails = myContacts[i].getEmails();
var phonesobj = myContacts[i].getPhones();
var phones = {}
for (var j = 0; j < phonesobj.length; j++) {
phones[phonesobj[j].getPhoneNumber().replace(/[_)(\s.-]/g,'')] = 1;
}
for (var j = 0; j < emails.length; j++) {
emailjson[emails[j].getAddress().toLowerCase()] = {id: myContacts[i].getId(), phones: phones};
}
}
Logger.log(JSON.stringify(emailjson))
return emailjson;
}
Using the emailjson object you can compare each of your contacts MUCH faster -- It will create this in about 10 seconds -- we will use this later.
Secondly, there are some things in your code that I would clean up -- it looks to me like you have a sheet with the name in column B, email in column H, and mobile number in column I.
Instead of collecting all of those values individually per cell (takes a long time), you should collect the entire data set as an array and then work with it that way:
function addClinet() {
var ss = SpreadsheetApp.openById('XXXX');
var sheetNew = ss.getSheetByName('NewClient');
var clientsgroup = ContactsApp.getContactGroup('Clients')
//this is where we will insert the function from above to get the emailjson obj
var emailjson = emailsasJSON()
var contactarray = sheetNew.getDataRange().getValues();
for (var i = 1 ; i < contactarray.length; i++){
var name = contactarray[i][1]
var email = contactarray[i][7]
var phone = contactarray[i][8]
if(emailjson[email.toLowerCase()].id) { //check if email exists
if(!emailjson[email.toLowerCase()]['phones'][phone.replace(/[_)(\s.-]/g,'')]) { //if email exists but phone doesn't, add phone
ContactsApp.getContactById(emailjson[email.toLowerCase()].id).addPhone(ContactsApp.Field.MOBILE_PHONE, phone)
emailjson[email.toLowerCase()]['phones'][phone.replace(/[_)(\s.-]/g,'')] = 1; //add it to the emailjson object in case there are more iterations of this contact in the sheet
}
} else { //add new contact if it doesn't exist
var newcontact = ContactsApp.createContact(name.split(' ')[0],name.split(' ')[1], email)
newcontact.addPhone(ContactsApp.Field.MOBILE_PHONE, phone)
emailjson[email.toLowerCase()]['id'] = newcontact.getId();
emailjson[email.toLowerCase()]['phones'][phone.toString().replace(/[_)(\s.-]/g,'')] = 1;
clientsgroup.addContact(newcontact)
}
}
}
I don't have your datasheet to error check this but this should speed up your function by a considerable amount. Let me know if it throws any errors or, if you could give me an example sheet, I could test it.
Lastly, I imagine that this isn't a client list that you consistently update, so I would take them off of this sheet and move them elsewhere, although it would take a considerable number of contacts on the list to bog this function down.

Related

How to filter out all emails that came from a mailing list in Gmail

Is there a way to filter out all emails that came from a mailing list within Gmail or Google Apps Script using a search query. I know you can filter out a specific email address using list:info#example.com. But I want a catch-all type of query or even a query to catch-all from a specific domain such as list:#example.com. However, this does not work. Any ideas? Any help is greatly appreciated, thank you!
This function will trash all messages from all inbox thread that are not in the list.
function emailFilter() {
var list=['a#company.com','b#company.com','c#company.com','d#company.com','e#company.com'];
var threads=GmailApp.getInboxThreads();
var token=null;
for(var i=0;i<threads.length;i++) {
if(threads[i].getMessageCount()) {
var messages=threads[i].getMessages();
for(var j=0;j<messages.length;j++) {
if(list.indexOf(messages[j].getFrom()==-1)) {
messages[j].moveToTrash();
}
}
}
}
}
I haven't tested it because I keep my inbox empty all of the time. You might want to replace 'moveToTrash()' to 'star()' for testing
What I could understand from your question and your comments, you need to filter the emails in a user's inbox that he has received, which don't only contain a certain label, but also a certain domain. If I understood well this code can help you:
function checkLabels() {
// Get the threads from the label you want
var label = GmailApp.getUserLabelByName("Label Test List");
var threadArr = label.getThreads();
// Init variable for later use
var emailDomain = '';
// Iterate over all the threads
for (var i = 0; i < threadArr.length; i++) {
// for each message in a thread, do something
threadArr[i].getMessages().forEach(function(message){
// Let's get the domains from the the users the messages were from
// example: list:#example.com -> Result: example.com
emailDomain = message.getFrom().split('<').pop().split('>')[0].split('#')[1];
// if emailDomain is equal to example.com, then do something
if(emailDomain === 'example.com'){
Logger.log(message.getFrom());
}
});
}
}
Using the Class GmailApp I got a certain label with the .getUserLabels() method and iterate through the threads thanks to the .getInboxThreads method. With a second loop and the .getMessages() you can get all the messages in a thread and for knowing the one who sent them, just use the .getFrom() method.
Docs
For more info check:
Gmail Service.
Class GmailMessage.
Class GmailThread.
So I was able to avoid replying to emails that come from a mailing list address by using the getRawContent() method and then searching that string for "Mailing-list:". So far the script is working like a charm.
function autoReply() {
var interval = 5; // if the script runs every 5 minutes; change otherwise
var date = new Date();
var day = date.getDay();
var hour = date.getHours();
var noReply = ["email1#example.com",
"email2#example.com"];
var replyMessage = "Hello!\n\nYou have reached me during non-business hours. I will respond by 9 AM next business day.\n\nIf you have any Compass.com related questions, check out Compass Academy! Learn about Compass' tools and get your questions answered at academy.compass.com.\n\nBest,\n\nShamir Wehbe";
var noReplyId = [];
if ([6,0].indexOf(day) > -1 || (hour < 9) || (hour >= 17)) {
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('from:#example.com is:inbox after:' + timeFrom);
var label = GmailApp.getUserLabelByName("autoReplied");
var repliedThreads = GmailApp.search('label:autoReplied newer_than:4d');
// loop through emails from the last 4 days that have already been replied to
for (var i = 0; i < repliedThreads.length; i++) {
var repliedThreadsId = repliedThreads[i].getMessages()[0].getId();
noReplyId.push(repliedThreadsId);
}
for (var i = 0; i < threads.length; i++) {
var message = threads[i].getMessages()[0];
var messagesFrom = message.getFrom();
var email = messagesFrom.substring(messagesFrom.lastIndexOf("<") + 1, messagesFrom.lastIndexOf(">"));
var threadsId = message.getId();
var rawMessage = message.getRawContent();
var searchForList = rawMessage.search("Mailing-list:");
var searchForUnsubscribe = rawMessage.search("Unsubscribe now");
// if the message is unread, not on the no reply list, hasn't already been replied to, doesn't come from a mailing list, and not a marketing email then auto reply
if (message.isUnread() && noReply.indexOf(email) == -1 && noReplyId.indexOf(threadsId) == -1 && searchForList === -1 && searchForUnsubscribe === -1){
message.reply(replyMessage);
threads[i].addLabel(label);
}
}
}
}

How to check Gmail Thread for Replies from Email

I am creating a basic CRM that needs to mark when a thread has been replied to.
I have created a script that can scan my inbox for threads from a list of emails in a sheet, check the last message in each thread and collect the .getFrom in order to see if I was the last to reply.
However, I can't figure out how to check if there has been a response from the person who's been contacted throughout the whole thread.
Here's the script that checks for the last message. (It's an extract of a larger script in case any references are missing):
Example Sheet
function UpdateStatus() {
// Connect to our active sheet and collect all of our email addresses in column G
var sheet = SpreadsheetApp.getActiveSheet();
var totalRows = sheet.getLastRow();
var range = sheet.getRange(2, COLUMN_WITH_EMAIL_ADDRESSES, totalRows, 1);
var emails = range.getValues();
// Attempt to iterate through 100 times (although we'll timeout before this)
for (var cntr = 0; cntr<100; cntr++ ) {
// If we've reached the end of our last, wrap to the front
if (lastRowProcessed >= totalRows) lastRowProcessed = 1;
// Increment the row we're processing
var currentRow = lastRowProcessed+1;
// Get the email address from the current row
var email = emails[currentRow-2][0];
// If the email address field is empty, skip to the next row
if (!email) {
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24);
continue;
}
// Look for all threads from me to this person
var threads = GmailApp.search('from:me to:'+email);
// If there are no threads, I haven't emailed them before
if (threads.length == 0) {
// Update the spreadsheet row to show we've never emailed
var range = sheet.getRange(currentRow,13, 1, 4 ).setValues([["NEVER", "", "", ""]] );
// And carry on
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24); // cache for 25 minutes
continue;
}
// Beyond a reasonable doubt
var latestDate = new Date(1970, 1, 1);
var starredMsg = "";
var iReplied = ""
// Iterate through each of the message threads returned from our search
for (var thread in threads) {
// Grab the last message date for this thread
var threadDate = threads[thread].getLastMessageDate();
// If this is the latest thread we've seen so far, make note!
if (threadDate > latestDate) {
latestDate = threadDate;
// Check to see if we starred the message (we may be back to overwrite this)
if (threads[thread].hasStarredMessages()) {
starredMsg = "★";
} else {
starredMsg = "";
}
// Open the thread to get messages
var messages = threads[thread].getMessages();
// See who was the last to speak
var lastMsg = messages[messages.length-1];
var lastMsgFrom = lastMsg.getFrom();
// Use regex so we can make our search case insensitive
var re = new RegExp(email,"i");
// If we can find their email address in the email address from the last message, they spoke last
// (we may be back to overwrite this)
if (lastMsgFrom.search(re) >= 0) {
iReplied = "NO";
} else {
iReplied = "YES";
}
}

Is there a limit to how many G-Forms i can link to a singe google spreadshee?

I have a script that is designed to create about 120 forms and link them to a single spreadsheet where I will analyze the data. I don't have any issues with the script until my spreadsheet has about a dozen forms linked to it. Then I get an error saying the destination ID is invalid, after logging the id, and entering it manually into a url I see no issues with the ID....
var ssSummaryId = '******redacted*******';
var form = FormApp.create('RM#' + rmNumber).setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
form.setDescription(valuesSummary[ii][2])
.setConfirmationMessage('Thanks for the update on room ' + rmNumber)
.setShowLinkToRespondAgain(false);
// form.setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
var formId = form.getId();
var formUrl = form.getPublishedUrl();
EDIT I'm adding my complete script and some additional info, just in case someone wants to check my code out and point out all it rookie mistakes.
I'm using Google scripts to build a spreadsheet and then create 120 slightly altered Google forms that are linked to a single spreadsheet, all the responses are by design on separate sheets named "form responses n". I consistently hit a wall once I exceed 10 forms linked to one sheet. Note; in initial testing I remember having a spreadsheet with 46 forms (and therefore sheets) linked to it. As you can see in the code below, I have the app restart from where it's left off after every 5 forms are created, so I'm not getting any 'extended runtime errors". Just the error below, typically after the script runs twice from Google Scripts IDE.
I'm just finally getting the hand of basic javascript after years of using and modifying js in web developing. So I apologize in advanced for the poor code.
Failed to set response destination. Verify the destination ID and try again. (line 54, file "Code")
function spreadsheet_builder() {
var ssSummaryId = '<<REDACTED>>';
var ssFormDataId = '<<REDACTED>>';
var batchSize = 5;
var buildStatus = false;
var ssSummary = SpreadsheetApp.openById(ssSummaryId);
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
if (ssSummary.getSheetByName('Summary') == null) {
var sheetSummary = ssSummary.getSheetByName('Sheet1');
} else {
var sheetSummary = ssSummary.getSheetByName('Summary');
}
var rangeSummary = sheetSummary.getDataRange();
var valuesSummary = rangeSummary.getValues();
buildStatus = get_last_position(valuesSummary, buildStatus); //either returns last position in array or 'true' if task is complete
if (buildStatus != true || buildStatus > 0) {
var formCreation = [];
var formData = get_form_data(ssFormDataId); // Get form questions from form Data ss, might be better to keep everything on the same sheet
batchSize = buildStatus + batchSize;
for ( var ii = buildStatus; ii < batchSize; ii++ ) {
if (valuesSummary[ii][1] != '') {
var formCreationReturn = form_builder(formData, valuesSummary, ii, ssSummaryId);
formCreation.push(formCreationReturn);
}
}
var aSum = [ssSummary, sheetSummary, rangeSummary];
final_storing_operation(formCreation, aSum, buildStatus);
}
if (sheetSummary.getName() != 'Summary') {
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
sheetSummary.activate().setName('Summary');
sheetSummary.setFrozenColumns(3);
sheetSummary.setFrozenRows(1);
sheetSummary.hideColumns(1);
//var sSumIndex = sheetSummary.getIndex();
}
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
sheetSummary.activate();
ssSummary.moveActiveSheet(1);
}
function form_builder(formData, valuesSummary, ii, ssSummaryId) {
var lastFormCreated = ii;
var rmNumber = valuesSummary[ii][1];
var form = FormApp.create('RM#' + rmNumber).setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
form.setDescription(valuesSummary[ii][2])
.setConfirmationMessage('Thanks for the update on room ' + rmNumber)
.setShowLinkToRespondAgain(false);
// form.setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
var formId = form.getId();
var formUrl = form.getPublishedUrl();
var sectionHeader = 'SECTION_HEADER'; //preformatted form question types.
var list = 'LIST';
var paragraphText = 'PARAGRAPH_TEXT';
for (var j = 1; j < formData.length; j++) { //top row is header
switch (formData[j][0]) {
case sectionHeader:
form.addSectionHeaderItem().setTitle(formData[j][1]);
break;
case list:
var item = form.addListItem();
item.setTitle(formData[j][1]).setHelpText(formData[j][2]);
item.setChoices([
item.createChoice(formData[j][3]),
item.createChoice(formData[j][4]),
item.createChoice(formData[j][5])
]);
break;
case paragraphText:
form.addParagraphTextItem().setTitle(formData[j][1]);
break;
default:
form.addSectionHeaderItem().setTitle('OOPS u\'don MESSED up');
break;
}
}
return [formId, formUrl, lastFormCreated, rmNumber];
}
function final_storing_operation(formCreation, aSum, buildStatus) {
SpreadsheetApp.setActiveSpreadsheet(aSum[0]);
aSum[1].activate();
var startRow = formCreation[0][2] + 1;
var newRange = aSum[1].getRange(startRow, 1, formCreation.length, 2); //row, clmn, rows, columns
var newValues = [];
for ( var ij = 0; ij < formCreation.length; ij++) {
var values = [formCreation[ij][0], "\=HYPERLINK(\"" + formCreation[ij][1] + "\", \"RM#" + formCreation[ij][3] + "\")"];
newValues.push(values);
}
newRange.setValues(newValues);
}
function get_last_position (valuesSummary, buildStatus) {
var rowPos = 1; // start at 1 to ignore headers
while (valuesSummary[rowPos][1] != '') {
if (valuesSummary[rowPos][0] == '') {
return rowPos;
}
rowPos++;
}
if(valuesSummary[rowPos][0] != '' && valuesSummary[rowPos][1] != '') {
buildStatus = true;
return buildStatus;
}
}
function get_form_data (ssFormDataId) {
var ssFormData = SpreadsheetApp.openById(ssFormDataId);
SpreadsheetApp.setActiveSpreadsheet(ssFormData);
var sheetFormData = ssFormData.getSheets()[0];
var rangeFormData = sheetFormData.getDataRange();
var valuesFormData = rangeFormData.getValues();
return valuesFormData;
}
As an alternative, you could create the forms, and intentionally not link them to a spreadsheet, then have some code that looped through every form, and extracted the data. You'd probably want to put the forms into their own folder.
Or, you'd need to build a form with Apps Script HTML Service, embed it into a Apps Script Gadget in a Google Site, and have everyone fill out the form from Sites.

google apps script very very slow

I am using a google spreadsheet as a simple database of members and I want to create a user interface for searching through them (The primary users for this are not very technically adept and there is quite allot of data associated with each member so viewing it as a spreadsheet can be a bit tedius)
I have written the folowing script which worked last night but appears to run so slowly it times out today and I have no idea why.
function findMember() {
// set spreadsheet variable
var SS = SpreadsheetApp.getActiveSpreadsheet();
// set sheet variables
var memberSearchSheet = SS.getActiveSheet();
var memberDataSheet = SS.getSheetByName("Member Data");
// get the search variables
var searchFirstName = memberSearchSheet.getRange('C2').getValue();
var searchLastName = memberSearchSheet.getRange('C3').getValue();
// get last row of data
var lastRow = memberDataSheet.getLastRow();
for (var i = 2;lastRow;i=i+1){
if (searchFirstName == memberDataSheet.getRange('R'+i+'C2').getValue() && searchLastName == memberDataSheet.getRange('R'+i+'C3').getValue()){
memberSearchSheet.getRange('C5').setValue(memberDataSheet.getRange('R'+i+'C5').getValue());
//throw new Error("ouch")
}
}
// small pop up notification in bottom right corner .toast(message, title, display time)
//var message = "Your search for " + searchFirstName + " " + searchLastName + " is complete.";
//SS.toast(message,"Search Complete",15);
};
You can probably trying getting all the data inside an array in one step and then quickly compare your value with those in the array. Something like this:
var sheet = SpreadsheetApp.getActive().getSheetByName("Member Data");
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
if (data[i][0] == firstName && data[i][1] == secondName) {
throw("Found");
}
}

GmailApp and forward/reply Invoked too many times - Not clear why (plus movetoarchive not working)

I am using the following two scripts to either reply to or forward emails when certain labels are applied. I have two sheets (replySheet and forwardSheet) that hold label names in the first column. replySheet then has the email reply text in the next cell, while forwardSheet has the email address to forward the message to.
Two questions:
I have received the error message "Service invoked too many times for one day" for GmailApp.getUserLabelByName. I understand that the limit for Google Apps for Education is 10,000 per day, but this code should just run every five minutes, or 288 times every day for each label. What am I misunderstanding? Any thoughts for re-writing the code to avoid this?
.moveToArchive() doesn't seem to do anything in replyLabel(). I've tried moving it to different points in the code (before and after sending the reply), but it doesn't archive the thread.
Thank you for any suggestions to either issue. Please let me know if I can make my question any clearer.
var thisSS = SpreadsheetApp.getActiveSpreadsheet();
var forwardSheet = thisSS.getSheetByName('Forwards');
var emailSheet = thisSS.getSheetByName('Email');
var alias = emailSheet.getRange(3, 2).getValue();
var replyTo = emailSheet.getRange(2, 2).getValue();
var fromName = emailSheet.getRange(1, 2).getValue();
var replySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Replies');
function forwardLabel() {
var data = forwardSheet.getRange(2, 1, forwardSheet.getLastRow(), 2).getValues();
for (i in data) {
var row = data[i];
var name = row[0].toString();
var email = row[1].toString();
if (name && (email != "")) {
var label = GmailApp.getUserLabelByName(name);
var threads = label.getThreads(0, 100);
for (i in threads) {
var messages = threads[i].getMessages();
for (j in messages) {
Logger.log(messages[j].getSubject());
messages[j].forward(email, {bcc:alias, from:alias, name:fromName}).markRead();
label.removeFromThread(threads[i]);
}
Utilities.sleep(1000);
}
}
}
}
function replyLabel() {
var data = replySheet.getRange(2, 1, replySheet.getLastRow(), 2).getValues();
var signature = emailSheet.getRange(4, 2).getValue().toString();
var alias = emailSheet.getRange(3, 2).getValue();
for (i in data) {
var labelName = data[i][0].toString();
var label = GmailApp.getUserLabelByName(labelName);
var replyText = data[i][1].toString();
replyText = replyText + signature;
if (label && (replyText !== "")) {
var labeledEmails = label.getThreads(0, 100);
for (j in labeledEmails) {
labeledEmails[j].moveToArchive();
label.removeFromThread(labeledEmails[j]);
var messages = labeledEmails[j].getMessages();
var message = messages[0];
message.reply(replyText,{htmlBody:replyText, bcc:alias, from:alias, name:fromName});
Utilities.sleep(2000);
}
}
}
}
How many labels do you have ? You have nested loops and the 288 gets multiplied by each loop and you could quickly be hitting the 10,000.
Also, note that you are not counting other GMail Read operations like getTHreads() and getMessages().
If you factor in all these, you could have a number exceeding 10,000.