Google Script can't call Twitter API when using onEdit() - twitter

I am trying to write a script which will pull data from twitter's API each time I edit a cell. The demo code I currently have looks like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Candidates");
function onEdit(e){
var rng = e.range;
// Check to make sure it is a single cell in specific column that was edited
if(rng.getColumn() == 1 && rng.getNumRows() == 1 && rng.getNumColumns() == 1){
callTwitter(rng.getRow());
}
}
/* --- ---------------------- --- */
/* --- Compare with core list --- */
/* --- ---------------------- --- */
function callTwitter(cRow){
var cRow = 2; // placeholder for testing
var twitterService = getTwitterService();
var response = JSON.parse(twitterService.fetch('https://api.twitter.com/1.1/users/lookup.json?screen_name=' + sheet.getRange(cRow, 1, 1, 1).getValue()));
sheet.getRange(cRow,2,1,1).setValue(response[0].screen_name);
}
What I've checked
I have confirmed that onEdit() gets called when I edit a cell and it does pass through the variable to the callTwitter() function.
Running the callTwitter() function on its own returns the desired result (pulls username from column A, queries twitter API and returns display name in column B).
The issue
The callTwitter() function always fails when I call it via the onEdit call. It stops right before the following line of code.
var response = JSON.parse(twitterService.fetch('https://api.twitter.com/1.1/users/lookup.json?screen_name=' + sheet.getRange(cRow, 1, 1, 1).getValue()));
My current assumption is that the onEdit call is somehow clashing with the API call, but I can't seem to find out why or how to solve it.
--
I have been trying to debug this for hours now and any help or direction on where to look would be very helpful.
Code not included here is the Twitter OAuth which I copied from Google's example https://github.com/googlesamples/apps-script-oauth1

Simple triggers like onEdit won't work in this scenario. According to the documentation,
They cannot access services that require authorization. For example, a simple trigger cannot send an email
because the Gmail service requires authorization, but a simple trigger
can translate a phrase with the Language service, which is anonymous.
You must use an installable trigger.
https://developers.google.com/apps-script/guides/triggers/installable
Create the fuction that you need to execute on edit, go Edit -> Current project's triggers, and select 'From spreadsheet'-> 'On edit.

Related

Make a cell in google sheets automatically 'insert comment'

I don't think I explained it well, column L is where I can log what I have done - with a date and time, so say I send a message, I would log MESSAGE SENT then say I make a call I could log CALLED and any info I wanted to remember - each entry being date and time stamped
In the image you can see the comment field - all i want is that to pop up when I click on the cell and be able to add to the comment field
You can use the onSelectionChange(e) trigger and check if the selected cell(s) fall into your criteria, if so then insert comment.
https://developers.google.com/apps-script/guides/triggers
You could use this sample script to set a comment on an edited cell:
function onEdit(e){
var range = e.range;
range.setNote('Last modified: ' + new Date());
}
However this requires that you edit the content of the cell to trigger the onEdit() function. Which you can modify instead to trigger the function manually (or assign the script to an image to act as a button) like so.
function setComment() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getRange('L14:L16').setNote('Last modified: ' + new Date());
}
Reference:
https://developers.google.com/apps-script/reference/spreadsheet/range#setNote(String)
https://developers.google.com/apps-script/guides/triggers#onedite

How can I automatically add current date when a cell is filled?

SoI'm using Google Sheets and I have an Activecampaign integration that adds a new row when a new user subscribe. I would like to add, with the user info, the current day - so I can know when people got in my list.
I have 4 tabs. The one that I want the day is called "leadData".
I tried this code, but it's not working:
function onChange(e) {
var sheet = e.source.getSheetByName("leadData")
columnToWatch = 1,
columnToStamp = 7, //change all of these to your needs...1 is column A, 2 is column B, etc
excluded = ["General Info", "Campaigns", "Automations"]; //add names of sheets/tabs to this list. The script will not work on these sheets.
if (e.range.columnStart !== columnToWatch || !e.value || excluded.indexOf(sheet.getName()) > -1) return;
sheet.getRange(e.range.rowStart, columnToStamp)
.setValue(new Date()).setNumberFormat("MM/dd HH:mm");
}
How can I solve it?
Answer:
The source and range fields are not part of the event object for onChange triggers. You must specify the Sheet and rows you want directly.
More Information:
As per the documentation on event objects, the Google Sheets onChange() trigger is an installable trigger and does not have the source nor the range fields in the event object that it gets passed.
Code Modifications:
You need to specify the sheet directly. Change the following line
var sheet = e.source.getSheetByName("leadData");
to:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("leadData");
and change
sheet.getRange(e.range.rowStart, columnToStamp)
.setValue(new Date()).setNumberFormat("MM/dd HH:mm");
to
sheet.getRange(sheet.getDataRange().getNumRows(), columnToStamp)
.setValue(new Date()).setNumberFormat("MM/dd HH:mm");
References:
Event Objects | Apps Script | Google Developers
Installable Triggers | Apps Script | Google Developers

spreadsheet onEdit function: make it work for users not logged in

I want to auto-sort a google spreadsheet column automatically after the input is changed. I achieved that with the code below and it works.
Question: Is there a way to achieve that the code works for everyone accesssing the sheet (logged in and not logged in)?
Right now it just works for me when I am logged in. I am wondering about that, as I read that the function "onEdit" is a simple trigger, that does not require the authorisation of the user (https://productforums.google.com/forum/#!topic/docs/BJd8zRYD1qU)
Thanks!
Code:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName("Calculator")
var range = sheet.getRange("I11:X142")
// Sorts by the values in column 9
range.sort({column: 9, ascending: false})
}

Limit the frequency of calls to onEdit

I wrote some scripts using the script editor to automate some google spread-sheet tasks.
I would like to do a few things after the user interacts with my users sheets, .e.g update some background sheets that start with _.
I wrote the following:
function onEdit(e){
var range = e.range;
if (e.source.getActiveSheet().getName()[0]!='_'){
//It is an user edit!
UpdateOtherHiddenTables()
}
};
My problem is that UpdateOtherHiddenTables() takes quite a while, like 2 mins, and it is triggered on any user edit so it is not ideal.
How would you make sure that after a user has interacted with a sheet, that UpdateOtherHiddenTables() is called, but not too often?
I would use Script properties in combination with a timed trigger. The on-Edit trigger only records the fact that the Spreadsheet was edited:
function recordEdit() {
var sp = PropertiesService.getScriptProperties();
sp.setProperty("edited", "yes");
}
This function needs to be run by an installable trigger, simple onEdit won't provide the authorization necessary to modify script properties.
The UpdateOtherHiddenTables function is set to run every 10 minutes, or every hour, or whatever interval you want. It checks whether a refresh is needed.
function UpdateOtherHiddenTables() {
var sp = PropertiesService.getScriptProperties();
if (sp.getProperty("edited") == "yes") {
// update stuff
sp.setProperty("edited", "no");
}
}
By the way: onEdit is only triggered by user edits. A script changing the values in a spreadsheet will not fire that trigger.
I'm using string values instead of Boolean, because Properties stringifies everything. Storing false gets you back the string "false", which is truthy...

Can you write a Google Sheets function that draws something?

Is it possible to write your own custom function in google sheets script that returns a drawn image, similar to how the SPARKLINE function works, except I want to make one that draws a pie chart instead.
I do not want to use Insert > Chart... > Pie Chart because that creates a floating chart on top of the spreadsheet. I would like to be able to write my own function that would return a pie chart that is embedded within the cell that the function is entered in, just like you can do with columns, bars, and line charts using sparkline.
How about following idea? This sample script embeds a chart to a cell using custom function on Spreadsheet. I think that this method is one of various ideas.
Problems :
When you want to create a chart and embed it to a cell using custom functions, you notice that insertChart() cannot be used. There are some limitations for using custom functions. But insertChart() creates floating charts. So in order to embed a chart to a cell, the function =IMAGE() is suitable for this situation. Here, setFormula() for setting =IMAGE() and DriveApp.createFile() for creating images from charts also cannot be used for custom functions.
Solution :
In order to avoid these limitations, I used Web Apps.
To use this sample script, please deploy Web Apps as follows.
On the Script Editor,
File
-> Manage Versions
-> Save New Version
Publish
-> Deploy as Web App
-> At Execute the app as, select "your account"
-> At Who has access to the app, select "Anyone, even anonymous"
-> Click "Deploy"
-> Copy "Current web app URL"
-> Click "OK"
When it deploys Web Apps, the approval required authorization can be done, simultaneously.
Sample Script :
Please copy and paste this script to a bound script of spreadsheet.
var folderId = "### Folder ID ###"; // This is a folder to save images.
var webappsurl = "https://script.google.com/macros/s/######/exec"; // Here, please put "Current web app URL".
function embedChart(range) {
var ac = SpreadsheetApp.getActiveSheet().getActiveCell();
var q1 = "?datarange=" + range;
var q2 = "&row=" + ac.getRow();
var q3 = "&col=" + ac.getColumn();
var url = webappsurl + q1 + q2 + q3;
UrlFetchApp.fetch(url);
}
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var chart = sheet.newChart()
.setChartType(Charts.ChartType.PIE)
.addRange(sheet.getRange(e.parameters.datarange))
.setOption('height', 280)
.setOption('width', 480)
.setOption('title', 'Sample chart')
.build();
var file = DriveApp.getFolderById(folderId).createFile(
chart.getAs('image/png').setName("chart_image.png")
);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
sheet.getRange(e.parameters.row, e.parameters.col).setFormula(
'=IMAGE("' + "http://drive.google.com/uc?id=" + file.getId() + '")'
);
}
Flow of Script :
embedChart()
Input =embedChart("a2:a6") in cell B7.
Using fetch(), sends data of a2:a6 and the inputted coordinate to doGet().
doGet()
Using doGet(), get the data.
Creates a chart using inputted range a2:a6. (in this case, creates a pie chart)
Saves a chart as an image. (in this case, saves as PNG)
Updates a permission of the image file to use for =IMAGE().
Embeds the image using =IMAGE() which was imported by setFormula().
Result :
By inputting =embedChart("a2:a6") in cell B7 as a custom function, following result can be obtained.
Note :
When the custom function embedChart() is used, loading time is about 40 seconds. (I don't know whether this occurs at only my environment.)
Permissions of the created image are ANYONE_WITH_LINK, VIEW.
embedChart() is overwritten by =IMAGE(). So when the spreadsheet is reopened, the response of =IMAGE() is much faster than that of embedChart().
If I misunderstand your question, I'm sorry.

Resources