Merge git://github.com/reality/depressionbot

This commit is contained in:
Thomas Menari 2013-01-24 22:38:34 +00:00
commit 6830ed73e1
12 changed files with 190 additions and 40 deletions

30
modules/event/README.md Normal file
View File

@ -0,0 +1,30 @@
## Event
Emit events for whatever you want man idk.
### Description
This is a library module designed for other modules to use to emit various
events at any point, and also to attach functions to said events. These are
similar to command hooks, however their advantage is that they may be called
anywhere in your code; they are particularly useful when you want to attach a
callback to a listener.
### API
#### addHook(eventName, callback)
This function will set a given callback to be executed every time the
emit API function is executed with the given event name. The arguments of your
callback are defined as an array in the emit call.
The best place to add hooks to commands is in the 'onLoad' function of your
module, as this ensures it will be run while all other modules are loaded so
nothing will be missed.
#### emit(eventName, [ arguments ])
This function executes all of the functions associated with the given eventName,
passing your given array of arguments.
For example, to emit an event when you detect a nick change:
dbot.api.event.emit('nick_changed', [ event.server, newNick ]);

28
modules/event/event.js Normal file
View File

@ -0,0 +1,28 @@
/**
* Module Name: event
* Description: Allow other modules to emit events and that
*/
var _ = require('underscore')._;
var event = function(dbot) {
this.dbot = dbot;
this.hooks = {};
this.api = {
'addHook': function(eventName, callback) {
if(!_.has(this.hooks, eventName)) this.hooks[eventName] = [];
this.hooks[eventName].push(callback);
},
'emit': function(eventName, args) {
if(_.has(this.hooks, eventName)) {
_.each(this.hooks[eventName], function(callback) {
callback.apply(callback.module, args);
});
}
}
};
}
exports.fetch = function(dbot) {
return new event(dbot);
};

View File

@ -34,17 +34,19 @@ var link = function(dbot) {
},
'~ud': function(event) {
var reqUrl = 'http://api.urbandictionary.com/v0/define?term=' + event.params[1];
var query = event.input[1];
var reqUrl = 'http://api.urbandictionary.com/v0/define?term=' + encodeURI(query);
request(reqUrl, function(error, response, body) {
var result = JSON.parse(body);
if(_.has(result, 'result_type') && result.result_type != 'no_results') {
event.reply(event.params[1] + ': ' + result.list[0].definition);
event.reply(query + ': ' + result.list[0].definition.split('\n')[0]);
} else {
event.reply(event.user + ': No definition found.');
}
});
}
};
commands['~ud'].regex = [/~ud (.+)/, 2];
this.commands = commands;
this.listener = function(event) {

@ -1 +1 @@
Subproject commit 616cc9f8f23afa6941c1c130626aeb851716c5aa
Subproject commit 33886df41d84c160270b581f072e710e4c9d00e8

View File

@ -46,3 +46,11 @@ Return a list of aliases for a given primary user.
#### isOnline(server, user, channel, useLowerCase)
Return whether a user is online in a given channel on the given server.
### Events
#### nick_changed(server, newNick)
This is executed when a new alias is added for a user.
#### new_user(server, nick)
This is executed when a new primary user is added to the known users DB.

View File

@ -1,5 +1,6 @@
{
"ignorable": false,
"dependencies": [ "command", "event" ],
"dbKeys": [ "knownUsers" ],
"help": "https://github.com/reality/depressionbot/blob/master/modules/users/README.md"
}

View File

@ -26,8 +26,15 @@ var pages = function(dbot) {
if(connections.hasOwnProperty(connection) &&
connections[connection].channels.hasOwnProperty(channel)) {
var chanData = dbot.api.stats.getChanStats(connection, channel, ["freq"]);
var chanFreq = [];
for(var i=0; i <= 6; i++){
for(var j=0; j <= 23; j++){
chanFreq.push(chanData.fields.freq.raw[i][j]);
}
}
var userData = { "active": [], "inactive": [], "offline": []};
var reply = dbot.api.stats.getChanUsersStats(connection, channel, [
"lines", "words", "lincent", "wpl", "in_mentions"
]);
@ -60,8 +67,14 @@ var pages = function(dbot) {
var userDataSorted = (userData.active.concat(userData.inactive)).concat(userData.offline);
res.render('users', { 'name': dbot.config.name, 'connection': connection,
'channel': channel, 'nicks': userDataSorted });
res.render('users', {
'name': dbot.config.name,
'connection': connection,
'channel': channel,
'userStats': userDataSorted,
'chanFreq': chanFreq,
'chanFreqLen': chanFreq.length });
} else {
res.render_core('error', { 'name': dbot.config.name, 'message': 'No such connection or channel.' });
}

View File

@ -48,6 +48,7 @@ var users = function(dbot) {
nick = this.api.resolveUser(event.server, nick);
} else {
knownUsers.users.push(nick);
dbot.api.emit('new_user', [ event.server, nick ]);
}
if(!_.include(channelUsers, nick)) {
@ -57,7 +58,7 @@ var users = function(dbot) {
var newNick = event.params.substr(1);
if(!this.api.isKnownUser(newNick)) {
knownUsers.aliases[newNick] = this.api.resolveUser(event.server, event.user);
if(_.has(dbot.modules, 'stats')) dbot.api.stats.renameStats(event.server, newNick);
dbot.api.event.emit('nick_change', [ event.server, newNick ]);
}
}
}.bind(this);
@ -78,6 +79,7 @@ var users = function(dbot) {
nick = this.api.resolveUser(event.server, nick);
} else {
knownUsers.users.push(nick);
dbot.api.emit('new_user', [ event.server, nick ]);
}
if(!_.include(channelUsers, nick)) {
channelUsers.push(nick);

67
run.js
View File

@ -5,32 +5,36 @@ var fs = require('fs'),
require('./snippets');
var DBot = function(timers) {
// Load DB
var rawDB;
try {
var rawDB = fs.readFileSync('db.json', 'utf-8');
} catch(err) {
this.db = {}; // If no db file, make empty one
/*** Load the DB ***/
if(fs.existsSync('db.json')) {
try {
this.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
} catch(err) {
console.log('Error loading db.json. Stopping: ' + err);
process.exit();
}
} else {
this.db = {};
}
try {
if(!this.db) { // If it wasn't empty
this.db = JSON.parse(rawDB);
}
if(!_.has(this.db, 'config')) {
this.db.config = {};
}
} catch(err) {
console.log('Syntax error in db.json. Stopping: ' + err);
if(!_.has(this.db, 'config')) {
this.db.config = {};
}
/*** Load the Config ***/
if(!fs.existsSync('config.json')) {
console.log('Error: config.json file does not exist. Stopping');
process.exit();
}
// Load config
this.config = _.clone(this.db.config);
try {
_.defaults(this.config, JSON.parse(fs.readFileSync('config.json', 'utf-8')));
} catch(err) {
console.log('Config file is invalid. Stopping');
console.log('Config file is invalid. Stopping: ' + err);
process.exit();
}
@ -44,7 +48,8 @@ var DBot = function(timers) {
// Load missing config directives from sample file
_.defaults(this.config, defaultConfig);
// Load Strings file
/*** Load main strings ***/
try {
this.strings = JSON.parse(fs.readFileSync('strings.json', 'utf-8'));
} catch(err) {
@ -220,13 +225,17 @@ DBot.prototype.reloadModules = function() {
// Load the module data
_.each([ 'commands', 'pages', 'api' ], function(property) {
var propertyObj = {};
try {
var propertyKey = require.resolve(moduleDir + property);
if(propertyKey) delete require.cache[propertyKey];
propertyObj = require(moduleDir + property).fetch(this);
} catch(err) {
console.log('Module error (' + module.name + ') in ' + property + ': ' + err);
}
if(fs.existsSync(moduleDir + property + '.js')) {
try {
var propertyKey = require.resolve(moduleDir + property);
if(propertyKey) delete require.cache[propertyKey];
propertyObj = require(moduleDir + property).fetch(this);
} catch(err) {
this.status[name] = 'Error loading ' + propertyKey + ': ' + err + ' - ' + err.stack.split('\n')[1].trim();
console.log('Module error (' + module.name + ') in ' + property + ': ' + err);
}
}
if(!_.has(module, property)) module[property] = {};
_.extend(module[property], propertyObj);
@ -286,7 +295,11 @@ DBot.prototype.reloadModules = function() {
_.each(this.modules, function(module, name) {
if(module.onLoad) {
module.onLoad();
try {
module.onLoad();
} catch(err) {
this.status[name] = 'Error in onLoad: ' + err + ' ' + err.stack.split('\n')[1].trim();
}
}
}, this);

View File

@ -43,7 +43,10 @@ block content
h1
#{primary}
small
"#{profile.tagline}"
if(profile.tagline)
"#{profile.tagline}"
div#backlink
a(href='/profile/'+connection) &laquo; Profiles
div.row.profile_row#profile_data
div.span3
@ -92,6 +95,3 @@ block content
#{chan.fields.wpl.data}
td
#{chan.fields.in_mentions.data}
ul.pager
li.previous
a(href='/profile/'+connection) &larr; Back to #{connection}.

View File

@ -14,6 +14,9 @@ block content
div.page-header.profile_page-header
h1
#{connection}
div#backlink
a(href='../connections') &laquo; Connections
ul.thumbnails
each profile, key in profiles
if profile.hasOwnProperty('profile') && profile.profile.avatar

View File

@ -2,9 +2,50 @@ extends ../layout
block content
script(type="text/javascript", src="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js")
style
.chart rect {
fill: steelblue;
}
.chart rect:hover {
fill: #000;
}
script
$(document).ready(function(){
// d3.js Graph
var w = 5.595;
var h = 120;
var x = d3.scale.linear()
.domain([0,1])
.range([0,w]);
var y = d3.scale.linear()
.domain([0,100])
.rangeRound([0,h]);
var chart = d3.select($("#chanFreqChart")[0]).append("svg")
.attr("class", "chart")
.attr("width", w * #{chanFreqLen} - 1)
.attr("height", h);
chart.selectAll("rect")
.data([#{chanFreq}])
.enter().append("rect")
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d); })
.attr("title", function(d){ return y(d); });
chart.append("line")
.attr("x1", 0)
.attr("x2", w * #{chanFreqLen})
.attr("y1", h - .5)
.attr("y2", h - .5)
.style("stroke", "#000");
// Allowing forcing of string stats data to sort as numeric
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"forcenum-pre": function ( a ) {
@ -22,6 +63,8 @@ block content
} );
$('.tip').tooltip();
$('rect').tooltip({
});
$('.data').dataTable({
"aoColumnDefs": [
{ "aDataSort": [ 1, 0 ], "asSorting": [ "asc" ], "aTargets": [ 0 ] },
@ -40,9 +83,16 @@ block content
});
});
h3 Users of #{channel} on #{connection}
div.page-header.profile_page-header
h1
#{channel}
small
#{connection}
div#backlink
a(href='/channels/'+connection) &laquo; Channel List
div#row
div.barchart#chanFreqChart
hr
div#row
table.table.table-hover.data
thead
@ -55,7 +105,7 @@ block content
th Verbosity
th Mentions
tbody
-each nick in nicks
-each nick in userStats
tr
td
a(href='/profile/'+connection+'/'+nick.primary)