I've been working to automatically pull data from an automated Gmail message. There are multiple daily emails that come through with the same label, so ideally I would like to loop through each email, and extract some of the data. I've set it up to use a few regex to grab the data, and it works for the first email. However, it won't loop correctly to find the next email with the label. Here is the code I have so far:
function parseEmailMessages (start) {
var label = GmailApp.getUserLabelByName("Bounce");
var threads = label.getThreads();
var sheet = SpreadsheetApp.getActiveSheet();
var tmp = [];
var messages = GmailApp.getMessagesForThreads(threads);
var bodies = [];
for (var i =0; i < threads.length; i++) {
var bodies = [];
for(k in threads[i].getMessages()) {
bodies.push(threads[i].getMessages()[i].getPlainBody());
var content = bodies.toString();
if (content) {
tmp = content.match(/[\n\r].*First Name\s*:\s*([^\n\r]*)/);
var firstname = (tmp && tmp[1]) ? tmp[1].trim() : 'No username';
tmp = content.match(/[\n\r].*Last Name\s*:\s*([^\n\r]*)/);
var lastname = (tmp && tmp[1]) ? tmp[1].trim() : 'No Lastname';
tmp = content.match(/[\n\r].*Customer ID\s*:\s*([^\n\r]*)/);
var customerID = (tmp) ? tmp[1].trim() : 'No CustomerID';
tmp = content.match(/[\n\r].*Invoice\s*:\s*([^\n\r]*)/);
var invoice = (tmp) ? tmp[1].trim() : 'No Invoice';
sheet.appendRow([firstname, lastname, customerID, invoice]);
Logger.log([firstname,lastname, customerID, invoice]);
}
}
}
};
It loops through correctly the first time, and then gives me an error: TypeError: Cannot call method "getPlainBody" of undefined.
Any help would be greatly appreciated!
You are seeing that error because you should use k variable in the for loop to get each message of that label. Check this line below:
threads[i].getMessages()[k].getPlainBody()
Tried changing this line in the for loop and its working for me.
Hope that helps!
After simplifying my code, I was able to get the script to loop correctly through my emails. This is the code that worked for me:
function processInboxToSheet() {
// Have to get data separate to avoid google app script limit!
//var start = 0;
var label = GmailApp.getUserLabelByName("Bounce");
var threads = label.getThreads();
var sheet = SpreadsheetApp.getActiveSheet();
var result = [];
var newLabel = GmailApp.getUserLabelByName("Done");
var oldLabel = GmailApp.getUserLabelByName("Bounce");
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
var content = messages[0].getPlainBody();
// implement your own parsing rule inside
if (content) {
var tmp;
tmp = content.match(/[\n\r].*First Name\s*:\s*([^\n\r]*)/);
var firstname = (tmp && tmp[1]) ? tmp[1].trim() : 'No username';
tmp = content.match(/[\n\r].*Last Name\s*:\s*([^\n\r]*)/);
var lastname = (tmp && tmp[1]) ? tmp[1].trim() : 'No Lastname';
tmp = content.match(/[\n\r].*Customer ID\s*:\s*([^\n\r]*)/);
var customerID = (tmp) ? tmp[1].trim() : 'No CustomerID';
tmp = content.match(/[\n\r].*Invoice\s*:\s*([^\n\r]*)/);
var invoice = (tmp) ? tmp[1].trim() : 'No Invoice';
sheet.appendRow([firstname, lastname, customerID, invoice]);
}
Utilities.sleep(500);
threads[i].addLabel(newLabel).removeLabel(oldLabel).refresh();
}
};
Related
//Invoice find and transfer to Warehouse Sheet
function searchInvoiceWhSh() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var shUserForm = ss.getSheetByName("Warehouse Form")
var shSalesSheet = ss.getSheetByName("Sales")
var sValue = shUserForm.getRange("G5").getValue();
var sData = shSalesSheet.getDataRange().getValues();
var currentRow = 9
for (var i=0; i<sData.length; i++) {
var row = sData[i];
if (row[0] == sValue) { //do something}
currentRow += 2
}}
I've used this to search for an "Invoice number" from the "Sales" worksheet and when found to transfer the data back to the user form.
If, for example, the invoice number is typed incorrectly into the "sValue" cell, then no data will be transferred.
How do I code a prompt message to ask the user to check the invoice number as no records were found?
Try:
function searchInvoiceWhSh() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const shUserForm = ss.getSheetByName("Warehouse Form")
const shSalesSheet = ss.getSheetByName("Sales")
const sValue = shUserForm.getRange("G5").getValue()
const sData = shSalesSheet.getDataRange().getValues()
const targetData = sData.filter(row => row[0] === sValue)
if (targetData.length) {
// Value(s) found
targetData.forEach(row => {
Logger.log(row)
})
} else {
SpreadsheetApp.getUi().alert(`No match found.`)
}
}
This will search for the sValue provided as in your code, but will store the row in a variable once found. If it's not found, it will create an alert pop-up with your specified message.
Alternatively, you can check out UI Class for other pop-up options.
Try it like this:
function myfunk() {
var ss = SpreadsheetApp.getActive()
var fsh = ss.getSheetByName("Warehouse Form")
var ssh = ss.getSheetByName("Sales")
var fv = fsh.getRange("G5").getValue();
var svs = ssh.getDataRange().getValues();
let m = 0;
svs.forEach((r, i) => {
if (r[0] == fv) {
m++;
}
SpreadsheetApp.getUi().alert(`${m} matches found`)
});
}
Always provides a result
Very inexperienced coder here, I have recently gotten a script working that uses regex to search for two different words occurring within a certain word limit. So I can search for "the" and "account" occurring within 10 words of each other, then my script prints the sentence it occurs in. However, my work requires me to search for lots of different work combinations and it has become a pain having to enter each word manually into the string /\W*(the)\W*\s+(\w+\s+){0,10}(account)|(account)\s+(\w+\s+){0,10}(the)/i; for example.
I would like to have something in the script where I can enter the words I want to search for just once, and they will be used in the string above. I have tried, what I think is, declaring variables like this:
var word1 = the
var word2 = account
/\W*(word1)\W*\s+(\w+\s+){0,10}(word2)|(word2)\s+(\w+\s+){0,10}(word1)/i;
But, again, very experienced coder so I'm a little out of my depth. Would really like something like the script snippet above to work in my full script listed below.
Here is my full working script without my attempt at declaring variables mentioned above:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var historySheet = ss.getSheetByName('master');
var resultsSheet = ss.getSheetByName('results');
var totalRowsWithData = historySheet.getDataRange().getNumRows();
var data = historySheet.getRange(1, 1, totalRowsWithData, 3).getValues();
var regexp = /\W*(the)\W*\s+(\w+\s+){0,10}(account)|(account)\s+(\w+\s+){0,10}(the)/i;
var result = [];
for (var i = 0; i < data.length; i += 1) {
var row = data[i];
var column = row[0];
if (regexp.exec(column) !== null) {
result.push(row); }}
if (result.length > 0) {
var resultsSheetDataRows = resultsSheet.getDataRange().getNumRows();
resultsSheetDataRows = resultsSheetDataRows === 1 ? resultsSheetDataRows : resultsSheetDataRows + 1;
var resultsSheetRange = resultsSheet.getRange(resultsSheetDataRows, 1, result.length, 3);
resultsSheetRange.setValues(result);}}
I tried this solution but not sure I have done it correctly as it only enters results in logs and not printing in the "results" sheet:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var historySheet = ss.getSheetByName('Sheet1');
var resultsSheet = ss.getSheetByName('Results1');
var totalRowsWithData = historySheet.getDataRange().getNumRows();
var data = historySheet.getRange(1, 1, totalRowsWithData, 3).getValues();
const regexpTemplate = '\W*(word1)\W*\s+(\w+\s+){0,10}(word2)|(word2)\s+(\w+\s+){0,10}(word1)';
var word1 = 'test1';
var word2 = 'test2';
var regexpString = regexpTemplate.replace(/word1/g, word1).replace(/word2/g, word2);
var regexp = new RegExp(regexpString, 'i');
Logger.log(regexp); // /W*(the)W*s+(w+s+){0,10}(account)|(account)s+(w+s+){0,10}(the)/i
var result = [];
for (var i = 0; i < data.length; i += 1) {
var row = data[i];
var column = row[0];
if (regexp.exec(column) !== null) {
result.push(row); }}
if (result.length > 0) {
var resultsSheetDataRows = resultsSheet.getDataRange().getNumRows();
resultsSheetDataRows = resultsSheetDataRows === 1 ? resultsSheetDataRows : resultsSheetDataRows + 1;
var resultsSheetRange = resultsSheet.getRange(resultsSheetDataRows, 1, result.length, 3);
resultsSheetRange.setValues(result);}}
Use the RegExp contructor.
const regexpTemplate = '\\W*(word1)\\W*\\s+(\\w+\\s+){0,10}(word2)|(word2)\\s+(\\w+\\s+){0,10}(word1)';
var word1 = 'the';
var word2 = 'account';
var regexpString = regexpTemplate.replace(/word1/g, word1).replace(/word2/g, word2);
var regexp = new RegExp(regexpString, 'i');
Logger.log(regexp); // /\W*(the)\W*\s+(\w+\s+){0,10}(account)|(account)\s+(\w+\s+){0,10}(the)/i
You can put this into a function to easily generate your new regular expression whenever you want to update the words.
/**
* Generate the regular expression with the provided words.
* #param {String} word1
* #param {String} word2
* #returns {RegExp}
*/
function generateRegExp(word1, word2) {
const regexpTemplate = '\\W*(word1)\\W*\\s+(\\w+\\s+){0,10}(word2)|(word2)\\s+(\\w+\\s+){0,10}(word1)';
var regexpString = regexpTemplate.replace(/word1/g, word1).replace(/word2/g, word2);
return new RegExp(regexpString, 'i');
}
/**
* Test the generateRegExp() function.
*/
function test_generateRegExp() {
var word1 = 'the';
var word2 = 'account';
var regexp = generateRegExp(word1, word2); // /\W*(the)\W*\s+(\w+\s+){0,10}(account)|(account)\s+(\w+\s+){0,10}(the)/i
// Use regexp just as you do in your script
// i.e. if (regexp.exec(column) !== null) { result.push(row); }
}
Your final script could look something like this.
function printSentences() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var historySheet = ss.getSheetByName('Sheet1');
var resultsSheet = ss.getSheetByName('Results1');
var totalRowsWithData = historySheet.getDataRange().getNumRows();
var data = historySheet.getRange(1, 1, totalRowsWithData, 3).getValues();
var result = [];
var regexp = generateRegExp("the", "account");
for (var i = 0; i < data.length; i += 1) {
var row = data[i];
var column = row[0];
if (regexp.exec(column) !== null) {
result.push(row);
}
}
if (result.length > 0) {
var resultsSheetDataRows = resultsSheet.getDataRange().getNumRows();
resultsSheetDataRows = resultsSheetDataRows === 1 ? resultsSheetDataRows : resultsSheetDataRows + 1;
var resultsSheetRange = resultsSheet.getRange(resultsSheetDataRows, 1, result.length, 3);
resultsSheetRange.setValues(result);
}
}
/**
* Generate the regular expression with the provided words.
* #param {String} word1
* #param {String} word2
* #returns {RegExp}
*/
function generateRegExp(word1, word2) {
const regexpTemplate = '\\W*(word1)\\W*\\s+(\\w+\\s+){0,10}(word2)|(word2)\\s+(\\w+\\s+){0,10}(word1)';
var regexpString = regexpTemplate.replace(/word1/g, word1).replace(/word2/g, word2);
return new RegExp(regexpString, 'i');
}
I have two questions here
Is it possible to add dynamic lists values to field injection list input ?
Can I create a trigger for this so this can be initiated from any other input selection say a class selection will populate all fields
I was just looking into FieldInjection.js whether that can be extented for the same
Can someone please provide a hint or direction for this ?
Thanks.
For anyone interested in the answer, I was able to achieve the above goal by changing the set function of the Java Class select input as folllowing
few imports
var extensionElementsHelper = require('../../../../helper/ExtensionElementsHelper'),
elementHelper = require('../../../../helper/ElementHelper')
var CAMUNDA_FIELD_EXTENSION_ELEMENT = 'camunda:Field';
function getExtensionFields(bo) {
return bo && extensionElementsHelper.getExtensionElements(bo, CAMUNDA_FIELD_EXTENSION_ELEMENT) || [];
}
then changing the set function to create extension element and push the field values as :
set: function(element, values, node) {
var bo = getBusinessObject(element);
var type = getImplementationType(element);
var attr = getAttribute(type);
var prop = {}
var commands = [];
prop[attr] = values.delegate || '';
var extensionElements = getExtensionFields(bo);
//remove any extension elements existing before
extensionElements.forEach(function(ele){
commands.push(extensionElementsHelper.removeEntry(getBusinessObject(element), element, ele));
});
if(prop[attr] !== ""){
var extensionElements = elementHelper.createElement('bpmn:ExtensionElements', { values: [] }, bo, bpmnFactory);
commands.push(cmdHelper.updateBusinessObject(element, bo, { extensionElements: extensionElements }));
var arrProperties = ["private org.camunda.bpm.engine.delegate.Expression com.cfe.extensions.SampleJavaDelegate.varOne","private org.camunda.bpm.engine.delegate.Expression com.cfe.extensions.SampleJavaDelegate.varTwo"]
var newFieldElem = "";
arrProperties.forEach(function(prop){
var eachProp = {
name:"",
string:"",
expression:""
}
var type = prop.split(" ")[1].split(".").reverse()[0];
var val = prop.split(" ")[2].split(".").reverse()[0];
eachProp.name = val;
if( type == "String"){
eachProp.string = "${" + val +" }"
}else if( type == "Expression"){
eachProp.expression = "${" + val +" }"
}
newFieldElem = elementHelper.createElement(CAMUNDA_FIELD_EXTENSION_ELEMENT, eachProp, extensionElements, bpmnFactory);
commands.push(cmdHelper.addElementsTolist(element, extensionElements, 'values', [ newFieldElem ]));
});
}
commands.push(cmdHelper.updateBusinessObject(element, bo, prop));
return commands;
}
Cheers !.
I have an email sent out on a daily basis that I need to pull data from to a Google Sheet. I have read on this and found a solution that worked for other's, but I've been unable to get it to work on mine. This is the code I've tried modifying:
function menu(e) {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Macros')
.addItem('parse mail', 'email')
.addToUi();
}
function parseMail(body) {
var a=[];
var keystr="First Name : ,Last Name : ,Customer ID : ,Invoice : ";
var keys=keystr.split(",");
var i,p,r;
for (i in keys) {
p=keys[i]+"\n([a-zA-Z0-9\ \,\.]*)\n";
r=new RegExp(p,"m");
try {a[i]=body.match(p)[1];}
catch (err) {a[i]="no match";}
}
}
function email() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var threads = GmailApp.search('subject: "Fwd: ARB Subscription*"');
var a=[];
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
for (var j = 0; j < messages.length; j++) {
a[j]=parseMail(messages[j].getPlainBody());
}
}
var nextRow=s.getDataRange().getLastRow()+1;
var numRows=a.length;
var numCols=a.length[0];
s.getRange(nextRow,1,numRows,numCols).setValues(a);
}
I've tried modifying the code, but the error I am getting is Cannot convert Array to Object[][] Line 35. any advice would be greatly appreciated
Get the dimensions of the data range as below.
Also, when you are setting the values on the range the size of two-dimensional array a must match the size of the range.
var dataRange = s.getDataRange();
var nextRow = dataRange.getLastRow() + 1;
var numRows = dataRange.getNumRows();
var numCols = dataRange.getNumColumns();
s.getRange(nextRow,1,numRows,numCols).setValues(a);
I am trying to extract specific info from email in one of my labels in Gmail. I've hacked (my scripting knowledge is very limited) the following together based on a script from https://gist.github.com/Ferrari/9678772. I am getting an error though: "Cannot convert Array to Gmail Thread - Line 5"
Any help will be greatly appreciated.
/* Based on https://gist.github.com/Ferrari/9678772 */
function parseEmailMessages(start) {
/* var threads = GmailApp.getInboxThreads(start, 100); */
var threads = GmailApp.getMessagesForThread(GmailApp.search("label:labelname"));
var sheet = SpreadsheetApp.getActiveSheet();
var tmp, result = [];
for (var i = 0; i < threads.length; i++) {
// Get the first email message of a threads
var message = threads[i].getMessages()[0];
// Get the plain text body of the email message
// You may also use getRawContent() for parsing HTML
var content = messages[0].getPlainBody();
// Implement Parsing rules using regular expressions
if (content) {
tmp = content.match(/Name and Surname:\n([A-Za-z0-9\s]+)(\r?\n)/);
var username = (tmp && tmp[1]) ? tmp[1].trim() : 'No username';
tmp = content.match(/Phone Number:\n([\s\S]+)/);
var phone = (tmp && tmp[1]) ? tmp[1] : 'No phone';
tmp = content.match(/Email Address:\n([A-Za-z0-9#.]+)/);
var email = (tmp && tmp[1]) ? tmp[1].trim() : 'No email';
tmp = content.match(/Prefered contact office:\n([\s\S]+)/);
var comment = (tmp && tmp[1]) ? tmp[1] : 'No office';
sheet.appendRow([username, phone, email, comment]);
}
}
};
Thanks folks.. This did the trick:
// Adapted from https://gist.github.com/Ferrari/9678772
function processInboxToSheet() {
// Have to get data separate to avoid google app script limit!
var start = 0;
var label = GmailApp.getUserLabelByName("yourLabelName");
var threads = label.getThreads();
var sheet = SpreadsheetApp.getActiveSheet();
var result = [];
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
var content = messages[0].getPlainBody();
// implement your own parsing rule inside
if (content) {
var tmp;
tmp = content.match(/Name and Surname:\n([A-Za-z0-9\s]+)(\r?\n)/);
var username = (tmp && tmp[1]) ? tmp[1].trim() : 'No username';
tmp = content.match(/Phone Number:\n([\s\S]+)/);
var phone = (tmp && tmp[1]) ? tmp[1] : 'No phone';
tmp = content.match(/Email Address:\n([A-Za-z0-9#.]+)/);
var email = (tmp && tmp[1]) ? tmp[1].trim() : 'No email';
tmp = content.match(/Prefered contact office:\n([\s\S]+)/);
var comment = (tmp && tmp[1]) ? tmp[1] : 'No office';
sheet.appendRow([username, phone, email, comment]);
Utilities.sleep(500);
}
}
};
var threads = GmailApp.getMessagesForThread(GmailApp.search("label:labelname"));
should include an array index since GmailApp.search returns an array, even if only one item is found.
var threads = GmailApp.getMessagesForThread(GmailApp.search("label:labelname")[0]);
would work but is wordy.
var thread_list = GmailApp.search("label:labelname");
var threads = GmailApp.getMessagesForThread(thread_list[0]);
IMO, the above is clearer in meaning.