2013-10-16 17:12:05 +02:00
|
|
|
/**
|
|
|
|
* Module Name: sstats
|
|
|
|
* Description: Simple Stats, in the absence of good ones.
|
|
|
|
*/
|
2013-10-17 14:46:55 +02:00
|
|
|
var _ = require('underscore')._,
|
|
|
|
async = require('async');
|
2013-10-16 17:12:05 +02:00
|
|
|
|
|
|
|
var sstats = function(dbot) {
|
2013-10-17 05:06:17 +02:00
|
|
|
if(!_.has(dbot.db, 'ssinception')) dbot.db.ssinception = new Date().getTime();
|
|
|
|
|
2013-10-17 13:42:05 +02:00
|
|
|
// This needs to be somewhere else
|
2013-10-17 05:59:29 +02:00
|
|
|
this.isUpperCase = function(word) {
|
|
|
|
return _.all(word.split(''), function(c) {
|
|
|
|
return c == c.toUpperCase();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-10-17 13:42:05 +02:00
|
|
|
this.internalAPI = {
|
|
|
|
// I'm not a huge fan of this but it's better than all the code
|
|
|
|
// repetition
|
|
|
|
'highscore': function(key, property, callback) {
|
|
|
|
var pList = {},
|
|
|
|
pPointer = property.split('.');
|
|
|
|
|
|
|
|
this.db.scan(key, function(item) {
|
|
|
|
var id = item.id;
|
|
|
|
for(var i=0;i<pPointer.length;i++) {
|
|
|
|
if(_.has(item, pPointer[i])) {
|
|
|
|
item = item[pPointer[i]];
|
|
|
|
} else {
|
|
|
|
item = null; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(item) {
|
|
|
|
pList[id] = item;
|
|
|
|
}
|
|
|
|
}, function() {
|
|
|
|
var pCounts = _.chain(pList)
|
|
|
|
.pairs()
|
|
|
|
.sortBy(function(p) { return p[1]; })
|
|
|
|
.reverse()
|
|
|
|
.first(10)
|
|
|
|
.value();
|
|
|
|
|
2013-10-17 14:46:55 +02:00
|
|
|
async.eachSeries(pCounts, function(pCount, next) {
|
|
|
|
dbot.api.users.getUser(pCount[0], function(user) {
|
|
|
|
pCount[0] = user.primaryNick; next();
|
|
|
|
});
|
|
|
|
}, function() {
|
|
|
|
callback(pCounts);
|
|
|
|
}.bind(this));
|
2013-10-17 13:42:05 +02:00
|
|
|
});
|
|
|
|
}.bind(this),
|
|
|
|
|
2013-10-17 14:46:55 +02:00
|
|
|
'channelHighscore': function(key, server, channel, property, callback) {
|
|
|
|
dbot.api.users.resolveChannel(server, channel, function(channel) {
|
|
|
|
if(channel) {
|
|
|
|
var newProperty = 'channels.' + channel.id + '.' + property;
|
|
|
|
this.internalAPI.highscore(key, newProperty, callback);
|
|
|
|
} else {
|
|
|
|
callback(null);
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
}.bind(this),
|
|
|
|
|
2013-10-17 13:42:05 +02:00
|
|
|
'formatHighscore': function(string, pCounts) {
|
|
|
|
var output = string;
|
|
|
|
for(var i=0;i<pCounts.length;i++) {
|
|
|
|
output += pCounts[i][0] + " (" + pCounts[i][1] + "), ";
|
|
|
|
}
|
|
|
|
return output.slice(0, -2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-16 17:12:05 +02:00
|
|
|
this.listener = function(event) {
|
|
|
|
event.cStats.lines++;
|
|
|
|
event.uStats.lines++;
|
2013-10-17 05:59:29 +02:00
|
|
|
|
2013-10-20 20:53:08 +02:00
|
|
|
var message = event.message.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, "");
|
|
|
|
var words = message.split(' '),
|
2013-10-17 05:59:29 +02:00
|
|
|
wCount = words.length,
|
|
|
|
capitals = 0,
|
|
|
|
curses = 0;
|
|
|
|
_.each(words, function(word) {
|
|
|
|
if(this.isUpperCase(word)) capitals++;
|
2013-10-17 06:48:45 +02:00
|
|
|
if(_.any(this.config.curses, function(curse) { return word.toLowerCase().indexOf(curse) != -1; })) {
|
|
|
|
curses++;
|
|
|
|
}
|
2013-10-17 05:59:29 +02:00
|
|
|
}, this);
|
|
|
|
|
|
|
|
event.uStats.words += wCount;
|
|
|
|
event.uStats.capitals += capitals;
|
|
|
|
event.uStats.curses += curses;
|
2013-10-17 16:31:05 +02:00
|
|
|
event.uStats.last = new Date().getTime();
|
2013-10-17 05:59:29 +02:00
|
|
|
|
|
|
|
event.cStats.words += wCount;
|
|
|
|
event.cStats.capitals += capitals;
|
|
|
|
event.cStats.curses += curses;
|
|
|
|
|
2013-10-16 17:12:05 +02:00
|
|
|
if(!_.has(event.uStats.channels, event.rChannel.id)) {
|
|
|
|
event.uStats.channels[event.rChannel.id] = {
|
2013-10-17 05:59:29 +02:00
|
|
|
'lines': 1,
|
|
|
|
'words': wCount,
|
|
|
|
'capitals': capitals,
|
|
|
|
'curses': curses
|
2013-10-16 17:12:05 +02:00
|
|
|
};
|
|
|
|
} else {
|
|
|
|
event.uStats.channels[event.rChannel.id].lines++;
|
2013-10-17 05:59:29 +02:00
|
|
|
event.uStats.channels[event.rChannel.id].words += wCount;
|
|
|
|
event.uStats.channels[event.rChannel.id].capitals += capitals;
|
|
|
|
event.uStats.channels[event.rChannel.id].curses += curses;
|
2013-10-16 17:12:05 +02:00
|
|
|
}
|
2013-10-20 20:29:54 +02:00
|
|
|
|
|
|
|
// Look for tracked words.
|
2013-10-20 20:53:27 +02:00
|
|
|
if(event.message.charAt(0) != '~') {
|
2013-10-20 20:53:08 +02:00
|
|
|
var wMap = {}; // Why reduce isn't working idk
|
|
|
|
_.each(words, function(word) {
|
|
|
|
word = word.toLowerCase();
|
|
|
|
if(!_.has(wMap, word)) wMap[word] = 0;
|
|
|
|
wMap[word]++;
|
|
|
|
});
|
|
|
|
_.each(wMap, function(count, word) {
|
|
|
|
this.api.getTrackedWord(word, function(tWord) {
|
|
|
|
if(tWord) {
|
|
|
|
tWord.total += count;
|
|
|
|
if(!_.has(tWord.channels, event.rChannel.id)) tWord.channels[event.rChannel.id] = 0;
|
|
|
|
if(!_.has(tWord.users, event.rUser.id)) tWord.users[event.rUser.id] = 0;
|
|
|
|
tWord.channels[event.rChannel.id] += count;
|
|
|
|
tWord.users[event.rUser.id] += count;
|
|
|
|
this.db.save('tracked_words', word, tWord, function() {});
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
}, this);
|
|
|
|
}
|
2013-10-20 20:29:54 +02:00
|
|
|
|
2013-10-16 17:12:05 +02:00
|
|
|
this.db.save('channel_stats', event.cStats.id, event.cStats, function() {});
|
|
|
|
this.db.save('user_stats', event.uStats.id, event.uStats, function() {});
|
|
|
|
}.bind(this);
|
|
|
|
this.on = 'PRIVMSG';
|
|
|
|
|
|
|
|
this.onLoad = function() {
|
|
|
|
// Preload user stats
|
|
|
|
dbot.instance.addPreEmitHook(function(event, callback) {
|
|
|
|
if(!event.rUser) return callback();
|
|
|
|
this.api.getUserStats(event.rUser.id, function(uStats) {
|
|
|
|
if(uStats) {
|
|
|
|
event.uStats = uStats;
|
|
|
|
callback();
|
|
|
|
} else {
|
|
|
|
this.api.createUserStats(event.rUser.id, function(uStats) {
|
|
|
|
event.uStats = uStats;
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
|
|
|
|
|
|
|
// Preload channel stats
|
|
|
|
dbot.instance.addPreEmitHook(function(event, callback) {
|
|
|
|
if(!event.rChannel) return callback();
|
|
|
|
this.api.getChannelStats(event.rChannel.id, function(cStats) {
|
|
|
|
if(cStats) {
|
|
|
|
event.cStats = cStats;
|
|
|
|
callback();
|
|
|
|
} else {
|
|
|
|
this.api.createChannelStats(event.rChannel.id, function(cStats) {
|
|
|
|
event.cStats = cStats;
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
2013-10-22 16:25:53 +02:00
|
|
|
|
|
|
|
dbot.api.event.addHook('~mergeusers', function(server, oldUser, newUser) {
|
|
|
|
this.api.getUserStats(oldUser.id, function(ouStats) {
|
|
|
|
this.api.getUserStats(newUser.id, function(nuStats) {
|
|
|
|
_.each(ouStats, function(stat, key) {
|
2013-10-23 13:46:07 +02:00
|
|
|
if(key != 'creation' && key != 'id') {
|
|
|
|
if(_.isObject(stat)) {
|
|
|
|
_.each(ouStats[key], function(stat, sKey) {
|
2013-10-24 18:00:15 +02:00
|
|
|
_.each(ouStats[key][sKey], function(stat, ssKey) {
|
2014-07-08 13:33:56 +02:00
|
|
|
if(_.has(nuStats[key][sKey], ssKey)) {
|
2013-10-24 18:00:15 +02:00
|
|
|
nuStats[key][sKey][ssKey] += stat;
|
2014-07-08 13:33:56 +02:00
|
|
|
}
|
2013-10-24 18:00:15 +02:00
|
|
|
});
|
2013-10-23 13:46:07 +02:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
nuStats[key] += stat;
|
|
|
|
}
|
2013-10-22 16:25:53 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
this.db.del('user_stats', oldUser.id, function() {});
|
2013-10-22 16:45:21 +02:00
|
|
|
this.db.save('user_stats', newUser.id, nuStats, function() {});
|
2013-10-22 16:35:37 +02:00
|
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
2013-10-22 16:25:53 +02:00
|
|
|
|
|
|
|
this.db.scan('tracked_words', function(tWord) {
|
|
|
|
if(_.has(tWord.users, oldUser.id)) {
|
|
|
|
if(_.has(tWord.users, newUser.id)) {
|
|
|
|
tWord.users[newUser.id] += tWord.users[oldUser.id];
|
|
|
|
} else {
|
|
|
|
tWord.users[newUser.id] = tWord.users[oldUser.id];
|
|
|
|
}
|
|
|
|
delete tWord.users[oldUser.id];
|
|
|
|
this.db.save('tracked_words', tWord.word, tWord, function() {});
|
|
|
|
}
|
2013-10-22 16:43:18 +02:00
|
|
|
}.bind(this), function() {});
|
2013-10-22 16:25:53 +02:00
|
|
|
}.bind(this));
|
2013-10-16 17:12:05 +02:00
|
|
|
}.bind(this);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.fetch = function(dbot) {
|
|
|
|
return new sstats(dbot);
|
|
|
|
};
|