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) { '~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) { request(reqUrl, function(error, response, body) {
var result = JSON.parse(body); var result = JSON.parse(body);
if(_.has(result, 'result_type') && result.result_type != 'no_results') { 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 { } else {
event.reply(event.user + ': No definition found.'); event.reply(event.user + ': No definition found.');
} }
}); });
} }
}; };
commands['~ud'].regex = [/~ud (.+)/, 2];
this.commands = commands; this.commands = commands;
this.listener = function(event) { 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) #### isOnline(server, user, channel, useLowerCase)
Return whether a user is online in a given channel on the given server. 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, "ignorable": false,
"dependencies": [ "command", "event" ],
"dbKeys": [ "knownUsers" ], "dbKeys": [ "knownUsers" ],
"help": "https://github.com/reality/depressionbot/blob/master/modules/users/README.md" "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) && if(connections.hasOwnProperty(connection) &&
connections[connection].channels.hasOwnProperty(channel)) { connections[connection].channels.hasOwnProperty(channel)) {
var userData = { "active": [], "inactive": [], "offline": []}; 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, [ var reply = dbot.api.stats.getChanUsersStats(connection, channel, [
"lines", "words", "lincent", "wpl", "in_mentions" "lines", "words", "lincent", "wpl", "in_mentions"
]); ]);
@ -60,8 +67,14 @@ var pages = function(dbot) {
var userDataSorted = (userData.active.concat(userData.inactive)).concat(userData.offline); var userDataSorted = (userData.active.concat(userData.inactive)).concat(userData.offline);
res.render('users', { 'name': dbot.config.name, 'connection': connection, res.render('users', {
'channel': channel, 'nicks': userDataSorted }); 'name': dbot.config.name,
'connection': connection,
'channel': channel,
'userStats': userDataSorted,
'chanFreq': chanFreq,
'chanFreqLen': chanFreq.length });
} else { } else {
res.render_core('error', { 'name': dbot.config.name, 'message': 'No such connection or channel.' }); 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); nick = this.api.resolveUser(event.server, nick);
} else { } else {
knownUsers.users.push(nick); knownUsers.users.push(nick);
dbot.api.emit('new_user', [ event.server, nick ]);
} }
if(!_.include(channelUsers, nick)) { if(!_.include(channelUsers, nick)) {
@ -57,7 +58,7 @@ var users = function(dbot) {
var newNick = event.params.substr(1); var newNick = event.params.substr(1);
if(!this.api.isKnownUser(newNick)) { if(!this.api.isKnownUser(newNick)) {
knownUsers.aliases[newNick] = this.api.resolveUser(event.server, event.user); 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); }.bind(this);
@ -78,6 +79,7 @@ var users = function(dbot) {
nick = this.api.resolveUser(event.server, nick); nick = this.api.resolveUser(event.server, nick);
} else { } else {
knownUsers.users.push(nick); knownUsers.users.push(nick);
dbot.api.emit('new_user', [ event.server, nick ]);
} }
if(!_.include(channelUsers, nick)) { if(!_.include(channelUsers, nick)) {
channelUsers.push(nick); channelUsers.push(nick);

39
run.js
View File

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

View File

@ -43,7 +43,10 @@ block content
h1 h1
#{primary} #{primary}
small small
if(profile.tagline)
"#{profile.tagline}" "#{profile.tagline}"
div#backlink
a(href='/profile/'+connection) &laquo; Profiles
div.row.profile_row#profile_data div.row.profile_row#profile_data
div.span3 div.span3
@ -92,6 +95,3 @@ block content
#{chan.fields.wpl.data} #{chan.fields.wpl.data}
td td
#{chan.fields.in_mentions.data} #{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 div.page-header.profile_page-header
h1 h1
#{connection} #{connection}
div#backlink
a(href='../connections') &laquo; Connections
ul.thumbnails ul.thumbnails
each profile, key in profiles each profile, key in profiles
if profile.hasOwnProperty('profile') && profile.profile.avatar if profile.hasOwnProperty('profile') && profile.profile.avatar

View File

@ -2,9 +2,50 @@ extends ../layout
block content block content
script(type="text/javascript", src="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js") 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 script
$(document).ready(function(){ $(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 // Allowing forcing of string stats data to sort as numeric
jQuery.extend( jQuery.fn.dataTableExt.oSort, { jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"forcenum-pre": function ( a ) { "forcenum-pre": function ( a ) {
@ -22,6 +63,8 @@ block content
} ); } );
$('.tip').tooltip(); $('.tip').tooltip();
$('rect').tooltip({
});
$('.data').dataTable({ $('.data').dataTable({
"aoColumnDefs": [ "aoColumnDefs": [
{ "aDataSort": [ 1, 0 ], "asSorting": [ "asc" ], "aTargets": [ 0 ] }, { "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 div#backlink
a(href='/channels/'+connection) &laquo; Channel List a(href='/channels/'+connection) &laquo; Channel List
div#row
div.barchart#chanFreqChart
hr
div#row div#row
table.table.table-hover.data table.table.table-hover.data
thead thead
@ -55,7 +105,7 @@ block content
th Verbosity th Verbosity
th Mentions th Mentions
tbody tbody
-each nick in nicks -each nick in userStats
tr tr
td td
a(href='/profile/'+connection+'/'+nick.primary) a(href='/profile/'+connection+'/'+nick.primary)