-
Notifications
You must be signed in to change notification settings - Fork 0
/
steamcrawler.js
187 lines (163 loc) · 5.99 KB
/
steamcrawler.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
var http = require("http");
var async = require("async");
var mongoose = require('mongoose');
var Random = require("random-js");
var Game, UserEntry;
// Database bs
var db = mongoose.connection;
db.on('error', console.error);
db.once('open', function() {
// Game Schema
var gameSchema = new mongoose.Schema({
appid: Number
, title: String
});
Game = mongoose.model('Game', gameSchema);
// UserEntry Schema
var userEntrySchema = new mongoose.Schema({
uid: Number
, pinged: { type : Date, default: Date.now }
, lastOnline: Number
, totalGames: Number
, library: [{
game: {type: mongoose.Schema.Types.ObjectId, ref: 'Game'}
, hours: Number
, hoursForever: Number
, lastPlayed: Number
}]
, error: String
});
UserEntry = mongoose.model('UserEntry', userEntrySchema);
});
mongoose.connect('mongodb://localhost/test');
// --End database bs
// THIS BE THE BUSINESS!
var totalPages = 172000000; // ! Be sure to keep this up to date
var maxRequests = 10;
var successGoal = 1000;
var currentAttempts = 0;
var currentSuccess = 0;
var delayMin = 0;
var delayMax = 0;
var random = new Random(Random.engines.mt19937().autoSeed());
var uidPrefix = 765611;
// Start pinging Steam's website
requestLoop();
function requestLoop(){
// Generate a random UID.
// Due to Javascript's integer limitations, only part of the full UID is
// used. The rest is held in the variable uidPrefix, and is only needed
// for appearances.
var steamUID = 97960265729 + random.integer(0, totalPages);
var url = "http://steamcommunity.com/profiles/"+uidPrefix.toString()+steamUID.toString()+"/games?tab=all"
download(url, function(data) {
// Check if the returned data is a user page
// User pages are expected to have the 'rgGames' variable)
if(data && data.indexOf("rgGames") !== -1){
// Slice out the Javascript section containing the game library
libraryString = data.slice(data.indexOf("rgGames"), data.length);
libraryString = libraryString.slice(11, libraryString.indexOf("];"));
// Evaluate the library string as an array
gameLibrary = eval("[" + libraryString + "]");
// Note that another ping attempt was made, and it returned a user page
currentAttempts++;
if (libraryString) {
// If the library (the rgGames field) wasn't empty...
var formattedGameLibrary = [];
var completedCallbacks = 0;
var asyncGameChecks = [];
gameLibrary.forEach(function(item) {
// If you've never seen the Async module in action, look it up on Google
asyncGameChecks.push(function(callback) {
// Check if the item is already in the Game collection
Game.findOne({ appid: item.appid }, function(err, game) {
if(err)
callback(err)
else {
// If it isn't, save it as a new Game to the Game collection
if(!game){
game = new Game( {
appid: item.appid
, title: item.name
});
game.save(function(err, game) {
if(err) return console.error(err);
});
}
// Properly format the game data for Mongoose and add it to an array
if(item.hours_forever){
// If the game has been played, save the play times...
formattedGameLibrary.push({
game: game
, hours: item.hours
, hoursForever: item.hours_forever.replace(/,/g, '')
, lastPlayed: item.last_played
});
}
else {
// ... otherwise, do not include the play time fields
formattedGameLibrary.push({
game: game
});
}
callback(null);
}
}); //- End Game.findOne()
}); //- End asyncGameChecks.push()
}); //- End gameLibrary.forEach()
async.parallel(asyncGameChecks, function(err){
if(err)
console.log("Error");
else {
// Make a user entry. These are not meant to be unique -- You can have
// more than one entry for the same UID, they'll simply have different
// time stamps. This should make it easy to track the same user over
// time.
var userEntry = new UserEntry({
uid: steamUID
, totalGames: gameLibrary.length
, library: formattedGameLibrary
});
userEntry.save(function(err, userEntry) {
if(err) return console.error(err);
// Having added a new user entry, we mark one more successful request
// Yay!
currentSuccess++;
});
}
}); //- End async.parallel()
}
else {
// ... if the field 'rgGames' was empty, the account is either private
// or has not been set up. Either way, we must note that the attempt
// was made and that it returned a valid account page.
var userEntry = new UserEntry({
uid: steamUID
, error: "no game data available"
});
userEntry.save(function(err, userEntry) {
if(err) return console.error(err);
});
}
process.stdout.write(currentSuccess + "/" + currentAttempts + " pings\033[0G");
}
// else -- Not a user page
// Begin again again again again...
requestLoop();
});
}
// Utility function that downloads a URL and invokes
// callback with the data.
function download(url, callback) {
http.get(url, function(res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on("end", function() {
callback(data);
});
}).on("error", function() {
callback(null);
});
}