forked from e4c4/instafeed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
helpers.js
executable file
·226 lines (205 loc) · 7.85 KB
/
helpers.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
var redis = require('redis');
var redistogo = require('redis-url');
var settings = require('./settings');
var crypto = require('crypto');
//var redisClient = redis.createClient(settings.REDIS_PORT, settings.REDIS_HOST);
if (process.env.REDISTOGO_URL) {
//var redisClient2 = require('redis-url').createClient(process.env.REDISTOGO_URL);
/*function newRedisClient() {
//var client = redis.createClient(redisConfig.port, redisConfig.host);
var client = require('redis-url').createClient(process.env.REDISTOGO_URL);
var clientAuth = function() { client.auth(process.env.REDISTOGO_URL.password); }
//client.addListener('connected', clientAuth);
//client.addListener('reconnected', clientAuth);
//clientAuth();
return client;
}*/
//var redisClient2 = newRedisClient();
var redisClient2 = redistogo.createClient(process.env.REDISTOGO_URL);
} else {
var redisClient2 = redis.createClient(settings.REDIS_PORT, settings.REDIS_HOST);
}
function isValidRequest(request) {
// First, let's verify the payload's integrity by making sure it's
// coming from a trusted source. We use the client secret as the key
// to the HMAC.
var hmac = crypto.createHmac('sha1', settings.CLIENT_SECRET);
hmac.update(request.rawBody);
var providedSignature = request.headers['x-hub-signature'];
var calculatedSignature = hmac.digest(encoding='hex');
// If they don't match up or we don't have any data coming over the
// wire, then it's not valid.
return !((providedSignature != calculatedSignature) || !request.body)
}
exports.isValidRequest = isValidRequest;
function debug(msg) {
if (settings.debug) {
console.log(msg);
}
}
exports.debug = debug;
/*
Each update that comes from Instagram merely tells us that there's new
data to go fetch. The update does not include the data. So, we take the
geography ID from the update, and make the call to the API.
*/
function processGeography(geoName, update){
var path = '/v1/geographies/' + update.object_id + '/media/recent/';
getMinID(geoName, function(error, minID){
var queryString = "?client_id="+ settings.CLIENT_ID;
if(minID){
queryString += '&min_id=' + minID;
} else {
// If this is the first update, just grab the most recent.
queryString += '&count=1';
}
var options = {
host: settings.apiHost,
// Note that in all implementations, basePath will be ''. Here at
// instagram, this aint true ;)
path: settings.basePath + path + queryString
};
if(settings.apiPort){
options['port'] = settings.apiPort;
}
// Asynchronously ask the Instagram API for new media for a given
// geography.
debug("processGeography: getting " + path);
settings.httpClient.get(options, function(response){
var data = '';
response.on('data', function(chunk){
debug("Got data...");
data += chunk;
});
response.on('end', function(){
debug("Got end.");
try {
// trying to remove invalid utf-8 chars... doesn't seem to help
//var re = /(?![\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})./g;
//data = data.replace(re, "")
data = unescape(encodeURIComponent(data));
var parsedResponse = JSON.parse(data);
} catch (e) {
console.log('Couldn\'t parse data. Malformed?');
return;
}
if(!parsedResponse || !parsedResponse['data']){
console.log('Did not receive data for ' + geoName +':');
console.log(data);
return;
}
setMinID(geoName, parsedResponse['data']);
// Let all the redis listeners know that we've got new media.
redisClient2.publish('channel:' + geoName, data);
debug("Published: " + data);
});
});
});
}
exports.processGeography = processGeography;
/*
get the tagged media from Instagram -- tag version of processGeography
*/
function processTag(tagName, update){
var path = '/v1/tags/' + update.object_id + '/media/recent/';
getMinID(tagName, function(error, minID){
var queryString = "?client_id="+ settings.CLIENT_ID;
if(minID){
console.log('minID is present for api call');
//queryString += '&min_id=' + minID;
//queryString += '&min_tag_id=' + minID;
} else {
console.log('no minID for api call');
// If this is the first update, just grab the most recent.
//queryString += '&count=1';
//queryString += '&min_tag_id=134086694019836510_16982601';
}
var options = {
host: settings.apiHost,
// Note that in all implementations, basePath will be ''. Here at
// instagram, this aint true ;)
path: settings.basePath + path + queryString
};
if(settings.apiPort){
options['port'] = settings.apiPort;
}
// Asynchronously ask the Instagram API for new media for a given
// geography.
debug("processTag: getting " + options.path);
settings.httpClient.get(options, function(response){
var data = '';
response.on('data', function(chunk){
debug("Got data...");
data += chunk;
});
response.on('end', function(){
debug("Got end.");
try {
// trying to remove invalid utf-8 chars... doesn't seem to help
//var re = /(?![\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})./g;
//data = data.replace(re, "")
data = unescape(encodeURIComponent(data));
var parsedResponse = JSON.parse(data);
} catch (e) {
console.log('Couldn\'t parse data. Malformed?');
return;
}
if(!parsedResponse || !parsedResponse['data']){
console.log('Did not receive data for ' + tagName +':');
console.log(data);
return;
}
//setMinID(tagName, parsedResponse['data']);
// Let all the redis listeners know that we've got new media.
redisClient2.publish('channel:' + tagName, data);
debug("Published: " + data);
});
});
});
}
exports.processTag = processTag;
function getMedia(callback){
// This function gets the most recent media stored in redis
redisClient2.lrange('media:objects', 0, 19, function(error, media){
//debug("getMedia: got " + media.length + " items");
// Parse each media JSON to send to callback
media = media.map(function(json){return JSON.parse(json);});
callback(error, media);
});
}
exports.getMedia = getMedia;
/*
In order to only ask for the most recent media, we store the MAXIMUM ID
of the media for every geography we've fetched. This way, when we get an
update, we simply provide a min_id parameter to the Instagram API that
fetches all media that have been posted *since* the min_id.
You might notice there's a fatal flaw in this logic: We create
media objects once your upload finishes, not when you click 'done' in the
app. This means that if you take longer to press done than someone else
who will trigger an update on your same geography, then we will skip
over your media. Alas, this is a demo app, and I've had far too
much red bull – so we'll live with it for the time being.
*/
function getMinID(geoName, callback){
redisClient2.get('min-id:channel:' + geoName, callback);
}
exports.getMinID = getMinID;
function setMinID(geoName, data){
var sorted = data.sort(function(a, b){
return parseInt(b.id) - parseInt(a.id);
});
var nextMinID;
try {
console.log('going to set nextMinID');
console.log('data is:');
console.log(sorted);
nextMinID = parseInt(sorted[0].id);
console.log('set nextMinID');
redisClient2.set('min-id:channel:' + geoName, nextMinID);
console.log('set Redis min-id:channel');
} catch (e) {
console.log('Error parsing min ID for: ' + geoName);
console.log(sorted);
}
}
exports.setMinID = setMinID;