TypeError: Cannot read property 'users' of undefined error - api

I have written the code:
function getId(username) {
var infoUrl = "https://www.instagram.com/web/search/topsearch/?context=user&count=0&query=" + username
return parseInt(fetch(infoUrl)['users']);
}
function fetch(url) {
var ignoreError = {
"muteHttpExceptions": true
};
var source = UrlFetchApp.fetch(url, ignoreError).getContentText();
var data = console.log(source);
return data;
}
To get the userID of the username input.
The error corresponds to the line:
return parseInt(fetch(infoUrl)['users']);
I have tried differnt things but I cant get it to work. The url leads to a page looking like this:
{"users": [{"position": 0, "user": {"pk": "44173477683", "username": "mykindofrock", "full_n........
Where the numbers 44173477683 after the "pk": are what I am trying to get as an output.
I hope someone can help as I am very out of my depth, but I guess this is how we learn! :)

I was surprised that the endpoint you provided actually led to a JSON file. I would have thought that to access the Instagram API, you would need register a developer account with Facebook etc. Nevertheless, it does return a JSON by visiting in the browser. I suppose that it just shows the publicly available information on each user.
However, with Apps Script it seems like a different story. I visited:
https://www.instagram.com/web/search/topsearch/?context=user&count=0&query=user
In a browser and chose a random user id. Then I called it from Apps Script with UrlFetchApp:
function test(){
var username = "username7890543216"
var infoUrl = "https://www.instagram.com/web/search/topsearch/?context=user&count=0&query=" + username
var options = {
'muteHttpExceptions': true
}
var result = UrlFetchApp.fetch(infoUrl, options)
console.log(result.getResponseCode())
}
Which returns a 429 response. Which is a "Too Many Requests" response. So if I had to guess, I would say that all requests to this unauthenticated endpoint from Apps Script have been blocked. This is why when replacing the console.log(result.getResponseCode()) with console.log(result.getContentText()), you get a load of HTML (not JSON) part of it which says:
<title>
Page Not Found • Instagram
</title>
Though maybe its IP based. Try and run this code from your end, unless you get a response code of 200, it is likely that you simply can't access this information from Apps Script.

You are setting data to the return value of console.log(source) which is undefined. So no matter what the data is, you will get undefined.
Another thing to avoid is that fetch will not necessarily be hoisted because fetch is a built in function to make API calls.

Related

Trying to use TwitterLib 25 with GAS

So using the following simple code to just test tweet to Twitter. I have signed up for the account and have all the pertinent info. The only thing I may be doing wrong is putting the wrong key/secret in the wrong area. Here is my Twitter dev setup:
This is my simple code:
function sendTweet(status) {
status = "Is this a cool tweet?";
var twitterKeys = {
TWITTER_CONSUMER_KEY: '**************************',
TWITTER_CONSUMER_SECRET: '**************************',
TWITTER_ACCESS_TOKEN: '**************************-**************************',
TWITTER_ACCESS_SECRET: '**************************',
};
var props = PropertiesService.getScriptProperties();
props.setProperties(twitterKeys);
var service = new Twitterlib.OAuth(props);
if (service.hasAccess()) {
var response = service.sendTweet(status);
if (response) {
Logger.log('Tweet ID ' + response.id_str);
} else {
// Tweet could not be sent
// Go to View -> Logs to see the error message
}
}
}
My consumer key/secret -> API Key and Secret below and my access token/secret -> Access Token and Secret below. I am seeing the following error which doesn't give me a lot to go on as far as errors and been fighting to figure this out for days.
Send tweet failure. Error was:
{"name":"Exception"}
options were:
{"method":"POST","payload":"status=Is%20this%20a%20cool%20tweet%3F","headers":{"Authorization":"OAuth oauth_consumer_key=\"**************************\", oauth_nonce=\"**************************\", oauth_signature=\"**************************%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1644456180\", oauth_token=\"**************************-**************************\", oauth_version=\"1.0\""},"escaping":false}
Any help or resources would be a huge help in figuring this out!! Thank you in advance.
phi
This turned out being I needed to turn on essential API access in the Twitter API Developer site. Once I turned that on, and retried, it was a success. Hope that helps anyone else.

Google Sheets API v4 receives HTTP 401 responses for public feeds

I'm having no luck getting a response from v4 of the Google Sheets API when running against a public (i.e. "Published To The Web" AND shared with "Anyone On The Web") spreadsheet.
The relevant documentation states:
"If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you."
And to provide the API key, the documentation states:
"After you have an API key, your application can append the query parameter key=yourAPIKey to all request URLs."
So, I should be able to get a response listing the sheets in a public spreadsheet at the following URL:
https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}?key={myAPIkey}
(with, obviously, the id and key supplied in the path and query string respectively)
However, when I do this, I get an HTTP 401 response:
{
error: {
code: 401,
message: "The request does not have valid authentication credentials.",
status: "UNAUTHENTICATED"
}
}
Can anyone else get this to work against a public workbook? If not, can anyone monitoring this thread from the Google side either comment or provide a working sample?
I managed to get this working. Even I was frustrated at first. And, this is not a bug. Here's how I did it:
First, enable these in your GDC to get rid of authentication errors.
-Google Apps Script Execution API
-Google Sheets API
Note: Make sure the Google account you used in GDC must be the same account you're using in Spreadsheet project else you might get a "The API Key and the authentication credential are from different projects" error message.
Go to https://developers.google.com/oauthplayground where you will acquire authorization tokens.
On Step 1, choose Google Sheets API v4 and choose https://www.googleapis.com/auth/spreadsheets scope so you have bot read and write permissions.
Click the Authorize APIs button. Allow the authentication and you'll proceed to Step 2.
On Step 2, click Exchange authorization code for tokens button. After that, proceed to Step 3.
On Step 3, time to paste your URL request. Since default server method is GET proceed and click Send the request button.
Note: Make sure your URL requests are the ones indicated in the Spreadsheetv4 docs.
Here's my sample URL request:
https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID?includeGridData=false
I got a HTTP/1.1 200 OK and it displayed my requested data. This goes for all Spreadsheetv4 server-side processes.
Hope this helps.
We recently fixed this and it should now be working. Sorry for the troubles, please try again.
The document must be shared to "Anyone with the link" or "Public on the web". (Note: the publishing settings from "File -> Publish to the web" are irrelevant, unlike in the v3 API.)
This is not a solution of the problem but I think this is a good way to achieve the goal. On site http://embedded-lab.com/blog/post-data-google-sheets-using-esp8266/ I found how to update spreadsheet using Google Apps Script. This is an example with GET method. I will try to show you POST method with JSON format.
How to POST:
Create Google Spreadsheet, in the tab Tools > Script Editor paste following script. Modify the script by entering the appropriate spreadsheet ID and Sheet tab name (Line 27 and 28 in the script).
function doPost(e)
{
var success = false;
if (e != null)
{
var JSON_RawContent = e.postData.contents;
var PersonalData = JSON.parse(JSON_RawContent);
success = SaveData(
PersonalData.Name,
PersonalData.Age,
PersonalData.Phone
);
}
// Return plain text Output
return ContentService.createTextOutput("Data saved: " + success);
}
function SaveData(Name, Age, Phone)
{
try
{
var dateTime = new Date();
// Paste the URL of the Google Sheets starting from https thru /edit
// For e.g.: https://docs.google.com/---YOUR SPREADSHEET ID---/edit
var MyPersonalMatrix = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/---YOUR SPREADSHEET ID---/edit");
var MyBasicPersonalData = MyPersonalMatrix.getSheetByName("BasicPersonalData");
// Get last edited row
var row = MyBasicPersonalData.getLastRow() + 1;
MyBasicPersonalData.getRange("A" + row).setValue(Name);
MyBasicPersonalData.getRange("B" + row).setValue(Age);
MyBasicPersonalData.getRange("C" + row).setValue(Phone);
return true;
}
catch(error)
{
return false;
}
}
Now save the script and go to tab Publish > Deploy as Web App.
Execute the app as: Me xyz#gmail.com,
Who has access to the app: Anyone, even anonymous
Then to test you can use Postman app.
Or using UWP:
private async void Button_Click(object sender, RoutedEventArgs e)
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(#"https://script.google.com/");
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("utf-8"));
string endpoint = #"/macros/s/---YOUR SCRIPT ID---/exec";
try
{
PersonalData personalData = new PersonalData();
personalData.Name = "Jarek";
personalData.Age = "34";
personalData.Phone = "111 222 333";
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(personalData), Encoding.UTF8, "application/json");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(endpoint, httpContent);
if (httpResponseMessage.IsSuccessStatusCode)
{
string jsonResponse = await httpResponseMessage.Content.ReadAsStringAsync();
//do something with json response here
}
}
catch (Exception ex)
{
}
}
}
public class PersonalData
{
public string Name;
public string Age;
public string Phone;
}
To above code NuGet Newtonsoft.Json is required.
Result:
If your feed is public and you are using api key, make sure you are throwing a http GET request.In case of POST request, you will receive this error.
I faced same.
Getting data using
Method: spreadsheets.getByDataFilter has POST request

Having trouble making a OAuth 1.0a signed request to the Tumblr API using HelloJS

I'm trying to interface with the Tumblr API to pull a list of followers. I'm brand new the whole OAuth thing, so I was trying to model my calls off the demos at https://adodson.com/hello.js/demos/tumblr.html . Unfortunately, the example they give only requires the API key for identification (https://www.tumblr.com/docs/en/api/v2#posts) where as getting the followers needs a signed OAuth request (https://www.tumblr.com/docs/en/api/v2#followers).
The call I'm using is:
function getFollowers(blog){
hello('tumblr').api('blog/'+blog+'/followers/').then(function(r){
console.log("r", r);
//Bellow here not really relevant
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
}
This generates the request url from the proxy:
https://auth-server.herokuapp.com/proxy?path=https%3A%2F%2Fapi.tumblr.com%2Fv2%2Fblog%2Fnmlapp.tumblr.com%2Ffollowers%2F%3Fapi_key%3DREDACTED08u%26callback%3D_hellojs_9kvqxi31&access_token=&then=redirect&method=get&suppress_response_codes=truee
and Tumblr's API returns
_hellojs_9kvqxi31({"meta":{"status":401,"msg":"Not Authorized"},"response":[]});
I can see that the login call has all of the OAuth info in the Query String Parameters field, and the one I'm trying to make does not, but I'm not sure what the right way to include that through helloJS is.
Got it, the function had to be wrapped in the login method. This was shown in the other example, but the way that it called parameters from the api object had me confused.
function doTheThing(network){
hello( network ).login({force:false}).then( function(r){
hello('tumblr').api('followers').then(function(r){
console.log("r", r);
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
});
}
//...
tumblr:{
get: {
//...
//This next part needs to be generated dynamically, but you get the idea
'followers': 'blog/BLOGNAME.tumblr.com/followers',
}
callback(p.path);
}
},
post: {
//...
'followers': function(p, callback) {
p.path = 'followers';
query(p, callback);
}
},

Meteor.http.get issue with Twitter API

I am using Meteor and the Twitter API for a project. I want to get information on a user from Twitter. I wrote a function that for example returns only the location of a user from Twitter. I believe this is the proper way to do a request on Meteor. Here it is :
Meteor.methods({getTwitterLocation: function (username) {
Meteor.http.get("https://api.twitter.com/1/users/show.json?screen_name="+ username +"&include_entities=true", function(error, result) {
if (result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson.location);
console.log("location works");
return (respJson.location)
}else {
return ( "Unknown user ")
}
});
}});
Now this function will log what's in the console on my Git Bash. I get someones Location by doing a Meteor.call. But I want to post what that function returns on a page. In my case, I want to post in on a user's profile. This doesn't work. But the console.log(respJson.location) returns the location in my Git Bash but it won't display anything on the profile page. This is what I did on my profile page:
profile.js :
Template.profile.getLocation= function(){
return Meteor.call("getTwitterLocation","BillGates");
}
profile.html :
<template name="profile">
from {{getLocation}}
</template>
With that I get "Seattle, WA" and " "location works" on my Git Bash but nothing on the profile page. If anyone knows what I can do, that'd be really appreciated. Thanks.
Firstly when data is returned from the server you need to use a synchronous call, as the callback will return the data when the server already thinks the meteor method has completed. (the callback will be fired at a later time, when the data is returned from the server, by which time the meteor client would have already got a response)
var result = Meteor.http.get("https://api.twitter.com/1/users/show.json?screen_name="+ username +"&include_entities=true");
if (result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson.location);
console.log("location works");
return (respJson.location)
}else {
return ( "Unknown user ")
}
The second is you need to use a Session hash to return the data from the template. This is because it will take time to get the response and the getLocation would expect an instant result (without a callback). At the moment client side javascript can't use synchronous api calls like on the server.
Template.profile.getLocation= function(){
return Session.get("twitterlocation");
}
Use the template created event to fire the meteor call:
Template.profile.created = function() {
Meteor.call("getTwitterLocation","BillGates", function(err,result) {
if(result && !err) {
Session.set("twitterlocation", result);
}
else
{
Session.set("twitterlocation", "Error");
}
});
});
Update:
Twitter has since updated its API to 1.1 a few modifications are required:
You now need to swap over to the 1.1 api by using 1.1 instead of 1. In addition you need to OAuth your requests. See https://dev.twitter.com/docs/auth/authorizing-request. Below contains sample data but you need to get proper keys
var authkey = "OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp=""+(new Date().getTime()/1000).toFixed(0)+"",
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
oauth_version="1.0"";
Be sure to remove the newlines, I've wrapped it to make it easy to read.
var result = Meteor.http.get("https://api.twitter.com/1.1/users/show.json?screen_name="+ username +"&include_entities=true",{headers:{Authorization : authkey});
If you find this a bit troublesome it might be easier to just use a package like https://github.com/Sewdn/meteor-twitter-api via meteorite to OAuth your requests for you.

Can't Query Google Analytics Reports API Using OAuth 2

I am trying to to use the latest version of the Report API using OAuth 2. It doesn't appear that there are many people using this version yet, so it has been really hard to find examples.
I have a refresh token, which I am using to generate an access token.
private AnalyticsService getAnalyticsService()
{
AuthorizationServerDescription description = new AuthorizationServerDescription();
description.TokenEndpoint = new Uri(login.TokenEndpoint);
description.AuthorizationEndpoint = new Uri(login.AuthorizationEndpoint);
WebServerClient client = new WebServerClient(description, login.ClientId, login.ClientSecret);
OAuth2Authenticator<WebServerClient> authenticator = new OAuth2Authenticator<WebServerClient>(client, authenticate);
AnalyticsService service = new AnalyticsService(authenticator);
return service;
}
private IAuthorizationState authenticate(WebServerClient client)
{
string[] scopes = new string[] { login.ScopeUrl }; // not sure if this is necessary
IAuthorizationState state = new AuthorizationState(scopes) { RefreshToken = login.RefreshToken };
client.RefreshToken(state);
return state;
}
This appears to be working just fine:
{
"access_token" : "ya29.AHES6ZQy67SSLHWJWGWcLbLn69yKfq59y6dTHDf4ZoH9vHY",
"token_type" : "Bearer",
"expires_in" : 3600
}
However, when I do a request, I am getting an error. For example, here
is a query that results in an error:
AnalyticsService service = getAnalyticsService();
ManagementResource.ProfilesResource.ListRequest request = service.Management.Profiles.List("~all", "~all");
return request.Fetch();
This is the error I get:
{"error":{"errors":[{"domain":"global","reason":"authError","message":"Invalid
Credentials","locationType":"header","location":"Authorization"}],"code":401,"message":"Invalid
Credentials"}}
I have tried other queries, providing valid profile IDs. However, I am
always getting a 401 error, saying I'm not authorized. I am having
trouble finding examples where people are using this code. It could be
something simple like a bad URL or something. Unfortunately, I have no
way to telling. It seems strange that I can get an access token, but I
can't seem to perform any queries.
With OAuth 2, the scope changed from:
https://www.google.com/analytics/feeds/
to:
https://www.googleapis.com/auth/analytics.readonly
You are getting the authentication error because you were trying to get access without the proper scope.
Quick and easy fix.