dbot/modules/sstats/sstats.js
2015-10-13 05:36:49 +00:00

220 lines
8.0 KiB
JavaScript

/**
* Module Name: sstats
* Description: Simple Stats, in the absence of good ones.
*/
var _ = require('underscore')._,
async = require('async');
var sstats = function(dbot) {
if(!_.has(dbot.db, 'ssinception')) dbot.db.ssinception = new Date().getTime();
// This needs to be somewhere else
this.isUpperCase = function(word) {
return _.all(word.split(''), function(c) {
return c == c.toUpperCase();
});
};
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;
if(!_.isArray(pPointer)) {
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();
async.eachSeries(pCounts, function(pCount, next) {
dbot.api.users.getUser(pCount[0], function(err, user) {
if(user) {
pCount[0] = user.primaryNick;
}
next();
});
}, function() {
callback(pCounts);
}.bind(this));
});
}.bind(this),
'channelHighscore': function(key, server, channel, property, callback) {
var cId = channel + '.' + server;
var newProperty = ['channels', cId, property];
this.internalAPI.highscore(key, newProperty, callback);
}.bind(this),
'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);
}
};
this.listener = function(event) {
if(event.channel == event.user) return; // ignore PMs
event.cStats.lines++;
event.uStats.lines++;
var message = event.message.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, "");
var cId = event.channel + '.' + event.server;
var words = message.split(' '),
wCount = words.length,
capitals = 0,
curses = 0;
_.each(words, function(word) {
if(this.isUpperCase(word)) capitals++;
if(_.any(this.config.curses, function(curse) { return word.toLowerCase().indexOf(curse) != -1; })) {
curses++;
}
}, this);
event.uStats.words += wCount;
event.uStats.capitals += capitals;
event.uStats.curses += curses;
event.uStats.last = new Date().getTime();
event.cStats.words += wCount;
event.cStats.capitals += capitals;
event.cStats.curses += curses;
if(!_.has(event.uStats.channels, cId)) {
event.uStats.channels[cId] = {
'lines': 1,
'words': wCount,
'capitals': capitals,
'curses': curses
};
} else {
event.uStats.channels[cId].lines++;
event.uStats.channels[cId].words += wCount;
event.uStats.channels[cId].capitals += capitals;
event.uStats.channels[cId].curses += curses;
}
// Look for tracked words.
if(event.message.charAt(0) != '~') {
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, cId)) tWord.channels[cId] = 0;
if(!_.has(tWord.users, event.rUser.id)) tWord.users[event.rUser.id] = 0;
tWord.channels[cId] += count;
tWord.users[event.rUser.id] += count;
this.db.save('tracked_words', word, tWord, function() {});
}
}.bind(this));
}, this);
}
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.channel) return callback();
var id = event.channel + '.' + event.server;
this.api.getChannelStats(id, function(cStats) {
if(cStats) {
event.cStats = cStats;
callback();
} else {
this.api.createChannelStats(id, function(cStats) {
event.cStats = cStats;
callback();
});
}
}.bind(this));
}.bind(this));
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) {
if(key != 'creation' && key != 'id') {
if(_.isObject(stat)) {
_.each(ouStats[key], function(stat, sKey) {
_.each(ouStats[key][sKey], function(stat, ssKey) {
if(_.has(nuStats[key][sKey], ssKey)) {
nuStats[key][sKey][ssKey] += stat;
}
});
});
} else {
nuStats[key] += stat;
}
}
});
this.db.del('user_stats', oldUser.id, function() {});
this.db.save('user_stats', newUser.id, nuStats, function() {});
}.bind(this));
}.bind(this));
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() {});
}
}.bind(this), function() {});
}.bind(this));
}.bind(this);
};
exports.fetch = function(dbot) {
return new sstats(dbot);
};