Diasabling user input during skill choice botpress - chatbot

I am new to botpress and working on a project where I want to disable the user input and send button. so is this possible in the current botpress version? if anyone can help me with this?

In file /data/assets/ui-studio/public/js/lite.7df1ba548a26568689c1.js copy the following script at the end.
var ua = window.navigator.userAgent;
var trident = ua.indexOf('Trident/');
var edge = ua.indexOf('Edge/');
var msie = ua.indexOf('MSIE ');
var intIE = 0 ;
var intcnt = 0 ;
if (trident > 0 || edge > 0 || msie > 0 ){
intIE = 1 ;
}
// target element that we will observe
const target = document.getElementById("app");
// config object
const config = {
childList: true,
subtree: true
};
function trackchange(mutations) {
var iframe = document.getElementById("app");
var str = iframe.outerHTML ;
var n = str.search("bpw-keyboard-quick_reply");
if (n > 1){
if (intIE < 1){
document.getElementById("input-message").placeholder = "Select from above options";
}
document.getElementById("btn-send").style.display = "none" ;
document.getElementById("input-message").disabled = true;
}else{
if (intIE < 1){
document.getElementById("input-message").placeholder = "Type here";
}
if (intcnt < 1){
intcnt = 0 ;
//document.getElementById("input-message").value = "Hi";
}
document.getElementById("btn-send").style.display = "" ;
document.getElementById("input-message").disabled = false;
}
}
// instantiating observer
const observer = new MutationObserver(trackchange);
// observing target
observer.observe(target, config);

Related

Can't figure out how to select items in the Facebook newsfeed for a Chome Extension

I'm working on a Chrome extension that inserts a button on every item in the Facebook newsfeed. I started off by using Tampermonkey to install a script that installs a button next to the subscribe video of every Youtube page (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/ask.html?aid=994ff494-1242-452f-a334-1bd616e18bb6), which worked fine.
Then I tried to modify it so it acts on the Facebook newsfeed, rather than the Youtube subscribe button. I changed the // match to go to facebook.com and targeting it to go after class='_3vuz', the Facebook div that houses the "like" button. But nothing happens when I go on Facebook; no button appears.
Here's my Tampermonkey code. Many thanks!
// ==UserScript==
// #name Facebook Fake News Button
// #namespace https://www.youtubeinmp3.com
// #version 1.2.2
// #description Adds a button to show you whether a Facebook article is true
// #author Ilana
// #match https://www.facebook.com/*
// #run-at document-end
// ==/UserScript==
function polymerInject(){
/* Create button */
var buttonDiv = document.createElement("div");
buttonDiv.style.width = "100%";
buttonDiv.id = "parentButton";
var addButton = document.createElement("button");
addButton.appendChild(document.createTextNode("Fake News"));
if(typeof(document.getElementById("iframeDownloadButton")) != 'undefined' && document.getElementById("iframeDownloadButton") !== null){
document.getElementById("iframeDownloadButton").remove();
}
addButton.style.width = "100%";
addButton.style.backgroundColor = "#181717";
addButton.style.color = "white";
addButton.style.textAlign = "center";
addButton.style.padding = "10px 0";
addButton.style.marginTop = "5px";
addButton.style.fontSize = "14px";
addButton.style.border = "0";
addButton.style.cursor = "pointer";
addButton.style.borderRadius = "2px";
addButton.style.fontFamily = "Roboto, Arial, sans-serif";
addButton.onclick = function () {
this.remove();
/* Add large button on click */
var addIframe = document.createElement("iframe");
addIframe.src = '//www.convertmp3.io/widget/button/?color=ba1717&video=' + window.location.href;
addIframe.style.width = "100%";
addIframe.style.border = "none";
addIframe.style.height = "60px";
addIframe.style.marginTop = "10px";
addIframe.style.overflow = "hidden";
addIframe.scrolling = "no";
addIframe.id = "iframeDownloadButton";
var targetElement = document.querySelectorAll("[id='meta']");
for(var i = 0; i < targetElement.length; i++){
if(targetElement[i].className.indexOf("ytd-watch") > -1){
targetElement[i].insertBefore(addIframe, targetElement[i].childNodes[0]);
}
}
};
buttonDiv.appendChild(addButton);
/* Find and add to target */
var targetElement = document.querySelectorAll("[class='_3vuz']");
for(var i = 0; i < targetElement.length; i++){
if(targetElement[i].className.indexOf("ytd-video-secondary-info-renderer") > -1){
targetElement[i].appendChild(buttonDiv);
}
}
/* Fix hidden description bug */
var descriptionBox = document.querySelectorAll("ytd-video-secondary-info-renderer");
if(descriptionBox[0].className.indexOf("loading") > -1){
descriptionBox[0].classList.remove("loading");
}
}
function standardInject() {
var pagecontainer=document.getElementById('page-container');
if (!pagecontainer) return;
if (/^https?:\/\/www\.facebook.com\/watch\?/.test(window.location.href)) run();
var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
var logocontainer=document.getElementById('logo-container');
if (logocontainer && !isAjax) { // fix for blocked videos
isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
}
var content=document.getElementById('content');
if (isAjax && content) { // Ajax UI
var mo=window.MutationObserver||window.WebKitMutationObserver;
if(typeof mo!=='undefined') {
var observer=new mo(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes!==null) {
for (var i=0; i<mutation.addedNodes.length; i++) {
if (mutation.addedNodes[i].id=='watch7-container' ||
mutation.addedNodes[i].id=='watch7-main-container') { // old value: movie_player
run();
break;
}
}
}
});
});
observer.observe(content, {childList: true, subtree: true}); // old value: pagecontainer
} else { // MutationObserver fallback for old browsers
pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
}
}
}
function onNodeInserted(e) {
if (e && e.target && (e.target.id=='watch7-container' ||
e.target.id=='watch7-main-container')) { // old value: movie_player
run();
}
}
function finalButton(){
var buttonIframeDownload = document.createElement("iframe");
buttonIframeDownload.src = '//www.convertmp3.io/widget/button/?color=ba1717&video=' + window.location.href;
buttonIframeDownload.id = "buttonIframe";
buttonIframeDownload.style.width = "100%";
buttonIframeDownload.style.height = "60px";
buttonIframeDownload.style.paddingTop = "20px";
buttonIframeDownload.style.paddingBottom = "20px";
buttonIframeDownload.style.overflow = "hidden";
buttonIframeDownload.scrolling = "no";
document.getElementById("watch-header").appendChild(buttonIframeDownload);
}
function run(){
if(!document.getElementById("parentButton") && window.location.href.substring(0, 25).indexOf("facebook.com") > -1 && window.location.href.indexOf("watch?") > -1){
var parentButton = document.createElement("div");
parentButton.className = "yt-uix-button yt-uix-button-default";
parentButton.id = "parentButton";
parentButton.style.height = "23px";
parentButton.style.marginLeft = "28px";
parentButton.style.paddingBottom = "1px";
parentButton.onclick = function () {
this.remove();
finalButton();
};
document.getElementById("watch7-user-header").appendChild(parentButton);
var childButton = document.createElement("span");
childButton.appendChild(document.createTextNode("Download MP3"));
childButton.className = "yt-uix-button-content";
childButton.style.lineHeight = "25px";
childButton.style.fontSize = "12px";
parentButton.appendChild(childButton);
}
}
if(document.getElementById("polymer-app") || document.getElementById("masthead") || window.Polymer){
setInterval(function(){
if(window.location.href.indexOf("watch?v=") < 0){
return false;
}
if(document.getElementById("count") && document.getElementById("parentButton") === null){
polymerInject();
}
}, 100);
}
else{
standardInject();
}

VSCode language extension with hierarchical Outline, DocumentSymbol

I'm trying to get outline working with a custom language in VScode. I have the below code but I feel like it is slow because of the way I find a range in class. Are there better ways to find the range and assign children. I've thought about just keeping track of the depth of the brackets and assigning all functions/methods/classes in higher depths into the last item of previous depth.
It was based off of this answer.
class JSLDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
public provideDocumentSymbols(document: vscode.TextDocument,
token: vscode.CancellationToken): Thenable<vscode.DocumentSymbol[]> {
return new Promise((resolve, reject) => {
var symbols: vscode.DocumentSymbol[] = [];
var depth = 0;
for (var i = 0; i < document.lineCount; i++) {
var line = document.lineAt(i);
var txt = line.text;
var ltxt = txt.toLowerCase();
let open_brackets = ltxt.match(/\(/g) || [];
let close_brackets = ltxt.match(/\)/g) || [];
// console.log(ltxt)
// console.log(open_brackets, close_brackets)
//console.log(i, open_brackets.length, close_brackets.length)
depth += open_brackets.length - close_brackets.length;
//console.log(depth);
if (ltxt.includes("define class(")) {
let sname = txt.trim().substr(14, txt.trim().length - 16); //this is hard coded right now but it's kind of working
let detail = "ARGS:x, y returns z";
let start_pos = new vscode.Position(i, 0);
let n_bracket = 1;
let i_char = 0;
//let children: vscode.DocumentSymbol[] = []
let ds = new vscode.DocumentSymbol(sname, detail, vscode.SymbolKind.Class, line.range, line.range);
for(var i_line = i; n_bracket > 0; i_line++){
let class_line = document.lineAt(i_line);
let mtxt = class_line.text;
let ic;
if(i == i_line) ic = 16;
else ic = 0;
for(i_char = ic; i_char < mtxt.length; i_char++){
if(mtxt[i_char] === "(") n_bracket++;
else if(mtxt[i_char] === ")") n_bracket--;
if(n_bracket === 0) break
}
if (/(\w[\w\d\s]*)=\s*method\({((?:\s*(?:\w[\w\d\s]*)(?:=[^,]*)?,?\s*)*)},/i.test(mtxt)) {
let result = mtxt.match(/(\w[\w\d\s]*)=\s*method\({((?:\s*(?:\w[\w\d\s]*)(?:=[^,]*)?,?\s*)*)},/i)!;
let mname = result[1].trim();
let m_details = ""
if(result.length == 3){
m_details = result[2].trim();
}
ds.children.push(new vscode.DocumentSymbol(mname, m_details, vscode.SymbolKind.Method, class_line.range, class_line.range));
}
if(n_bracket === 0) break
}
let end_pos = new vscode.Position(i_line, i_char);
let rng = new vscode.Range(start_pos, end_pos);
ds.range = rng;
//ds.children = children;
symbols.push(ds);
}
else if (/(\w[\w\d\s]*)=\s*function\({((?:\s*(?:\w[\w\d\s]*)(?:=[^,]*)?,?\s*)*)},/.test(ltxt)) {
let result = txt.match(/(\w[\w\d\s]*)=\s*function\({((?:\s*(?:\w[\w\d\s]*)(?:=[^,]*)?,?\s*)*)},/i)!;
let sname = result[1].trim();
let detail = "";
if(result.length == 3){
detail = "(" + result[2].trim() + ")";
}
symbols.push(new vscode.DocumentSymbol(sname, detail, vscode.SymbolKind.Function, line.range, line.range));
}
}
resolve(symbols);
});
}
}

Chart.js click on labels, using bar chart

i need help with my Chart.js interactivity. When I click on the label, I need to return the column(index) number at which I clicked.
I tried to use getElementsAtEvent but it only work if I click directly at chart.
This http://jsfiddle.net/yxz2sjam/ is pretty much what I am looking for but getPointsAtEvent is no longer available in the new versions.
canvas.onclick = function (evt) {
var points = chart.getPointsAtEvent(evt);
alert(chart.datasets[0].points.indexOf(points[0]));
};
I also found this http://jsfiddle.net/1Lngmtz7/ but it isn't working with bar chart.
var ctx = document.getElementById("myChart").getContext("2d");
var myRadarChart = new Chart(ctx, {
type: 'radar',
data: data
})
$('#myChart').click(function (e) {
var helpers = Chart.helpers;
var eventPosition = helpers.getRelativePosition(e, myRadarChart.chart);
var mouseX = eventPosition.x;
var mouseY = eventPosition.y;
var activePoints = [];
helpers.each(myRadarChart.scale.ticks, function (label, index) {
for (var i = this.getValueCount() - 1; i >= 0; i--) {
var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5);
var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, Chart.defaults.global.defaultFontSize);
var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, Chart.defaults.global.defaultFontStyle);
var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, Chart.defaults.global.defaultFontFamily);
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
ctx.font = pointLabeFont;
var labelsCount = this.pointLabels.length,
halfLabelsCount = this.pointLabels.length / 2,
quarterLabelsCount = halfLabelsCount / 2,
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
var width = ctx.measureText(this.pointLabels[i]).width;
var height = pointLabelFontSize;
var x, y;
if (i === 0 || i === halfLabelsCount)
x = pointLabelPosition.x - width / 2;
else if (i < halfLabelsCount)
x = pointLabelPosition.x;
else
x = pointLabelPosition.x - width;
if (exactQuarter)
y = pointLabelPosition.y - height / 2;
else if (upperHalf)
y = pointLabelPosition.y - height;
else
y = pointLabelPosition.y
if ((mouseY >= y && mouseY <= y + height) && (mouseX >= x && mouseX <= x + width))
activePoints.push({ index: i, label: this.pointLabels[i] });
}
}, myRadarChart.scale);
var firstPoint = activePoints[0];
if (firstPoint !== undefined) {
alert(firstPoint.index + ': ' + firstPoint.label);
}
});
Thank for response.
I solve the problem with
document.getElementById("chart").onclick = function(e)
{
var activeElement = weatherMainChart.lastTooltipActive;
console.log(activeElement[0]._index);
};
this solution register clicks on chart and label, then I restricted it with e.layerY to register only clicks on label section.
document.getElementById("chart").onclick = function(e)
{
var activeElement = weatherMainChart.lastTooltipActive;
if(e.layerY > 843 && e.layerY < 866 && activeElement[0] !== undefined)
console.log(activeElement[0]._index);
};
If you add a click handler through the onClick option you can use the following code using the getElementsAtEventForMode() call:
function handleClick(evt) {
var col;
switch(chartType) {
case "horizontalBar":
this.getElementsAtEventForMode(evt, "y", 1).forEach(function(item) { col = item._index });
break;
case "bar":
this.getElementsAtEventForMode(evt, "x", 1).forEach(function(item) { col = item._index });
break;
}
if (!col) {
return;
}
alert("Column " + col + " was selected");
};
You'll probably need to add extra switch checks for other chart types but I'm sure you get the idea.
Using version 2.4.0, i created an onClick Event, and inside it
var activeIndex = localChart.tooltip._lastActive[0]._index;
var clickCoordinates = Chart.helpers.getRelativePosition(e, localChart.chart);
if (clickCoordinates.y >= 530) { //custom value, depends on chart style,size, etc
alert("clicked on " + localChart.data.labels[activeIndex]);
}
I Solved this problem with single or multiple label click you will be find using true/false
First you need to set your chartJs Id click
below code SessionChart = Your ChartJs ID e.g. ("myChart") I was replace it for my Id
document.getElementById("SessionChart").onclick = function (evt) {
var meta = SubscriberSessionChart.getDatasetMeta(0);
if (meta.$filler.chart.legend.legendItems[0].text.toLowerCase() "sessions")
{
if (meta.$filler.chart.legend.legendItems[0].hidden) {
sessionHidden = true;
}
}
}
here "sessions" = first label text
meta.$filler.chart.legend.legendItems[0].text.toLowerCase() = is your first label
from Array so you can get multiple label's click here true / false
if (meta.$filler.chart.legend.legendItems[0].hidden) = your label is not active then
you will get hidden true otherwise you will get false if not tick on label
by default label tick hidden is false in chart js

File Uploader - Upload excel file and convert it into JSON and bind to the table

After uploading the excel file on file uploader control in sapui5, excel file is converted in to JSON using following code.
handleExcelUpload : function(e) {
this._import(e.getParameter("files")
&& e.getParameter("files")[0]);
},
_import : function(file) {
if (file && window.FileReader) {
var reader = new FileReader();
that = this;
result = {};
var data;
reader.onload = function(e) {
var data = e.target.result;
var wb = XLSX.read(data, {
type : 'binary'
});
wb.SheetNames
.forEach(function(sheetName) {
var roa = XLSX.utils
.sheet_to_row_object_array(wb.Sheets[sheetName]);
if (roa.length > 0) {
result[sheetName] = roa;
}
});
};
reader.readAsBinaryString(file);
};
},
Note : I have used jszip.js and xlsx.js library to convert excel to JSON
Now in result variable I am getting JSON format data, and this data I have bind to the table.
The issue is, JSON binding with table is working fine with the Chrome, Firefox latest browser but, its not working on in IE 11 browser or it's showing only No data in table
Is there any other file reader method which supports IE11?
Yes I got answer.. I found readAsArrayBuffer method in Javascript which is compatible for all latest browser even in IE11
Here is my working code.
XML code:
<FileUploader id="fileUploader" name="myFileUpload"
class="sapUiSmallMarginEnd" uploadUrl="upload/" width="400px"
tooltip="Upload your file to the local server" uploadComplete="handleUploadComplete"
change="handleExcelUpload" placeholder="Please Select File" />
JS code:
handleExcelUpload : function(e) {
this._import(e.getParameter("files")
&& e.getParameter("files")[0]);
},
_import : function(file) {
debugger;
if (file && window.FileReader) {
var reader = new FileReader();
that = this;
//result = {};
//var data;
reader.onload = function(evt) {
var data = evt.target.result;
//var xlsx = XLSX.read(data, {type: 'binary'});
var arr = String.fromCharCode.apply(null, new Uint8Array(data));
var xlsx = XLSX.read(btoa(arr), {type: 'base64'});
result = xlsx.Strings;
result = {};
xlsx.SheetNames.forEach(function(sheetName) {
var rObjArr = XLSX.utils.sheet_to_row_object_array(xlsx.Sheets[sheetName]);
if(rObjArr.length > 0){
result[sheetName] = rObjArr;
}
});
return result;
that.b64toBlob(xlsx, "binary");
};
reader.readAsArrayBuffer(file);
};
},
b64toBlob : function(b64Data, contentType) {
contentType = contentType || '';
var sliceSize = 512;
b64Data = b64Data.replace(/^[^,]+,/, '');
b64Data = b64Data.replace(/\s/g, '');
var byteCharacters = Base64.decode(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length;offset += sliceSize){
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {
type : contentType
});
}
After this add the base64 util
var Base64 = {}; // Base64 namespace
Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
Base64.encode = function(str, utf8encode) {
utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode;
var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded;
var b64 = Base64.code;
plain = utf8encode ? Utf8.encode(str) : str;
c = plain.length % 3;
if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } }
for (c=0; c<plain.length; c+=3) {
o1 = plain.charCodeAt(c);
o2 = plain.charCodeAt(c+1);
o3 = plain.charCodeAt(c+2);
bits = o1<<16 | o2<<8 | o3;
h1 = bits>>18 & 0x3f;
h2 = bits>>12 & 0x3f;
h3 = bits>>6 & 0x3f;
h4 = bits & 0x3f;
e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
}
coded = e.join('');
coded = coded.slice(0, coded.length-pad.length) + pad;
return coded;
}
Base64.decode = function(str, utf8decode) {
utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode;
var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded;
var b64 = Base64.code;
coded = utf8decode ? Utf8.decode(str) : str;
for (var c=0; c<coded.length; c+=4) {
h1 = b64.indexOf(coded.charAt(c));
h2 = b64.indexOf(coded.charAt(c+1));
h3 = b64.indexOf(coded.charAt(c+2));
h4 = b64.indexOf(coded.charAt(c+3));
bits = h1<<18 | h2<<12 | h3<<6 | h4;
o1 = bits>>>16 & 0xff;
o2 = bits>>>8 & 0xff;
o3 = bits & 0xff;
d[c/4] = String.fromCharCode(o1, o2, o3);
// check for padding
if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2);
if (h3 == 0x40) d[c/4] = String.fromCharCode(o1);
}
plain = d.join(''); // join() is far faster than repeated string concatenation in IE
return utf8decode ? Utf8.decode(plain) : plain;
}
Try this, without external library:
/*In the function "Press" */
var file = oFileUploader.getFocusDomRef().files[0];
if (file && window.FileReader) {
var reader = new FileReader();
var that = this;
reader.onload = function(e) {
var strCSV = e.target.result;
var arrCSV = strCSV.replace(/['",]/g, '').split(/[↵\n]+/).join(';').split(';');
var noOfCols = 11; // 11 Columns
var hdrRow = arrCSV.splice(0, noOfCols);
var oData = [];
while (arrCSV.length > 0) {
var obj = {};
var row = arrCSV.splice(0, noOfCols)
if (row.length > 1) {
for (var i = 0; i < row.length; i++) obj[hdrRow[i].replace(/\r/g, "")] = row[i].trim();
oData.push(obj) // Data Json
}
}
oTable.setModel(new sap.ui.model.json.JSONModel(oData)); // Binding model
};
reader.readAsText(file, 'ISO-8859-1');
}
Regards.
Check the code in my gist
Upload_CSV.js
Place this in the controller to parse the file using JS client side
var fU = this.getView().byId("idfileUploader");
var domRef = fU.getFocusDomRef();
var file = domRef.files[0];
var reader = new FileReader();
var params = "ItemsJson=";
reader.onload = function(oEvent) {
var strCSV = oEvent.target.result;
var arrCSV = strCSV.match(/[\w .]+(?=,?)/g);
var noOfCols = 3;
var headerRow = arrCSV.splice(0, noOfCols);
var data = [];
while (arrCSV.length > 0) {
var obj = {};
var row = arrCSV.splice(0, noOfCols);
for (var i = 0; i < row.length; i++) {
obj[headerRow[i]] = row[i].trim();
}
data.push(obj);
}
data.reverse();
var json = JSON.stringify(data); // send to the backend
if I did not answer you please tell me

Google Spreadsheet - How to avoid sending email duplicates?

I am having an issue with a script. I used the following script from Google Developers Website in order to do a simple merge mail. See https://developers.google.com/apps-script/articles/mail_merge
I modified a bit the script so to prevent email duplicates. However, even if the script seems to work as it marks 'EMAIL_SENT' in each row every time an email is sent. It does not pay attention if the mail as already been marked and still send the mail.
I believe there is an error at line 16 "var emailSent = rowData[6];"
I would really appreciate if someone could help me. Whoever you are thanks in advance.
Here is the modified script :
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheets()[0];
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 7);
var templateSheet = ss.getSheets()[1];
var emailTemplate = templateSheet.getRange("A2").getValue();
var objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var Resume = DriveApp.getFilesByName('Resume.pdf') var Portfolio = DriveApp.getFilesByName('Portfolio.pdf') var rowData = objects[i];
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "Architectural Internship";
var emailSent = rowData[6];
if (emailSent != EMAIL_SENT) {
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {
attachments: [Resume.next(), Portfolio.next()]
});
dataSheet.getRange(2 + i, 7).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
function fillInTemplateFromObject(template, data) {
var email = template;
var templateVars = template.match(/\${\"[^\"]+\"}/g);
for (var i = 0; i < templateVars.length; ++i) {
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
}
return email;
}
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue;
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty. // Arguments: // - cellData: string function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise. function isAlnum(char) { return char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || isDigit(char); }
// Returns true if the character char is a digit, false otherwise. function isDigit(char) { return char >= '0' && char <= '9'; }
Your code is really hard to read and the functions that return 2 or more objects make it even harder...you are using variable names that are also a bit confusing.... but that is probably a personal pov :-)
Anyway, I think I've found the issue: when you write var rowData = objects[i];
This "object" is actually the result of the getRowData function but if you look at this function, you'll see that it returns 2 objects, the first one being itself the result of another function (getObjects) ...
You are checking the value is the 6th element of the array which is actually an object and compare it to a string. The equality will never be true.
I didn't go further in the analyse since I found it really confusing ( as I already said) but at least you have a first element to check .
I would suggest you rewrite this code in a more simple way and use more appropriate variable names to help you while debugging.
I would recommend logging both values before executing to make sure they are the same. I would also guess that the email_sent and EMAIL_SENT are different data types. Can also try forcing the value to string for comparison.
To clarify:
logger.Log(emailSent);
logger.Log(EMAIL_SENT);
if (emailSent.toString() != EMAIL_SENT.toString())
{...
Error is in this line of code -
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
It's considering only 2 columns in the range. Changed 2 to 3 and it worked fine.