Skip to content
This repository has been archived by the owner on Apr 25, 2018. It is now read-only.

Commit

Permalink
Adding support for hashtags
Browse files Browse the repository at this point in the history
  • Loading branch information
whizzzkid committed Nov 16, 2017
1 parent 00387d1 commit f28acba
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 81 deletions.
39 changes: 26 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,34 @@ A user's public data on Instagram can be accessed on `https://www.instagram.com/

As of now, the service is running on [https://igpi.ga/](https://igpi.ga/)(Heroku) I am not aware about limits, it's basically running on free tier. If it hits any limit, I'll have to figure that out. But the intended use is to replace `http://www.instagram.com` with `https://igpi.ga/` or `https://igapi.ga/`.

## Accessing Data
## Requests

| Data Type | End Point | Parameters | Sample URL |
|-|-|-|-|
| User Infor | /\<username\>/ | \_\_a=1 (advanced enabled) | https://igpi.ga/whizzzkid/?__a=1 |
| User Posts | \<username\>/media/ | callback: \<jsonp\><br>count: \<number_of_posts\><br>cursor: \<next\> | https://igpi.ga/whizzzkid/media?count=3 |
| User Posts<br>(Faster) | /graphql/query/ | callback: \<jsonp\><br>count: \<number_of_posts\><br>cursor: \<next\><br>*user\_id: <user\_id>| https://igpi.ga/graphql/query/?user_id=1606740656&count=3 |
| HashTags | /explore/tags/\<hashtag\>/media/ | callback: \<jsonp\><br>count: \<number_of_posts\><br>cursor: <next> | https://igpi.ga/explore/tags/yyc/media?count=3 |
| HashTags<br>(Convienient) | /graphql/query/ | callback: \<jsonp\><br>count: \<post_count\><br>cursor: \<next\><br>*tag: \<hashtag\>| https://igpi.ga/graphql/query/?tag=yyc&count=3|
|||*: Required||

## Response

Each response (except for advanced parameters looks like):
```
{
"next": "next page url",
"posts": [
{
},
...
]
}
```

There is a lot more info in each posts. Check them out [here](https://igpi.ga/graphql/query/?tag=yyz)

* Getting Instagrams's data as is. The following will give you access to the same data instagram provides.

`https://igpi.ga/<user>/media/` or `https://igapi.ga/<user>/media/`

* Limiting the amount of images to be sent.

`https://igpi.ga/<user>/media/?count=10` or `https://igapi.ga/<user>/media/?count=10`

* Using jsonp

`https://igpi.ga/<user>/media/?callback=foo` or `https://igapi.ga/<user>/media/?callback=foo`

* Using pagination: Each response has url links to the next and previous page, you can use that to traverse through the results.

## Integration

Expand Down
174 changes: 108 additions & 66 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,9 @@ const InstaProxy = {};
NOT_FOUND: 404,
SERVER_ERROR: 500
};
/** @const */ InstaProxy.GRAPH_OPTIONS = {
id: '',
first: 3,
after: ''
};
/** @const */ InstaProxy.GRAPH_PATH = '/graphql/query/';
/** @const */ InstaProxy.GRAPH_QUERY_ID = '17888483320059182';
/** @const */ InstaProxy.GRAPH_USER_QUERY_ID = '17888483320059182';
/** @const */ InstaProxy.GRAPH_TAG_QUERY_ID = '17875800862117404';


/**
Expand Down Expand Up @@ -73,36 +69,6 @@ InstaProxy.constructURL = function(protocol, host, path, query) {
};


/**
* Perform GQL response reconstruction
* @param {Object} request
* @param {Object} json
* @return {Object} json response
* @this
*/
InstaProxy.reconstructJSONfromGQLResponse = function(request, json) {
json = json.data.user.edge_owner_to_timeline_media;
var response = {};
var query;

// just copying.
query = Object.assign({}, request.query);

if (json.page_info.has_next_page) {
query['cursor'] = json.page_info.end_cursor;
response['next'] = this.constructURL(
this.PROTOCOL, request.get('host'), request.path, query);
}

response.images = [];
for (var i in json.edges) {
response.images.push(json.edges[i].node);
}

return response;
};


/**
* Builds the callback function for handling Instagram response.
* @param {Function} callback
Expand Down Expand Up @@ -131,9 +97,6 @@ InstaProxy.instagramFetcher = function(callback) {
* @this
*/
InstaProxy.fetchFromInstagram = function(path, query, callback) {
this.log(
'Processing [P:"' + path + '", ' +
'Q:"' + JSON.stringify(query) + '"]');
Https.get(
this.constructURL(
'https', 'www.instagram.com', path, query),
Expand All @@ -142,6 +105,64 @@ InstaProxy.fetchFromInstagram = function(path, query, callback) {
};


/**
* Performs fetch from IG's GQL servers.
* @param {object} param
* @param {object} request
* @param {object} response
* @this
*/
InstaProxy.fetchFromInstagramGQL = function(param, request, response) {

var queryId = '';

if (param.id != null) {
queryId = this.GRAPH_USER_QUERY_ID;
}

if (param.tag_name != null) {
queryId = this.GRAPH_TAG_QUERY_ID;
}

if (queryId != '') {
var query = this.generateGraphQLQuery(queryId, param, request);

var callback = function(body) {
var json = JSON.parse(body).data;
if (param.id != null) {
json = json.user.edge_owner_to_timeline_media;
} else {
json = json.hashtag.edge_hashtag_to_media;
}
var response = {};
var query;

// just copying.
query = Object.assign({}, request.query);

if (json.page_info.has_next_page) {
query['cursor'] = json.page_info.end_cursor;
response['next'] = this.constructURL(
this.PROTOCOL, request.get('host'), request.path, query);
}

response.posts = [];
for (var i in json.edges) {
response.posts.push(json.edges[i].node);
}

return response;
}.bind(this);

this.fetchFromInstagram(
this.GRAPH_PATH,
query,
this.callbackWrapper(
response, this.generateCallBackForWrapper(callback, response)));
}
};


/**
* Detects if the URL is safe based on blacklist.
* @param {String} urlString
Expand Down Expand Up @@ -238,38 +259,30 @@ InstaProxy.callbackWrapper = function(response, callback) {


/**
* Processing users by ID from the graph
* @param {Number} userId
* @param {Object} request
* @param {Object} response
* Generates query object for graphQL.
* @param {string} queryId
* @param {object} extraParams
* @param {object} request
* @return {object} query
* @this
*/
InstaProxy.processByUserId = function(userId, request, response) {
// Create a copy
var variables = Object.assign({}, this.GRAPH_OPTIONS);
InstaProxy.generateGraphQLQuery = function(queryId, extraParams, request) {
var variables = {};

// Assign values
variables.id = userId;
if (request.query.count != null) {
variables.first = request.query.count;
}
variables.first = (request.query.count != null) ? request.query.count : 1;
if (request.query.cursor != null) {
variables.after = request.query.cursor;
}
// Generate query for IG-GQL server.
var query = {
query_id: this.GRAPH_QUERY_ID,

for (var i in extraParams) {
variables[i] = extraParams[i];
}

return {
query_id: queryId,
variables: JSON.stringify(variables)
};
// Fetch
var callback = function(body) {
return this.reconstructJSONfromGQLResponse(request, JSON.parse(body));
}.bind(this);

this.fetchFromInstagram(
this.GRAPH_PATH,
query,
this.callbackWrapper(
response, this.generateCallBackForWrapper(callback, response)));
};


Expand Down Expand Up @@ -302,7 +315,13 @@ InstaProxy.processAdvanceParams = function(request, response) {
InstaProxy.processGQL = function(request, response) {
// if request has user id
if (request.query.user_id) {
this.processByUserId(request.query.user_id, request, response);
this.fetchFromInstagramGQL(
{ id: request.query.user_id }, request, response);
}

if (request.query.tag) {
this.fetchFromInstagramGQL(
{ tag_name: request.query.tag }, request, response);
}
};

Expand All @@ -316,14 +335,27 @@ InstaProxy.processGQL = function(request, response) {
InstaProxy.processLegacy = function(request, response) {
var callback = function(body) {
var json = JSON.parse(body);
this.processByUserId(json.user.id, request, response);
this.fetchFromInstagramGQL({ id: json.user.id }, request, response);
};
this.fetchFromInstagram(
'/' + request.params.username + '/',
{ '__a': 1 },
this.callbackWrapper(response, callback.bind(this)));
};


/**
* Process by tagname.
* @param {object} request
* @param {object} response
* @this
*/
InstaProxy.processTagName = function(request, response) {
this.fetchFromInstagramGQL(
{ tag_name: request.params.tag }, request, response);
};


/**
* Send Response.
* @param {Object} response
Expand Down Expand Up @@ -420,6 +452,10 @@ InstaProxy.safeRefererMW = function(request, response, next) {
'Denying request from referer: ' + request.headers.referer)
);
} else {
this.log(
'Processing [P:"' + request.path + '", ' +
'Q:"' + JSON.stringify(request.query) +'", ' +
'R:"' + request.referer + '"]');
next();
}
};
Expand Down Expand Up @@ -447,7 +483,13 @@ InstaProxy.setUpRoutes = function() {
this.safeRefererMW.bind(this),
this.processLegacy.bind(this));

// remining, including advanced params
// Tag Names
this.app.get(
'/explore/tags/:tag/media/',
this.safeRefererMW.bind(this),
this.processTagName.bind(this));

// remining, including advanced params
this.app.get(
'*',
this.safeRefererMW.bind(this),
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "insta-proxy",
"version": "4.1.1",
"version": "4.2.0",
"description": "Provides an API layer over Instagram's public API.",
"main": "app.js",
"scripts": {
Expand Down

0 comments on commit f28acba

Please sign in to comment.