How to get the data without breaking the call in the Facebook Graph API? - facebook

What I need to do to circumvent this issue, because when I request data for 2 months I already receive this error, when there is a break per day, I have the following call. With Little data works perfect, but when I increase the period the server brings me
User request limit reached","type":"OAuthException","is_transient":true,"code":17,"error_subcode":2446079,"fbtrace_id":"...
function solicitacaoAssicrona(){
var service = getService()
var batch = [{"method": "GET", "relative_url":"v3.2/act_1125789557444919/insights/impressions,reach,frequency,spend,campaign_name,account_name,clicks,cost_per_10_sec_video_view,cpm,cpp?level=campaign&since=2016-03-03&until=2019-03-04&time_increment=1&limit=100"}]
// var batchUrl = encodeURIComponent(JSON.stringify(batch));
// Logger.log(batchUrl);
var url = "https://graph.facebook.com?include_headers=false&batch=" + encodeURIComponent(JSON.stringify(batch))
var response = UrlFetchApp.fetch(url, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
Logger.log(result)
// response.forEach(function(resp){
// var resp = JSON.parse(resp.body);
// //Logger.log(JSON.stringify(resp, null, 2));
//
//// resp.data[0].values.forEach(function(response){
////
////
//// })
////
// })
}
I'Ve looked at the documentation, but to the moment without success!
https://developers.facebook.com/docs/marketing-api/insights/best-practices/
That's the call I have
var metricas = [
'impressions',
'reach',
'unique_clicks',
'account_currency',
'account_id',
'account_name',
'ad_id',
'ad_name',
'adset_id',
'adset_name',
'buying_type',
'campaign_id',
'campaign_name',
'clicks',
'cost_per_inline_link_click',
'cost_per_inline_post_engagement',
'cost_per_unique_click',
'cost_per_unique_inline_link_click',
'cpc',
'cpm',
'cpp',
'ctr',
'date_start',
//'date_stop',
'frequency',
'inline_link_click_ctr',
'inline_link_clicks',
'inline_post_engagement',
'objective',
'relevance_score',
'social_spend',
'spend',
'unique_ctr',
'unique_inline_link_click_ctr',
'unique_inline_link_clicks',
'unique_link_clicks_ctr',
//'website_ctr',
'video_10_sec_watched_actions',
'cost_per_10_sec_video_view',
'video_30_sec_watched_actions',
'video_avg_percent_watched_actions',
'video_avg_time_watched_actions',
'video_p100_watched_actions',
'video_p25_watched_actions',
'video_p50_watched_actions',
'video_p75_watched_actions',
'video_play_actions',
'video_thruplay_watched_actions',
'video_p95_watched_actions',
]
var parameters = metricas.join(',');
var url = 'https://graph.facebook.com/v3.2/act_xxxxxxxxxx/insights?fields= + parameters + '&level=ad&time_range[since]=2019-02-05&time_range[until]=2019-04-05&time_increment=1&limit=200'

It's to do with how much data you can retrieve with batch requests. For longer periods, you should divide it into smaller chunks, sequential to each other, thus retrieving the data needed in multiple requests. Have a look at this example:
Code.gs
From line 88 of the file, you can see how it can be divided in multiple requests.
https://github.com/halsandr/Facebook_Connector/blob/master/Code.gs
function dateDelta(dObj, num) {
if (isNaN(num)) {
var dateStart = new Date(dObj);
} else {
var dateStart = new Date(dObj);
var dateStart = new Date(dateStart.setDate(dateStart.getDate() + num));
}
var dd = dateStart.getDate();
var mm = dateStart.getMonth()+1; //January is 0!
var yyyy = dateStart.getFullYear();
if(dd<10){
dd='0'+dd;
}
if(mm<10){
mm='0'+mm;
}
var dateStart = yyyy + "-" + mm + "-" + dd;
return dateStart;
}
var gStartDate = new Date(request.dateRange.startDate);
var gStartDate = new Date(dateDelta(gStartDate, -1));
var gEndDate = new Date(request.dateRange.endDate);
var gEndDate = new Date(dateDelta(gEndDate, +1));
var gRange = Math.ceil(Math.abs(gEndDate - gStartDate) / (1000 * 3600 * 24));
var gBatches = Math.ceil(gRange / 92);
if (gBatches < 2) {
var batch = [{"method": "GET", "relative_url": request.configParams.pageID + "/insights/page_fans,page_impressions,page_post_engagements?since=" + dateDelta(gStartDate) + "&until=" + dateDelta(gEndDate)}];
//console.log(batch);
} else {
batch = [];
var iterRanges = gRange / gBatches;
for (i = 0; i < gBatches; i++) {
var iterStart = dateDelta(gStartDate, (iterRanges * i));
if (i == (gBatches - 1)) {
var iterEnd = dateDelta(gEndDate);
} else {
var iterEnd = dateDelta(gStartDate, (iterRanges * (i + 1)) + 1);
}
batch.push({"method": "GET", "relative_url": request.configParams.pageID + "/insights/page_fans,page_impressions,page_post_engagements?since=" + iterStart + "&until=" + iterEnd})
}
//console.log(batch);
}
// Fetch the data with UrlFetchApp
var url = "https://graph.facebook.com?include_headers=false&batch=" + encodeURIComponent(JSON.stringify(batch))

Related

TypeError: Cannot read property 'getChild' of null - Apps Script

I am a newbie and am trying to use a script to send our school website's feeds
to our Google Chat (Google Workspace for Edu).
I found a code here that works like a charm with the testing Url (https://cloudblog.withgoogle.com/products/gcp/rss/),
but returns me an error when I point to our school's website.
TypeError: Cannot read property 'getChild' of null
Here is the code and below the Debug error
// URL of the RSS feed to parse
var RSS_FEED_URL = "https://www.icriccardomassa.edu.it/agid/feed/";
// https://cloudblog.withgoogle.com/products/gcp/rss/"; <- this works!
// Webhook URL of the Hangouts Chat room
var WEBHOOK_URL = "https://chat.googleapis.com/v1/spaces/AAAAueQ0Yzk/messages?key=AI [..]";
// When DEBUG is set to true, the topic is not actually posted to the room
var DEBUG = false;
function fetchNews() {
var lastUpdate = new Date(PropertiesService.getScriptProperties().getProperty("lastUpdate"));
var lastUpdate = new Date(parseFloat(PropertiesService.getScriptProperties().getProperty("lastUpdate")) || 0);
Logger.log("Last update: " + lastUpdate);
Logger.log("Fetching '" + RSS_FEED_URL + "'...");
var xml = UrlFetchApp.fetch(RSS_FEED_URL).getContentText();
var document = XmlService.parse(xml);
// var items = document.getRootElement().getChild('channel').getChildren('item').reverse();
var items = document.getRootElement().getChild('channel').getChildren('item').reverse();
Logger.log(items.length + " entrie(s) found");
var count = 0;
for (var i = 0; i < items.length; i++) {
var pubDate = new Date(items[i].getChild('pubDate').getText());
var og = items[i].getChild('og');
var title = og.getChild("title").getText();
var description = og.getChild("description").getText();
var link = og.getChild("url").getText();
if(DEBUG){
Logger.log("------ " + (i+1) + "/" + items.length + " ------");
Logger.log(pubDate);
Logger.log(title);
Logger.log(link);
// Logger.log(description);
Logger.log("--------------------");
}
if(pubDate.getTime() > lastUpdate.getTime()) {
Logger.log("Posting topic '"+ title +"'...");
if(!DEBUG){
postTopic_(title, description, link);
}
PropertiesService.getScriptProperties().setProperty("lastUpdate", pubDate.getTime());
count++;
}
}
Logger.log("> " + count + " new(s) posted");
}
function postTopic_(title, description, link) {
var text = "*" + title + "*" + "\n";
if (description){
text += description + "\n";
}
text += link;
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify({
"text": text
})
};
UrlFetchApp.fetch(WEBHOOK_URL, options);
}
Thank you in advance for your help!
Debugger errors

Google Form to Slack threaded message

In Google Form, on submit, I want to send a new message as one of the fields from the form and want to include the rest of the form fields as threaded messages in to the parent message.
I manually captured one of my thread_ts and successfully sent threaded messages. But I do not know what is the [best] way to get thread_ts id as I submit the form.
var channel = 'xxxx';
function onSubmit(e) {
var response = e.response.getItemResponses();
var Parent = response[0].getResponse();
var Thread = response[1].getResponse();
var Thread2 = response[2].getResponse();
var message = '#Test - ' + Parent;
var payload = {
"payload": '{"text": "' + message + '"}'
//"payload": '{"channel": "xxx","thread_ts": "1571950486.000500","text": "' + Thread + '--' + Thread2 + '"}'
}
var options = {
"method": "post",
"payload": payload
};
UrlFetchApp.fetch(webhookUrl, options);
};
I do not get any errors.
Here is how I was able to do it:
var webhookUrl = 'https://hooks.slack.com/services/***/***/***';
function SendtoSlack(e) {
var formResponse = e.response;
var itemResponses = formResponse.getItemResponses();
for (var i = 0; i < itemResponses.length; i++) {
switch (itemResponses[i].getItem().getTitle()) {
case "Client":
var Client = itemResponses[i].getResponse() || '';
break;
case "Day":
var date = itemResponses[i].getResponse() || '';
break;
case "Time":
var time = itemResponses[i].getResponse() || '';
break;
case "Device":
var Device = itemResponses[i].getResponse() || '';
break;
case "Browser":
var Browser = itemResponses[i].getResponse() || '';
break;
case "Geo":
var GEO = itemResponses[i].getResponse() || '';
break;
}
}
var redirectAlert = '#***'+' - ' + Client;
var payload = {
"payload": '{"text": "' + redirectAlert + '"}',
}
var options = {
"method": "post",
"payload": payload
};
UrlFetchApp.fetch(webhookUrl, options);
var webhookUrl1 = 'https://slack.com/api/conversations.history?token=xoxp-***-***-***-***&channel=***&pretty=1';
var slackJSON = UrlFetchApp.fetch(webhookUrl1);
var slackJSONObject = JSON.parse(slackJSON);
var ts = slackJSONObject.messages[0].ts;
var threadedMessagePayload = {
"payload": '{"thread_ts": "' + ts + '","text": "' + '`\nDevice: ' + Device + '\nBrowser: ' + Browser + '\nGeo: ' + GEO + '\nReported Date & Time: ' + date + ' ' + time + '"}',
}
var optionsForThreadedMessage = {
"method": "post",
"payload": threadedMessagePayload
};
UrlFetchApp.fetch(webhookUrl, optionsForThreadedMessage);
}

Add a user to a group programatically

Is it possible to add a user to a group via REST/sdk?
Scenario: We want to add all our users to a mandatory group on a regularly basis.
Thanks!Max
you can get group ids from share point list like this
function GetMandatoryGroups() {
var context;
var factory;
var appContextSite;
var oList;
var collListItem;
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
this.web = appContextSite.get_web();
oList = this.web.get_lists().getByTitle('MandatoryGroups');
context.load(oList);
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><RowLimit>100</RowLimit></View>');
collListItem = oList.getItems(camlQuery);
context.load(collListItem, 'Include(Title,Id)');
context.executeQueryAsync(
Function.createDelegate(this, successHandler),
Function.createDelegate(this, errorHandler)
);
function successHandler() {
MandatoryGroups = new Array();
var listItemInfo = '';
var listitemenumerator = collListItem.getEnumerator();
while (listitemenumerator.moveNext()) {
var olistitem = listitemenumerator.get_current();
//listItemInfo += '<li>ID:' + olistitem.get_id().toString() + ' GroupID: ' + olistitem.get_item('Title') + '</li>';
MandatoryGroups.push(olistitem.get_item('Title'));
}
AutoJoinGroups();
// document.getElementById("message").innerHTML = 'Lists found' + oList.get_title() + ':<ul>' + listItemInfo + '</ul>';
}
function errorHandler(sender, args) {
document.getElementById("message").innerText =
"Could not complete cross-domain call: " + args.get_message();
}
}
you can join users to the mendatory groups like this (after user logs in )
function AutoJoinGroups() {
yam.platform.request({
// yam.request({
url: "groups.json?mine=1",
method: "GET",
data: {},
success: function (group) {
//for ($i = 0; $i < MandatoryGroups.length; $i++) {
// if (!ArrayContains(MandatoryGroups[$i].toString(), group)) {
// joinGroupAsync(MandatoryGroups[$i].toString());
// setTimeout('', 10000);
// // setTimeout(joinGroupAsync(MandatoryGroups[$i].toString()),5000);
// }
//}
var i = 0;
function AsyncAutoJoinLoop() {
if (i < MandatoryGroups.length) {
if (!ArrayContains(MandatoryGroups[i].toString(), group)) {
setTimeout(function () {
joinGroupAsync(MandatoryGroups[i].toString());
i++;
if (i < MandatoryGroups.length) {
AsyncAutoJoinLoop();
}
}, 3000)
}
}
}
AsyncAutoJoinLoop();
// getMyGroups();
},
error: function (group) {
console.error("There was an error with the request.");
}
});
}
function joinGroupAsync(id) {
yam.platform.request({
// yam.request({
url: "group_memberships.json?group_id=" + id,
method: "POST",
data: {},
success: function (group) {
},
error: function (group) {
console.error("There was an error with the request.");
}
});
}
you can add group to the sharepoint list like this.
function InsertMandatoryItem() {
var context;
var factory;
var appContextSite;
var oListItem;
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
this.web = appContextSite.get_web();
var oList = this.web.get_lists().getByTitle('MandatoryGroups');
var itemCreateInfo = new SP.ListItemCreationInformation();
oListItem = oList.addItem(itemCreateInfo);
oListItem.set_item('Title', InsertGroupId);
oListItem.update();
context.load(oListItem);
context.executeQueryAsync(
Function.createDelegate(this, onQuerySucceeded),
Function.createDelegate(this, onQueryFailed)
);
function onQuerySucceeded() {
//alert('Item created: ' + oListItem.get_id());
// getMyGroups();
// AutoJoinGroups();
$.getScript(scriptbase + 'SP.RequestExecutor.js', GetMandatoryGroups);
}
function onQueryFailed(sender, args) {
$.getScript(scriptbase + 'SP.RequestExecutor.js', GetMandatoryGroups);
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
}
You can remove remove group from sharepoint list like this
function RemoveMandatoryGroup() {
var context;
var factory;
var appContextSite;
var collListItem;
var itemId;
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
this.web = appContextSite.get_web();
var oList = this.web.get_lists().getByTitle('MandatoryGroups');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml("<View><Query><Where><Eq><FieldRef Name='Title'/><Value Type='Text'>" + RemoveGroupId.toString() + "</Value></Eq></Where></Query></View>");
collListItem = oList.getItems(camlQuery);
context.load(collListItem, 'Include(Title,Id)');
// this.oListItem.deleteObject();
context.executeQueryAsync(
Function.createDelegate(this, onQuerySucceeded),
Function.createDelegate(this, onQueryFailed)
);
function onQuerySucceeded() {
var oListItem;
var listitemenumerator = collListItem.getEnumerator();
while (listitemenumerator.moveNext()) {
var itemtoDelete = listitemenumerator.get_current();
////listItemInfo += '<li>ID:' + olistitem.get_id().toString() + ' GroupID: ' + olistitem.get_item('Title') + '</li>';
//MandatoryGroups.push(olistitem.get_item('Title'));
itemId = itemtoDelete.get_id();
}
oListItem = oList.getItemById(itemId);
oListItem.deleteObject();
context.executeQueryAsync(Function.createDelegate(this, onQueryDeleteSucceeded), Function.createDelegate(this, onQueryDeleteFailed));
//alert('Item created: ' + oListItem.get_id());
function onQueryDeleteSucceeded() {
//alert('Request failed. ' + args.get_message() +
// '\n' + args.get_stackTrace());
getMyGroups();
$.getScript(scriptbase + 'SP.RequestExecutor.js', GetMandatoryGroups);
}
function onQueryDeleteFailed() {
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
}
function onQueryFailed(sender, args) {
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
}
No, there isn't. This is by design. Yammer likes to entice with the carrot, not by the stick. What we've done is create communications to ask people to join a specific group.
The api does allow the ability for the currently logged to be joined to a specific group. E.g. Put a link on a SharePoint site that says "Join the Yammer group", and have the action join that user to the group. You can see the details for how to do that here:
https://developer.yammer.com/restapi/#rest-groups
ya, in costume app yo can autojoin that user when he logs in..Same thing i ma doing.

Calling a function from onEdit() trigger doesn't work

I want to run a function that updates some values when I edit one cell of a column. This line of the trigger works well: dataCell0.setValue(today_date(new Date())[2]);. But this other line updatePercent(); doesn't. But if I call this updatePercent() function from a time based trigger (in Resources), it works well. What is going wrong with this updatePercent() call?
function onEdit(){
var s = SpreadsheetApp.getActiveSheet();
if( ( s.getName() == "mySheet1" ) || (s.getName() == "mySheet2") ) { //checks that we're on the correct sheet
var r = s.getActiveCell();
if( s.getRange(1, r.getColumn()).getValue() == "PORCENT_TIME") { // If you type a porcent, it adds its date.
var dataCell0 = r.offset(0, 1);
dataCell0.setValue(today_date(new Date())[2]);
updatePercent();
}
}
}
Here the updatePercent function code:
/**
* A function to update percent values accoding to input date.
**/
function updatePercent() {
var sheet = SpreadsheetApp.getActiveSheet();
var column = getColumnNrByName(sheet, "PORCENT_TIME");
var input = sheet.getRange(2, column+1, sheet.getLastRow(), 4).getValues();
var output = [];
for (var i = 0; i < input.length; i++) {
var fulfilledPercent = input[i][0];
Logger.log("fulfilledPercent = " + fulfilledPercent);
var finalDate = input[i][3];
Logger.log("finalDate = " + input[i][3]);
if ( (typeof fulfilledPercent == "number") && (finalDate instanceof Date) ) {
var inputDate = input[i][1]; // Date when input was added.
var restPorcentPen = 100 - fulfilledPercent;
var restantDays = dataDiff(inputDate, finalDate);
var percentDay = restPorcentPen/restantDays;
Logger.log("percentDay = " + percentDay);
var passedTime = dataDiff(inputDate, new Date());
Logger.log("passedTime = " + passedTime);
var passedPorcent = passedTime * percentDay; // How much percent this passed time is?
Logger.log("passedPorcent = " + passedPorcent);
var newPorcent = (fulfilledPercent + passedPorcent);
newPorcent = Math.round(newPorcent * 100) / 100;
Logger.log("newPorcent = " + newPorcent);
var newInputDate = hoje_data(new Date())[2]; // Now update the new input date
// newPorcent = newPorcent.toFixed(2);
output.push([newPorcent, newInputDate]);
sheet.getRange(2, column+1, output.length, 2).setValues(output);
Logger.log(" ");
var column25Dec = getColumnNrByName(sheet, "PORCENT_25DEZ");
var passedTimeSince25Dec = dataDiff(new Date(2013,11,25), new Date()); // Months: January is 0;
var decPercent = (newPorcent - (passedTimeSince25Dec * percentDay)); // .toFixed(2).replace(".", ",");
decPercent = Math.round(decPercent * 100) / 100;
// if (sheet.getRange(output.length+1, column25Dec+1).getValues() == ''){
sheet.getRange(output.length+1, column25Dec+1).setValue(decPercent );
// }
var remainingYears = dataDiffYears(new Date(), finalDate);
sheet.getRange(output.length+1, column).setValue(remainingYears);
}
else {
newPorcent = "Put a final date"
output.push([newPorcent, inputDate]);
sheet.getRange(2, column+1, output.length, 2).setValues(output);
}
if (finalDate instanceof Date){
var remainingYears = dataDiffYears(new Date(), finalDate);
// Logger.log("remainingYears = " + remainingYears);
}
else {
remainingYears = "insert a valid date";
}
sheet.getRange(output.length+1, column).setValue(remainingYears);
}
}
I will guess you're using the new gSheets. Check if it will work in the old-style sheets. The new sheets' onEdit trigger has problems, particularly with getActive.
My problem was in the updatePercent() funciton. Thank you, guys!

import private google fusion table to google docs spreadsheet

I want to build a chart to google fusion table. I know there is an option to do it with fusion table but I need to do that using google spreadsheet.
How do I import a private fusion table to a spreadsheet?
function getdata(authToken) {
query = encodeURIComponent("SELECT * FROM tableid");
var URL = "http://www.google.com/fusiontables/api/query?sql=" + query;
var response = UrlFetchApp.fetch(URL, {
method: "get",
headers: {
"Authorization": "GoogleLogin auth=" + authToken,
}
});
return response.getContentText();
}
The code above gives me the table headers only.
Don't set each cell individually as in the example below unless you need to process each bit of data. Using this is about 10x faster:
var rows = o.length;
var columns = o[0].length;
cell.offset(<startrow>, <startcolumn>, rows, columns).setValues(o);
After a deep research, finally i figured it out after a deep search and reading here.
This is how it looks for the code google docs spreadsheet app script:
function onOpen()
{
var tableID = '00000' // Add the table ID of the fusion table here
var email = UserProperties.getProperty('email');
var password = UserProperties.getProperty('password');
if (email === null || password === null) {
email = Browser.inputBox('Enter email');
password = Browser.inputBox('Enter password');
UserProperties.setProperty('email',email);
UserProperties.setProperty('password', password);
} else {
email = UserProperties.getProperty('email');
password = UserProperties.getProperty('password');
}
var authToken = getGAauthenticationToken(email,password);
query = encodeURIComponent("SELECT * FROM tableID");
var URL = "http://www.google.com/fusiontables/api/query?sql=" + query;
var response = UrlFetchApp.fetch(URL, {
method: "get",
headers: {
"Authorization": "GoogleLogin auth=" + authToken,
}
});
var tableData = response.getContentText();
var o = Utilities.parseCsv(response.getContentText());
var doc = SpreadsheetApp.getActiveSpreadsheet();
var cell = doc.getRange('a1');
var index = 0;
for (var i in o) {
var row = o[i];
var col = 0;
for (var j in row) {
cell.offset(index, col).setValue(row[j]);
col++;
}
index++;
}
}
function getGAauthenticationToken(email, password) {
password = encodeURIComponent(password);
var response = UrlFetchApp.fetch("https://www.google.com/accounts/ClientLogin", {
method: "post",
payload: "accountType=GOOGLE&Email=" + email + "&Passwd=" + password + "&service=fusiontables&Source=testing"
});
var responseStr = response.getContentText();
responseStr = responseStr.slice(responseStr.search("Auth=") + 5, responseStr.length);
responseStr = responseStr.replace(/\n/g, "");
return responseStr;
}
After that you can do whatever you want in the spreadsheet.
BTW, I still think there is a simple way to import a private table into a spreadsheet automaticly.