Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger an email when a cell is written into from another app (IFTTT)

So here's what I've been working on. I'm a basketball coach and have a spreadsheet that pulls in all of my players' tweets from IFTTT.com (it basically takes the RSS feed of a twitter list and when it is updated, it updates the spreadsheet).

I have been working on coding that basically says "if a player tweets an inappropriate word, email me immediately."

I've got the code figured out that if I just type in an inappropriate word, it'll turn the cell red and email me. However, I have not figured out how to get the code to email me after IFTTT automatically updates the spreadsheet with tweets.

Here is my code thus far. Right now I've just got one "trigger" word that is "players" just to try and get the spreadsheet to work. Here's the code:

function onEdit(e) {
    var ss = SpreadsheetApp.getActiveSpreadsheet();//Get the spreadsheet
    var sheet = ss.getActiveSheet()//Get the active sheet
    var cell = ss.getActiveCell().activate();//Get the active cell. 
    var badCell = cell.getA1Notation();//Get the cells A1 notation.
    var badCellContent = cell.getValue();//Get the value of that cell. 


    if (badCellContent.match("players")){
        cell.setBackgroundColor("red")
        MailApp.sendEmail("[email protected]", "Notice of possible inappropriate tweet", "This tweet       says: " + badCellContent + ".");
    }
}

Here is a link to the spreadsheet I'm working with right now: https://docs.google.com/spreadsheets/d/1g5XaIycy69a3T2YcWhcbBy0hYrxSfoEEz8c4-zP63O8/edit#gid=0 Any help or guidance on this is greatly appreciated! Thanks!

like image 499
Tony Adragna Avatar asked Oct 24 '14 18:10

Tony Adragna


1 Answers

I originally wrote this answer for your previous question, so it includes answers to some of your comments from there, but since you're continuing to go asking the community to write this step-by-step , here's the next step.


The issue I'm running into is that if three tweets come into the spreadsheet at the same time, with my code, it's only going to update the most recent cell, not all three. Does that make sense?

Yes, it does make sense.

When an onEdit() trigger function calls Spreadsheet Service functions to get current info from the sheet, it enters a "Race condition". If any changes occur in the sheet after the change that triggered onEdit(), and the time when it gets scheduled, those changes will be visible when it runs. That's what you see when you assume that the change you're processing is in the last row - by the time you're processing it, there may be a new last row.

Good news, though - the attributes of the event object passed to onEdit contain the details of the change. (The parameter e.) See Event objects.

By using e.range and e.value you'll find you have the location and content of the edited cell that kicked the trigger. If additional tweets arrive before the trigger is serviced, your function won't be tricked into processing the last row.

In new sheets, the onEdit() can get triggered for multiple-cell changes, such as cut & paste. However unlikely that it may happen, it's worth covering.

Well, after getting the spreadsheet all setup & actually using the trigger from IFTTT, it doesn't work. :( I'm assuming it's not dubbing it as the active cell whenever it automatically pulls it into the spreadsheet. Any idea on a workaround on that?

Q: When is an edit not an edit? A: When it's made by a script. In that case, it's a change. You can add an installable on Change function to catch those events. Unfortunately, the change event is less verbose than an edit event, so you are forced to read the spreadsheet to figure out what has changed. My habit is to have the change handler simulate an edit by constructing a fake event (just as we'd do for testing), and passing it to the onEdit function.

So give this a try. This script:

  • handles a list of "bad words". (Could just as easily be monitoring for mentions of your product or cause.)
  • has an onEdit() function that uses the event object to evaluate the row(s) that triggered the function call.
  • colors "bad" tweets
  • has a function for testing the onEdit() trigger, based on How can I test a trigger function in GAS?
  • includes playCatchUp(e), an installable trigger function (change and/or time-based) that will evaluate any rows that have not been evaluated before. Script property "Last Processed Row" is used to track that row value. (If you plan to remove rows, you'll need to adjust the property.)
  • Has the sendMail function commented out.

Enjoy!

// Array of bad words. Could be replaced with values from a range in spreadsheet.
var badWords = [
  "array",
  "of",
  "unacceptable",
  "words",
  "separated",
  "by",
  "commas"
];

function onEdit(e) {
  if (!e) throw new Error( "Event object required. Test using test_onEdit()" );

  Logger.log( e.range.getA1Notation() );

  // e.value is only available if a single cell was edited
  if (e.hasOwnProperty("value")) {
    var tweets = [[e.value]];
  }
  else {
    tweets = e.range.getValues();
  }
  var colors = e.range.getBackgrounds();

  for (var i=0; i<tweets.length; i++) {
    var tweet = tweets[i][0];
    for (var j=0; j< badWords.length; j++) {
      var badWord = badWords[j];
      if (tweet.match(badWord)) {
        Logger.log("Notice of possible inappropriate tweet: " + tweet);
        colors[i][0] = "red";
        //MailApp.sendEmail(myEmail, "Notice of possible inappropriate tweet", tweet);
        break;
      }
    }
  }
  e.range.setBackgrounds(colors);
  PropertiesService.getDocumentProperties()
                   .setProperty("Last Processed Row",
                                (e.range.getRowIndex()+tweets.length-1).toString());
}

// Test function, adapted from https://stackoverflow.com/a/16089067/1677912
function test_onEdit() {
  var fakeEvent = {};
  fakeEvent.authMode = ScriptApp.AuthMode.LIMITED;
  fakeEvent.user = "[email protected]";
  fakeEvent.source = SpreadsheetApp.getActiveSpreadsheet();
  fakeEvent.range = fakeEvent.source.getActiveSheet().getDataRange();
  // e.value is only available if a single cell was edited
  if (fakeEvent.range.getNumRows() === 1 && fakeEvent.range.getNumColumns() === 1) {
    fakeEvent.value = fakeEvent.range.getValue();
  }

  onEdit(fakeEvent);
}

// Installable trigger to handle change or timed events
// Something may or may not have changed, but we won't know exactly what
function playCatchUp(e) {
  // Check why we've been called
  if (!e)
    Logger.log("playCatchUp called without Event");
  else {
    // If onChange and the change is an edit - no work to do here
    if (e.hasOwnProperty("changeType") && e.changeType === "EDIT") return;

    // If timed trigger, nothing special to do.
    if (e.hasOwnProperty("year")) {
      var date = new Date(e.year, e.month, e["day-of-month"], e.hour, e.minute, e.second); 
      Logger.log("Timed trigger: " + date.toString() );
    }
  }

  // Find out where to start processing tweets
  // The first time this runs, the property will be null, yielding NaN
  var lastProcRow = parseInt(PropertiesService.getDocumentProperties()
                         .getProperty("Last Processed Row"));
  if (isNaN(lastProcRow)) lastProcRow = 0;

  // Build a fake event to pass to onEdit()
  var fakeEvent = {};
  fakeEvent.source = SpreadsheetApp.getActiveSpreadsheet();
  fakeEvent.range = fakeEvent.source.getActiveSheet().getDataRange();
  var numRows = fakeEvent.range.getLastRow() - lastProcRow;
  if (numRows > 0) {
    fakeEvent.range = fakeEvent.range.offset(lastProcRow, 0, numRows);
    onEdit(fakeEvent);
  }
  else {
    Logger.log("All caught up.");
  }  
}

screenshot

like image 173
Mogsdad Avatar answered Sep 23 '22 05:09

Mogsdad