Exception: Invalid email: [Ljava.lang.Object | sendEmailFromTemplate - google-apps-script

I get this error, even if the email address is valid. I tried to remplace createDraft by sendEmail, but the error still ongoing.
Error message
Erreur
Exception: Invalid email: [Ljava.lang.Object;#7a4edc0
sendEmailFromTemplate # Code.gs:50
function sendEmailFromTemplate() {
var sheet = SpreadsheetApp.getActiveSheet();
var selectedRow = sheet.getActiveCell().getRow();
const Nom = sheet.getRange("D" + selectedRow).getValue();
const Matricule = sheet.getRange("E" + selectedRow).getValue();
const Recepient = sheet.getRange("K" + selectedRow).getValue();
const Fonction = sheet.getRange("G" + selectedRow).getValue();
const date = new Date(sheet.getRange("C" + selectedRow).getValue());
const FormattedDATE = Utilities.formatDate(date, "GMT", "EEEEE, dd MMMM yyyy");
const FormattedDATEFR = LanguageApp.translate(FormattedDATE, 'en', 'fr');
const Adresse = sheet.getRange("J" + selectedRow).getValue();
const UADM = sheet.getRange("F" + selectedRow).getValue();
const Type = sheet.getRange("B" + selectedRow).getValue();
const Check = sheet.getRange("A" + selectedRow).getValue();
if (Check !== true) {
return Logger.log("Not sending email as check is not true in column A");
}
const TemplateTexte = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Modele_Bienvenue").getRange(4, 1).getValue();
const fileId = "1NHqn0-1gG1fmNxfvHuYQIIbtf4aOtZQB"; // Added: Please set the file ID of the logo image.
const blob = DriveApp.getFileById(fileId).getBlob(); // Added
const CorpsMessage = TemplateTexte.
replace("{Nom}",Nom).
replace("{Matricule}",Matricule).
replace("{Adresse}",Adresse).
replace("{UADM}",UADM).
replace("{Date}", FormattedDATEFR.
replace("{Fonction}",Fonction));
const Objet = "Confirmation date d'entrée en fonction - " + Fonction + " - " + Nom;
const html = CorpsMessage.
replace(/\n/g, '<br>').
replace('{LOGO}', '<img src="cid:logo">');
if (validateEmail(Recepient)) {
var cc = [];
if (Type === "Voirie") {
cc.push("jan.bas#real.com");
} else if (Type === "Parcs") {
cc.push("mou.bou#real.com");
}
// Get all the email addresses associated with the account
const aliases = GmailApp.getAliases();
// Check if the desired alias exists in the list of aliases
if (aliases.indexOf("rh#real.com") != -1) {
const EmailDraft = GmailApp.createDraft(Recepient, Objet, CorpsMessage, {
from: "rh#real.com",
cc: cc,
htmlBody: html,
inlineImages: {logo: blob}});
} else {
// Handle the error if the alias does not exist
Logger.log("The desired alias does not exist");
}
sheet.getRange("M" + selectedRow).setValue(new Date());
sheet.getRange("A" + selectedRow).setValue(false);
} else {
Logger.log("Invalid email address: " + Recepient);
}
}
function validateEmail(Recepient) {
const re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(Recepient).toLowerCase());
}

When I saw your script, it seems that the value of cc is an array. The value of cc is required to be a comma-separated list of email addresses to CC. I thought that this might be the reason of your current issue of About Exception: Invalid email: [Ljava.lang.Object;#7a4edc0`. In this case, how about the following modification?
From:
cc: cc,
To:
cc: cc.join(","),
Reference:
sendEmail(recipient, subject, body, options)

Related

TypeError: Cannot read property 'getResponseCode' of undefined doGet_Appraisal throws error but before it was executed properly how to resolve

It was actually a Google Sheets project, so I'm learning on the fly as best as I can, but I can't even format this code below correctly - ugh!)
The error I am receiving is this:
TypeError: Cannot read property 'getResponseCode' of undefined
doGet_Appraisal # Appraisal_Email.gs:64
And the code is below:
//1. Performing to generate the Payslip getting sheet contents
function doGet_Appraisal(range,shTabName) {
var blob,exportUrl,name,options,response,sheetTabId,ss,ssID,url_base,email,subject;
var folderId ="1o9WImzeKdzsek7DfZWD_qzFsI9yLoplz";
var milliseconds = 2000;
range = range ? range : "B1:R44";
ss = SpreadsheetApp.openById("sheet_ID");
shTabName = "Appraisal Payslip Template";
ssID = ss.getId();
sh = ss.getSheetByName(shTabName);
const body = "Dear Employee" + "\n\n" + "<p>" + "<b> Payslip for the month of January is Available </b>" + "</p>";
var htmlText = body.replace(/\n/g,'\n<br>');
var bioCode = sh.getRange('F12');
const values = [...new Set(bioCode.getDataValidation().getCriteriaValues()[0].getValues().flat())];
var first = values[0];
var number = values.length - 1;
bioCode.setValue(first);
//2. Looping the Emp_ID till null
for(i = 0;i < number;i++)
{
if (sh.getRange('F12').getValue().length > 0) {
var person = sh.getRange('F12').getValue();
sheetTabId = sh.getSheetId();
//3. Getting the Corresponfing EMail based on Emp_ID
email = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Appraisal Payslip Template').getRange('N12').getValue().toString();
Utilities.sleep(milliseconds);
url_base = ss.getUrl().replace(/edit$/,'');
name = sh.getRange("S1").getValue();
name = name + ""
subject = `${name}`;
Logger.log('email: ' + email)
Logger.log('body: ' + body)
//4. Exporting the sheet as PDF (contents)
exportUrl = url_base + 'export?exportFormat=pdf&format=pdf' +
'&gid=' + sheetTabId + '&id=' + ssID +
'&range=' + range +
'&size=A4' +
'&portrait=true' +
'&fitw=true' +
'&sheetnames=false&printtitle=false&pagenumbers=true' +
'&gridlines=false' +
'&fzr=false';
//5. Authorizing email setup OAuthToken
options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
//6. Getting the sheet url and export as PDF
options.muteHttpExceptions = true;
response = UrlFetchApp.fetch(exportUrl, options);
//var response2 = UrlFetchApp.fetch(exportUrl, options).getBlob();
Logger.log(response.getResponseCode())
const nextValue = values[values.indexOf(bioCode.getValue()) + 1] || values[0];
bioCode.setValue(nextValue);
}
else {const nextValue = values[values.indexOf(bioCode.getValue()) + 1] ||
values[0];
bioCode.setValue(nextValue);
}
//7. If response code is not 200 return error exporting PDF
if (response.getResponseCode() !== 200) {
console.log("Error exporting Sheet to PDF! Response Code: " + response.getResponseCode());
return;
}
blob = response.getBlob();
blob.setName(name + '.pdf')
GmailApp.sendEmail(email, subject, htmlText,
{
htmlBody: htmlText,
attachments: [{
fileName: `${name}` + ".pdf",
//content: response2.getBytes(),
mimeType: "application/pdf"
}]
});
//pdfFile = DriveApp.getFolderById(folderId).createFile(blob);
}
}
This was worked recently but now throws error
TypeError: Cannot read property 'getResponseCode' of undefined
doGet_Appraisal # Appraisal_Email.gs:64

How to generate PDF from a Google Sheet and send copies to specific email accounts when a specific cell in the sheet is edited?

I am trying to make an invoice for a project. The way it works is a person would fill in the required details and as soon as the details are filled. They would edit a specific cell (could be a drop-down like "invoice filled" and "invoice incomplete"). As soon as they click on filled, it would send the sheet as a PDF file to different emails.
I'm having a hard time going around this through apps scripts.
Here is a sample
function createAndSendPDF() {
const docID = '___________'; // ID of spreadsheet
const feuilleID = '0'; // sheetID
const email = 'emailAddress#email.com';
// const dossier = DriveApp.getFolderById('____________'); // ID of Folder if you want to save a copy of the pdf file
const d = Utilities.formatDate(new Date(), "GMT+1", "yyyyMMdd")
const fichier = 'myFileName' + "_" + d + ".pdf"
const objet = 'Test pdf';
const corps = "Please find ...";
const url = 'https://docs.google.com/spreadsheets/d/' + docID + '/export?';
const exportOptions =
'exportFormat=pdf&format=pdf' +
'&size=A4' +
'&portrait=true' +
'&fitw=true' +
'&sheetnames=false&printtitle=false' +
'&pagenumbers=false&gridlines=false' +
'&fzr=false' +
'&gid=' + feuilleID;
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
var reponse = UrlFetchApp.fetch(url + exportOptions, params).getBlob();
GmailApp.sendEmail(email, objet, corps, {
htmlBody: corps,
attachments: [{
fileName: fichier,
content: reponse.getBytes(),
mimeType: "application/pdf"
}]
});
// dossier.createFile(reponse.setName(fichier));
}
You simply need to add a trigger of type onEdit(e) and control when the checkbox value is set to true.
Then you basically need to send an email with the attachment:
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getSheetByName('Proforma Sheet')
const PDF_MIME = "application/pdf"
const listEmail = ["test1#gmail.com", "test2#gmail.com"]
const sendPDF = (e) => {
const { range: { rowStart, columnStart } } = e
const {value} = e
if (rowStart === 11 && columnStart === 12 && value === "TRUE" ) {
const copy = DriveApp.getFileById(ss.getId()).getAs(PDF_MIME).copyBlob()
listEmail.forEach(email=>{
MailApp.sendEmail(email, "Your Spreadsheet", "See attched", {
attachments: [copy]
})
})
}
}
And create an installable edit trigger for the sendPDF function.

Trying to set a column as email sent so I dont keep resending emails again and again

I have a request form, need to send email to the requesters once done, only thing is it keeps resending to the entire list. I want to restrict and send only to those who haven't been emailed before so need to add "Email Sent" in a new column. But its not working. Can someone help
function sendEmails() {
const sheet = SpreadsheetApp.getActiveSheet();
const dataRange = sheet.getRange("A2:E6");
const data = dataRange.getValues();
data.forEach(function (rowData) {
const recipient = rowData[0];
const emailAddress = rowData[2];
const status = rowData[3];
const done = rowData[4];
if (status !== 'approved') {
return
}
const greeting = 'Dear ' + recipient + ',\n'
const statusMessage = 'Your request is ' + status ;
const greatJobMessage = 'Thanks for playing! :)';
const message = [greeting, statusMessage, greatJobMessage].join('\n');
const subject = 'Request '+ status;
MailApp.sendEmail(emailAddress, subject, message);
done.setValue('sent')
})
}
In your script, how about the following modification?
Modified script:
function sendEmails() {
const sheet = SpreadsheetApp.getActiveSheet();
const dataRange = sheet.getRange("A2:E6");
const data = dataRange.getValues();
const rangeList = data.reduce(function (ar, [recipient, , emailAddress, status, done], i) {
if (status == 'approved' && done != 'sent') {
const greeting = 'Dear ' + recipient + ',\n'
const statusMessage = 'Your request is ' + status;
const greatJobMessage = 'Thanks for playing! :)';
const message = [greeting, statusMessage, greatJobMessage].join('\n');
const subject = 'Request ' + status;
MailApp.sendEmail(emailAddress, subject, message);
ar.push("E" + (i + 2));
}
return ar;
}, []);
if (rangeList.length > 0) {
sheet.getRangeList(rangeList).setValue('sent');
}
}
In this modification, when the values of columns "D" and "E" are approved and not sent, an email is sent. And, when the email was sent, sent is put to the column "E". For this, I used the rangeList. By this, a value can be put to the split cells.
References:
getRangeList(a1Notations)
setValue(value) of Class RangeList
reduce()
Added:
From your following replying,
Works for this sheet but I need to set columns for email, Status etc at will because my form is long then 26 columns and will keep changing
In this case, how about the following sample script?
Sample script:
function sendEmails() {
// Please modify the colum numbers of the following variables for your actual situation.
const recipientCol = 1; // This is the column A
const emailAddressCol = 3; // This is the column C
const statusCol = 4; // This is the column D
const doneCol = 5; // This is the column E
const sheet = SpreadsheetApp.getActiveSheet();
const dataRange = sheet.getRange("A2:E6");
const data = dataRange.getValues();
const rangeList = data.reduce(function (ar, r, i) {
const recipient = r[recipientCol - 1];
const emailAddress = r[emailAddressCol - 1];
const status = r[statusCol - 1];
const done = r[doneCol - 1];
if (status == 'approved' && done != 'sent') {
const greeting = 'Dear ' + recipient + ',\n'
const statusMessage = 'Your request is ' + status;
const greatJobMessage = 'Thanks for playing! :)';
const message = [greeting, statusMessage, greatJobMessage].join('\n');
const subject = 'Request ' + status;
MailApp.sendEmail(emailAddress, subject, message);
ar.push("E" + (i + 2));
}
return ar;
}, []);
if (rangeList.length > 0) {
sheet.getRangeList(rangeList).setValue('sent');
}
}

Google Sheets, how to send email with attachment to every customer

I have a sheet, with some tabs, in one tab i make statement for my clients, there i select a customer in B2 and using a query formula i get the desired data, after that i have a script to first hide blank rows and then send that email with the pdf as attachment, and i do thins only when need send that email.
this is my script:
function HideEdoCta() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Edo Cta');
var lastRow = sheet.getLastRow();
var range = sheet.getRange(1, 4, lastRow, 1);
var data = range.getValues();
//Rows
for (var i = 0; i < data.length; i++) {
if (data[i][0] == 1) {
sheet.hideRows(i + 1)
}
}
sheet.hideColumns(4, 1)
}
function UnHideEdoCta() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Edo Cta');
sheet.unhideRow(sheet.getDataRange());
}
function MailEdoCta(email, subject, body, sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Edo Cta');
var valueToCheck = sheet.getRange('C1').getValue();
if (valueToCheck == true && casa != '') {
HideEdoCta()
SpreadsheetApp.flush()
var email = sheet.getRange('D1').getValue(); // Enter the required email address here
var casa = sheet.getRange('B2').getValue();
var subject = 'Estado de Cuenta Sky';
var body =
"Hola, <strong>" + casa + "</strong><br><br>" +
"xxx.<br>" +
"-- <br>" +
"<strong>xxx</strong><br>"
;
// Base URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&scale=1' // 1= Normal 100% / 2= Fit to width / 3= Fit to height / 4= Fit to Page
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=true&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
}).getBlob().setName(sheet.getName() + ".pdf");
// Uncomment the line below to save the PDF to the root of your drive.
// var newFile = DriveApp.createFile(response).setName(sheet.getName() + ".pdf")
GmailApp.sendEmail(email, subject, "",
{ name: 'xxx', htmlBody: body, attachments: [response] }
);
SpreadsheetApp.flush()
UnHideEdoCta()
sheet.getRange('C1').setValue(false)
sheet.getRange('B2').clearContent()
}
else {
sheet.getRange('C1').setValue(false)
}
}
in this script i have a validation that if there is a customer selected in B2 and C1 is true, send the email, because the trigger is set to run every minute.
now what i need is send the same email, every 5th of the month to all my customers automatically (i only have 24 customers)
how can i make this possible without selecting one by one ?
any help please ?
here is a sample sheet with sample data
Try this
function mailing() {
var doc = SpreadsheetApp.getActive();
var sh = SpreadsheetApp.getActiveSheet();
var data = doc.getRange('listOfCustomers').getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][1]) {
sh.getRange('B2').setValue(data[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(1000);
}
}
};
a copy of your spreadsheet would have been appreciated for testing.
In response to your comment from above:
This is the basic script that reads the information spreadsheet and calls the email function along with an object that identifies the template and other important information.
function sendEmails() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Emails');
const [hA, ...dt] = sh.getDataRange().getValues();
const vs = dt.filter(r => r[0] && r[1] && r[2] && r[3] && r[4] == 'Send' && r[5]);
const idx = {};
hA.forEach((h, i) => { idx[h] = i; });
vs.forEach(function (r) {
let obj = { index: idx, row: r }
this[r[5]](obj);//This executes the function named in r[5] and passes it obj
});
}
This is one of the functions that sends emails or creates a draft:
function sendEmails103(obj) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('libImages');
const [hA, ...dt] = sh.getDataRange().getValues();
let idx = {};
hA.forEach((h, i) => idx[h] = i);
let imgObj = {};
vs = dt.filter(r => r[idx['filename']] == obj.row[obj.index['htmlFile']])
vs.forEach((r, i) => {
let params = { muteHttpExceptions: true, headers: { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
let aurl = "https://photoslibrary.googleapis.com/v1/mediaItems/" + r[idx['mediaItemId']]
let resp = JSON.parse(UrlFetchApp.fetch(aurl, params).getContentText());
let burl = `${resp.baseUrl}=w${r[idx['maxwidth']]}-h${r[idx['maxheight']]}`
imgObj[r[idx['Key']]] = UrlFetchApp.fetch(burl).getBlob();
});
let htmlTemplate = HtmlService.createTemplateFromFile(obj.row[obj.index['htmlFile']]);
let html = htmlTemplate.evaluate().getContent();
if (html) {
if (obj.row[obj.index['operation']] == 'Create Draft') {
GmailApp.createDraft(obj.row[obj.index['Recipients']], obj.row[obj.index['Subject']], '', { htmlBody: html, inlineImages: imgObj, replyTo: obj.row[obj.index['replyTo']] });
} else {
GmailApp.sendEmail(obj.row[obj.index['Recipients']],obj.row[obj.index['Subject']],'',{htmlBody:html,inlineImages:imgObj,replyTo: obj.row[obj.index['replyTo']]});
}
}
}
This probably not for everyone, but I like having more room to customize my letters and having been a web developer for a long time I'm quite comfortable composing letters in html which is probably not true of everyone.

How to wait for new input from user in Telegram Bot?

I'm building a bot for Telegram to be able to users add to a spreadsheet "friends".
The idea is that when they start the bot they can simply write the name of the "friends" (e.g., #friend1 #friend2 #friend3), and they will be added to a new spreadsheet.
I'm writing in the Google Spreadsheet Script Editor.
Started to declare the variables, and set up the webhook.
var token = "token";
var url = "https://api.telegram.org/bot" + token;
var webAppUrl = "spreadhsheetURL"
var ssId = "spreadsheeID";
var myID = "$$$$$$$"
function getMe() {
var response = UrlFetchApp.fetch(url + "/getMe");
Logger.log(response.getContentText());
}
function getUpdates() {
var response = UrlFetchApp.fetch(url + "/getUpdates");
Logger.log(response.getContentText());
}
function setWebhook() {
var response = UrlFetchApp.fetch(url + "/setWebhook?url="+ webAppUrl);
Logger.log(response.getContentText());
}
function sendText(id, text) {
var response = UrlFetchApp.fetch(url + "/sendMessage?chat_id=" + id + "&text=" + encodeURIComponent(text));
Logger.log(response.getContentText());
}
function doGet(e){
return HtmlService.createHtmlOutput("Hello " + JSON.stringify(e));
}
All is functioning but when I tell BOT to ask the user if the wants to add new friends the program doesn't wait for the user to write and gives the "Error" of the last else.
function doPost(e){
try{
var contents = JSON.parse(e.postData.contents);
var text = contents.message.text;
var id = contents.message.from.id;
var name = contents.message.from.username;
sendText(id, "Hi " + name + " do you want to add more friends? (Yes/No)");
if(text == "Yes"){
sendText(id, "Add new friends (e.g., #friend1 #friend2 #friend3)).");
var sheetName = name;
var newText = text.split(" ").slice(1).join(" ");
//check if already exist the spreadshet
var sheet = ss.getSheetByName(sheetName) ? ss.getSheetByName(sheetName) : ss.insertSheet(sheetName);
// check the number of words (friends)
var str_len = newText.split(" ").length;
// Print friends per row
for (i=0; i<str_len; i++){
sheet.appendRow([new Date(), id, name, newText.split(" ")[i]]);
}
sendText(id,"The user " + newText + " was added to your list " + sheetName + " with success.");
}else{
if(text == "No"){
sendText(id, "Thank you.")
}else{
sendText(id, "Error.")
}
}
var ss = SpreadsheetApp.openById(ssId);
}catch(e){
sendText(myID, JSON.stringify(e,null,4));
}
}
Want to know if anyone could help me.
Thanks
The problem is that you are doing one delivery for two processes at once, and each process needs to be responded to at its own time, so you have to separate each response from each process.
You can use the reply_markup parameter with ForceReply for data input needs, which is available in almost all send methods.
A little improvement, modified version to solve the problem:
function doPost(e){
try {
var contents = JSON.parse(e.postData.contents);
var text = contents.message.text;
var id = contents.message.from.id;
var name = contents.message.from.username;
var answer = ["Yes","No"];
if ( text === "/start" ) {
sendText(id, "Hi " + name + " do you want to add more friends? (Yes/No)");
} else if ( answer.includes( text ) ) {
if ( text === "Yes" ) {
//force user to input
var dataForceReply = {
method: "post",
payload: {
method: "sendMessage",
chat_id: String( contents.message.chat.id ),
text: "Add new friends (e.g., #friend1 #friend2 #friend3))",
reply_markup: JSON.stringify({
"force_reply": true
})
}
};
UrlFetchApp.fetch( url + "/", dataForceReply );
} else sendText(id, "Thank you.");
//When the user provides feedback input
} else if ( (( contents || {} ).message || {}).reply_to_message ) {
//Make sure the user's answer is for the same question
if ( contents.message.reply_to_message.chat.id == contents.message.chat.id ) {
var ss = SpreadsheetApp.openById(ssId);
var sheetName = name;
var newText = text.split(" ").slice(1).join(" ");
//check if already exist the spreadshet
var sheet = ss.getSheetByName(sheetName) ? ss.getSheetByName(sheetName) : ss.insertSheet(sheetName);
//check the number of words (friends)
var str_len = newText.split(" ").length;
// Print friends per row
for (i=0; i<str_len; i++){
sheet.appendRow([new Date(), id, name, newText.split(" ")[i]]);
}
sendText(id,"The user " + newText + " was added to your list " + sheetName + " with success.");
} else sendText(id, "Error.");
}
} catch(e) {
sendText( myID, e );
}
}