Script to send a chat message based on an IF condition in Google Sheets - if-statement

I'm trying to edit a script that was intended to send an email if column D is checked (true) but instead of sending an email, I now want it to send a chat. I've created a webhook for the chat group, and it is sort of working, but it's sending a message on EVERY edit made to the sheet.
I'm just a high school teacher way out of my depth here. If anyone can help me figure out how to fix the script, I'd be quite grateful.
When I associate my function with the on edit trigger it sends a chat for every action. If I use the on open trigger, it does nothing.
Screenshot of code
function myFunction() {
var WebWhooklink = "link to my group chat goes here"
var message = { text: "Student has returned"};
var payload = JSON.stringify(message);
var options = {
method: 'POST',
contentType: 'application/json',
payload: payload
};
var response = UrlFetchApp.fetch(WebWhooklink, options ).getContentText();
}
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "Electronic Hall Pass" ) { //checks that we're on Sheet1 or not
var r = s.getActiveCell();
if( r.getColumn() == 1 ) { //checks that the cell being edited is in column A
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === '' ) //checks if the adjacent cell is empty or not?
nextCell.setValue(new Date());
}
}
}
function onEdit2(e) {
if(e.value != "TRUE" ) return;
e.source.getActiveSheet().getRange(e.range.rowStart,e.range.columnStart+2).setValue(new Date());
}

You may try something like this that checks if the edit was made in Column D (the fourth column):
function onEdit(e){
if(e.range.getColumn()==4){
--> Put your function here
}
}

Thanks so much Martin! That fixed it!
function sendMailEdit(e){
if (e.range.columnStart != 4 || e.value != "TRUE") return;
let sName = e.source.getActiveSheet().getRange(e.range.rowStart,1).getValue();
var WebWhooklink = "webhook link to chat goes here"
var message = { text: "Student has returned"};
var payload = JSON.stringify(message);
var options = {
method: 'POST',
contentType: 'application/json',
payload: payload
};
var response = UrlFetchApp.fetch(WebWhooklink, options ).getContentText();
}

Related

Issues with textfinder and IF

I have a piece of code that was scrounged together from another code that is known working. What it does is searches a column using textfinder for a value, in this case, yesterday's date, if nothing is found, it should send an email.
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('FormResponses');
var ss2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('AlertDate');
var search = ss2.getRange("B2").getValue(); //cell has yesterday's date
var lastRow = ss.getLastRow();
var range = ss.getRange(1,4,lastRow); //define range for column D, column D contains dates
//find all occurrences of search key in column D and push range to array
var ranges = range.createTextFinder(search).findAll();
if (ranges!==null) {
var message = '';
}else {
var message = 'Test';
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EmailGroup").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Subject';
var link = "blahblahblah"
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + message + '\n\n**This is an automated message**\n' + link);
}
}
As you see, it should search column D, then, if it finds something, the message variable will be blank, else if it finds nothing, it will send an email to the email addresses chosen. I'm not sure how the results from a textfinder work with this and I think the way it is written is incorrect. Any help is appreciated, unfortunately, I cannot share the document in question as my company doesn't allow sharing outside of the domain. Thank you!
Try this:
You will need to update the sheet names I moved them to end of the line comments
function findAndSendMail() {
var ss=SpreadsheetApp.getActive()
var sh1=ss.getSheetByName('Sheet1');//FormResponses
var sh2=ss.getSheetByName('Sheet2');//AlertDate
var d1=new Date(sh2.getRange("B2").getValue()); //cell has yesterday's date
var d2=new Date(d1.getFullYear(),d1.getMonth(),d1.getDate()).valueOf();
var rg1 = sh1.getRange(1,4,sh1.getLastRow(),1);
var vA=rg1.getValues().map(function(r){
var dt=new Date(r[0]);
var dtv=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();
return dtv;
});
if(vA.indexOf(d2)==-1) {
var message='test';
var emailAddress = ss.getSheetByName("Sheet3").getRange("A1").getValue();//EmailGroup
var subject = 'Subject';
var link = "blahblahblah"
}
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + message + '\n\n**This is an automated message**\n' + link);
//SpreadsheetApp.getUi().alert('I found something');//just for testing
}
}
This version sends an email if yesterday is not found.
In answer to you last question:
function findAndSendMail() {
var ss=SpreadsheetApp.getActive()
var sh1=ss.getSheetByName('Sheet1');//FormResponses
var sh2=ss.getSheetByName('Sheet2');//AlertDate
var d1=new Date(sh2.getRange("B2").getValue()); //cell has yesterday's date
if(isDate(d1)) {
var d2=new Date(d1.getFullYear(),d1.getMonth(),d1.getDate()).valueOf();
var rg1 = sh1.getRange(1,4,sh1.getLastRow(),1);
var vA=rg1.getValues().map(function(r){
var dt=new Date(r[0]);
var dtv=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();
return dtv;
});
if(vA.indexOf(d2)==-1) {
var message='test';
var emailAddress = ss.getSheetByName("Sheet3").getRange("A1").getValue();//EmailGroup
var subject = 'Subject';
var link = "blahblahblah"
}
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + message + '\n\n**This is an automated message**\n' + link);
//SpreadsheetApp.getUi().alert('I found something');//just for testing
}
}else{
SpreadsheetApp.getUi().alert('Data is sheet2!B2 is not a date')
return;
}
}
function isDate(date){
return(Object.prototype.toString.call(date) === '[object Date]');
}

Alexa aws lambda function prompting an error when creating a new object

I am starting to learn how to create a lambda function. I wanted to implement a lambda function that can control a led on an arduino uno device. I found this code on GitHub.
I copied and pasted it and got the following error:
"errorMessage": "Exception: TypeError: Cannot read property 'new' of
undefined".
I searched on StackOverflow and I found this similar question but the answer did not help a lot.
Please I need help in fixing this code. I do not know if it matters but the variable endpoint that contains links for a cloud (sparkfun) is out of service.
Thank you so much for your time and consideration!
Below is the code:
var https = require('https'); //include https
var http = require('http');
exports.handler = (event, context) => {
try {
if (event.session.new) {
// New Session
console.log("NEW SESSION"); //log this for debugging
}
switch (event.request.type) {
case "LaunchRequest":
// Launch Request
console.log(`LAUNCH REQUEST`)
context.succeed(
generateResponse(
buildSpeechletResponse("Welcome to the ForceTronics Home Automation Skill, say turn light on or turn light off", true), //response for Alexa if you just call the skill without intent
{}
)
)
break;
case "IntentRequest":
// Intent Request
console.log(`INTENT REQUEST`)
switch(event.request.intent.name) { //switch statement to select the right intent
case "TurnLightOn": //if you told alexa to turn the light on this will be true
var endpoint = "https://data.sparkfun.com/input/1nlZrX0NbdfwxgrY0zA0?private_key=0mDlvAkdoRIq976eakpa&lightstate=1" //https string to log data to phant phant
https.get(endpoint, function (result) { //use https get request to send data to phant
console.log('Success, with: ' + result.statusCode);
context.succeed(
generateResponse( //if you succeeded allow Alexa to tell you state of light
buildSpeechletResponse("The light is turned on", true),
{}
)
)
}).on('error', function (err) {
console.log('Error, with: ' + err.message);
context.done("Failed");
});
break;
case "TurnLightOff": //the turn light off intent
var endpoint2 = "https://data.sparkfun.com/input/1nlZrX0NbdfwxgrY0zA0?private_key=0mDlvAkdoRIq976eakpa&lightstate=0"; // phant string to set light state to off
https.get(endpoint2, function (result) {
console.log('Success, with: ' + result.statusCode);
context.succeed(
generateResponse( //Alexa response if successful
buildSpeechletResponse("The light is turned off", true),
{}
)
);
}).on('error', function (err) {
console.log('Error, with: ' + err.message);
context.done("Failed");
});
break;
case "IsWasherRunning": //check the state of the washer
var endpoint3 = "http://data.sparkfun.com/output/pw8EWVl18zUgW9OYzJNv.csv?page=1"; // phant csv file page
http.get(endpoint3, function (response) { //http get request, data is returned to response
response.setEncoding('utf8');
response.on('data', function (body) { //now let's go through response data from phant cloud
//following variables are getting hour and date info to ensure data is new and not old
var lHour = Date().substr(16,2); //hour from amazon server
var dHour = body.substr(35,2); //hour from phant cloud data
var lDate = Date().substr(8,2);
var dDate = body.substr(32,2);
if(checkTime(lHour, dHour, lDate, dDate)===1) { //if this is true data from phant is fresh
var s = body.substr(22,1); //read washer state data from phant string
if (s=='1') { //if it is 1 then washer is on
context.succeed(
generateResponse( //if you succeeded allow Alexa to tell you state of light
buildSpeechletResponse("The washer is running", true),
{}
)
);
}
else if (s=='0') { //if it is 0 then washer is off
context.succeed(
generateResponse( //if you succeeded allow Alexa to tell you state of light
buildSpeechletResponse("The washer is off", true),
{}
)
);
}
else { //if this is true then there is an issue getting data from phant
context.succeed(
generateResponse( //if you succeeded allow Alexa to tell you state of light
buildSpeechletResponse("There was an error checking washer state", true),
{}
)
);
}
}
else { //if this is true the data is old and there maybe something wrong with the hardware
console.log("didn't pass date and time test");
context.succeed(
generateResponse( //if you succeeded allow Alexa to tell you state of light
buildSpeechletResponse("No recent washer state data available", true),
{}
)
);
}
});
response.on('error', console.error);
});
break;
default:
throw "Invalid intent";
}
break;
case "SessionEndedRequest":
// Session Ended Request
console.log(`SESSION ENDED REQUEST`);
break;
default:
context.fail(`INVALID REQUEST TYPE: ${event.request.type}`)
}
} catch(error) { context.fail(`Exception: ${error}`) }
}
// builds an Alexa response
buildSpeechletResponse = (outputText, shouldEndSession) => {
return {
outputSpeech: {
type: "PlainText",
text: outputText
},
shouldEndSession: shouldEndSession
}
};
//plays Alexa reponse
generateResponse = (speechletResponse, sessionAttributes) => {
return {
version: "1.0",
sessionAttributes: sessionAttributes,
response: speechletResponse
}
};
//this function checks to see if data from phant cloud is relitively new
function checkTime(lTime, dTime, lDay, dDay) {
//turn hour and date strings into ints
var lT = parseInt(lTime);
var dT = parseInt(dTime);
var lD = parseInt(lDay);
var dD = parseInt(dDay);
if(lD===dD && lT===dT) { //if it is same day and hour then data is good
return 1;
}
else if(lD===dD && (lT - 1)===dT) { //if day is the same and hour is only off by one then data is good
return 1;
}
else if((lD - 1)===dD && lT===0 && dT===23) { //if date is one off but time is around midnight then data is good
return 1;
}
else return 0; //if none of the above if statements are true then data is old
}
It means the object 'event' (look at your line exports.handler) does not have a key called 'session'. Everything after that is non consequential until you fix that. I cannot know what you wanted to accomplish with that call.
Do this:
console.log(event);
You can also look up 'event' in the AWS documentation.
Then you can look for whatever you thought 'session' was and move forward from there. Good luck. Post details of what you thought 'session' was if you get stuck. I don't think you need that though. I can't know for sure from what you posted.

Chat application using CFWebsocket

How can we develop a facebook like chat application using cfwebsocket. There is no description about how to send an user entered message to the server and how to send that message to a particular client from the server.
<script type="text/javascript">
function mymessagehandler(aevent, atoken)
{
console.log(aevent);
console.log(atoken);
var message = aevent.msg;
var txt=document.getElementById("myDiv");
txt.innerHTML = txt.innerHTML + message +"<br>";
}
</script>
<cfwebsocket name="mycfwebsocketobject" onmessage="mymessagehandler" subscribeto="stocks" >
<cfdiv id="myDiv"></cfdiv>
The above code just prints ok in the display. I am not sure how to pass my message inside the stocks object. Can anyone help on this? Thanks in advance
This is the stocks application that I am using
this.wschannels = [ {name="stocks", cfclistener="myChannelListener" }];
This is what I have did to make my chat application work
This is the chat application
<cfwebsocket name="ChatSocket" onOpen="openHandler" onMessage="msgHandler" onError="errHandler">
This is the script
function openHandler(){
//Subscribe to the channel, pass in headers for filtering later
ChatSocket.subscribe('chatChannel',{name: 'TheUserName', UserID: 'TheUserID', AccountID: 'AnUniqueID' });
}
// function to send the message. we can call this when the user clicks on send message
function publish(userID){
var msg = {
AccountID: "AnUniqueID",
publisher: userID,
id: userID,
message: document.getElementById("Name").value + " : " + document.getElementById("message").value
};
//When including headers, the "selector" is where you will filter who it goes to.
var headers = {
AccountID: "AnUniqueID",
publisher: userID,
id: userID
};
// we can save the chat history by an ajax call here
ChatSocket.publish('chatChannel',msg, headers);
}
// this is the receiving function
function msgHandler(message){
// if condition to display the message to the user who are sending and receiving
if(message.data !== undefined && message.data.message !== undefined && (message.data.id == '#session.userID#' || message.data.publisher == '#session.userID#')) {
var data = message.data.message;
console.log(data);
//showing the message
var txt=document.getElementById("myDiv");
txt.innerHTML+= data + "<br>";
}
}
function errHandler(err){
console.log('err');
console.log(err);
}

What's the best way to process array of JSON messages posted to Nodejs server?

A client sends an array of JSON messages to be stored at Nodejs server; but client will require some sort of acknowledgement for each message (through unique id), that it was properly stored at server, and hence doesn't need to be sent again.
At server I want to parse the JSON array, then loop through it, store each message in db, store response for this message in JSON array named responses, and finally send this responses array to the client. But as the db operations are async, all other code is executed before any result returned from db storing methods. My question is how to keep updating the responses array, untill all db operations are complete?
var message = require('./models/message');
var async = require('async');
var VALID_MESSAGE = 200;
var INVALID_MESSAGE = 400;
var SERVER_ERROR = 500;
function processMessage(passedMessage, callback) {
var msg = null;
var err = null;
var responses = [];
isValidMessage(passedMessage, function(err, result) {
if(err) {
callback( createResponse(INVALID_MESSAGE, 0) );
}else{
var keys = Object.keys(result);
for(var i=0, len = keys.length; i<len; i++) {
async.waterfall([
//store valid json message(s)
function storeMessage(callback) {
(function(oneMessage) {
message.processMessage(result[i], function(res) {
callback(res, result[i].mid, callback);
});
})(result[i]);
console.log('callback returns from storeMessage()');
},
//create a json response to send back to client
function createResponse(responseCode, mid, callback) {
var status = "";
var msg = "";
switch(responseCode) {
case VALID_MESSAGE: {
status = "pass";
msg = "Message stored successfuly.";
break;
}
case INVALID_MESSAGE: {
status = "fail";
msg = "Message invalid, please send again with correct data.";
break;
}
case SERVER_ERROR: {
status = "fail";
msg = "Internal Server Error! please contact the administrator.";
break;
}
default: {
responseCode = SERVER_ERROR;
status = "fail";
msg = "Internal Server Error! please contact the administrator.";
break;
}
}
var response = { "mid": mid, "status": status, "message": msg, "code": responseCode};
callback(null, response );
}
],
function(err, result) {
console.log('final callback in series: ', result);
responses.push(result);
});
}//loop ends
}//else ends
console.log('now we can send response back to app as: ', responses);
});//isValid finishes
}
To expand on what lanzz said, this is a pretty common solution (start a number of "tasks" all at the same time, and then use a common callback to determine when they're all done). Here's a quick paste of my function from my userStats function, which gets the number of active users (DAU, WAU, and HAU):
exports.userStats = function(app, callback)
{
var res = {'actives': {}},
day = 1000 * 60 * 60 * 24,
req_model = Request.alloc(app).model,
actives = {'DAU': day, 'MAU': day*31, 'WAU': day*7},
countActives = function(name, time) {
var date = new Date(new Date().getTime() - time);
req_model.distinct('username',{'date': {$gte: date}}, function(e,c){
res.actives[name] = parseInt(c ? c.length : 0, 10);
if(Object.keys(actives).length <= Object.keys(res.actives).length)
callback(null, res);
});
};
var keys = Object.keys(actives);
for(var k in keys)
{
countActives(keys[k], actives[keys[k]]);
}
};
Only send your responses array when the number of items in it equals the number of keys in your result object (i.e. you've gathered responses for all of them). You can check if you're good to send after you push each response in the array.

jQuery DatePicker calendar not returning correct month

I am soooo close here. I'm hoping a guru can help me with this last piece. I'm populating a jQuery DatePicker calendar with XML from an RSS feed. Upon clicking a highlighted date where there's an event, I'm creating a URL with a query string so I can display all the event for the clicked day. Everything is working... until I change the month by clicking previous or next month. My script will return the correct day, but is only returning the current month. For example, I navigate to May and click the 5th, my URL will be events.htm?month=june&day=5. Any ideas on why it will not return my selected month? Here's my code:
var data = $.ajax({
url: "news.xml",
type: "GET",
dataType: "xml",
async:false,
success: function(xml){
return xml;
}
} ).responseText;
$(document).ready(function() {
var events = getSelectedDates();
$("div.datepicker").datepicker({
beforeShowDay: function(date) {
var result = [true, '', null];
var matching = $.grep(events, function(event) {
return event.published.getDate() === date.getDate() && event.published.getMonth() === date.getMonth() && event.published.getFullYear() === date.getFullYear();
});
if (matching.length) {
result = [true, 'highlight', null];
}
return result;
},
onSelect: function(dateText) {
var date, selectedDate = new Date(dateText),
i = 0,
event = null;
while (i < events.length && !event) {
date = events[i].published;
if (selectedDate.getFullYear() === date.getFullYear() &&
selectedDate.getMonth() === date.getMonth() &&
selectedDate.getDate() === date.getDate()) {
event = events[i];
}
i++;
}
if (event) {
var eMonNum = event.published.getMonth();
var d = new Date();
var eMonNum = new Array();
eMonNum[0] = "january";
eMonNum[1] = "february";
eMonNum[2] = "march";
eMonNum[3] = "april";
eMonNum[4] = "may";
eMonNum[5] = "june";
eMonNum[6] = "july";
eMonNum[7] = "august";
eMonNum[8] = "september";
eMonNum[9] = "october";
eMonNum[10] = "november";
eMonNum[11] = "december";
var eMon = eMonNum[d.getMonth()];
var eDay = event.published.getDate();
window.location = "events.htm?month="+eMon+"&day="+eDay;
}
}
});
});
function getSelectedDates() {
return $(data).find('entry').map(function() {
return {
title: $('title', this).text(),
published: new Date($('published', this).text())
};
}).get();
}
Found the problem, when you copied this list from a resource, you forgot to replace the variables.
CHANGE (at the end of month list)
var eMon = eMonNum[d.getMonth()];
TO:
var eMon = eMonNum[event.published.getMonth()];
All I did was get rid of the bad variable and reassigned your month variable to the one you used for the day. You can also remove the declaration of the d variable, as you will not need it.
Best of luck!