3
0
mirror of https://github.com/reality/dbot.git synced 2024-11-27 22:39:26 +01:00

Merge branch 'master' of github.com:reality/depressionbot

This commit is contained in:
Luke Slater 2012-03-19 18:46:48 +00:00
commit 8c81842643
16 changed files with 536 additions and 85 deletions

118
README.md Normal file
View File

@ -0,0 +1,118 @@
# Depressionbot IRC Bot
## Introduction
Depressionbot is an IRC bot which aims to be the fanciest IRC bot around - On
the general standard of software fanciness, dbot is rated as being '75% the same
as bathing in fine, fine grape juice.'
Please note that this documentation is not complete and is a work in progress,
given I started it rather a long time after I began development of the project.
Please don't judge me too harshly for this as I am, in fact, mildly allergic to
writing documentation.
Requirements:
- Node JS
- [JSBot](http://github.com/reality/JSBot "JSBot"), the Javascript library I
wrote to handle the IRC protocol and event listeners etc.
- Various modules have their own requirements also.
## Modules:
### Command
This handles the command execution logic for DBot.
1. Does the input match a command key in *dbot.commands* ?
2. Is there a quote category which matches the first part of the input
(*~category*)?
3. Is there a command name similar to to the first part of the input (*~name*)
in *dbot.commands*?
This is the only module which is force loaded, even if it's not in the
configuration.
### Quotes
This is the original reason that DBot was created, stores and displays quotes.
Commands:
- _~q category_ - Display a random quote from a given category.
- _~qadd category=newquote_ - Add a new quote to the database.
- _~qstats_ - Show a list of the biggest quote categories.
- _~qsearch category needle_ - Search for a quote in a given category.
- _~rmlast [category]_ - Remove the last quote added to a given category, or the
last quote added.
- _~rm category quote_ - Remove a given quote from the given category.
- _~qcount category_ - Show the number of quotes stored in the given category.
- _~rq_ - Show a random quote from a random category.
- _~d_ - Show a quote from the category which matches the bot's name.
- _~link category_ - Create a link to the page on the web interface which displays the
given category's quotes.
- _~qprune_ - Delete empty quote categories.
Unfortunately, this module is fairly highly coupled with certain other areas of
the program. I am working on this, but note, for example, that one can still
access quotes with the *~category* syntax even if the quotes module isn't
loaded.
### Admin
Various administration functionality such as banning users, hot-reloading the
code and ordering him to talk. Note that commands added here are handled with
their own listener, rather than being part of the command logic which is handled
by the Command module. Functionality in this module can be slightly unsafe as
not much error checking on the input is performed.
TODO: Add summaries for each command in this module.
### Spelling
Will attempt to correct a users' spelling by using the levenshtein distance
algorithm. One corrects the spelling of their previous message by simply posting
a message with their correction and an asterisk:
> user: I am a tutrle.
> user: *turtle
user meant: I am a turtle.
The regular expression for this module also accepts two asterisks at the
beginning of the correction, or at the end; it also accepts several words as the
correction and deals with these fairly intelligently. Users may also attempt
to correct another users like so:
> userone: I am a tutrle.
> usertwo: userone: *turtle
> usertwo thinks userone meant: I am a turtle.
### JS
This module provides two commands which allow the execution of Javascript code.
For regular users, there is the *~js* command, which is completely sandboxed,
but can still be used for calculation and the like.
> ~js Array(16).join('wat'-1) + " Batman!";
'NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!'
This feature is fairly safe as the user doesn't have access to anything
dangerous, and is safe from infinite loops or locking DBot up because the code
which is run is killed if it does not finish within a short amount of time.
For administrators, the incredibly useful *~ajs* command is also available. The
input for this command is simply 'eval'-ed and therefore has full access to
DBot's memory. Of course, this is incredibly unsafe, but I find it rather fun;
remember to only give extremely trusted friends administrator access to DBot, as
there's nothing to stop them wiping the database or something similar - if
you're worried about that kind of thing, do not load this module.
However, it's useful for many things, such as administrative activity for
which there isn't a command in the admin module. For example, you could hot-add
a new administrator like this:
> ~ajs dbot.admin.push('batman');
2
You can also use this for debugging, or even adding new commands while DBot is
running.

View File

@ -1,4 +1,6 @@
var fs = require('fs');
var sys = require('sys')
var exec = require('child_process').exec;
var adminCommands = function(dbot) {
var dbot = dbot;
@ -6,17 +8,28 @@ var adminCommands = function(dbot) {
var commands = {
'join': function(data, params) {
dbot.instance.join(params[1]);
dbot.say(dbot.admin, 'Joined ' + params[1]);
dbot.say(data.channel, 'Joined ' + params[1]);
},
'opme': function(data, params) {
dbot.instance.send('MODE ' + params[1] + ' +o ', dbot.admin);
dbot.instance.send('MODE ' + params[1] + ' +o ', data.user);
},
'part': function(data, params) {
dbot.instance.part(params[1]);
},
// Do a git pull and reload
'greload': function(data, params) {
var child;
child = exec("git pull", function (error, stdout, stderr) {
console.log(stderr);
dbot.say(data.channel, 'Git pulled that shit.');
commands.reload(data, params);
}.bind(this));
},
'reload': function(data, params) {
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
dbot.reloadModules();
@ -24,11 +37,25 @@ var adminCommands = function(dbot) {
},
'say': function(data, params) {
var c = params[1];
if (params[1] === "@") {
var c = data.channel;
} else {
var c = params[1];
}
var m = params.slice(2).join(' ');
dbot.say(c, m);
},
'act': function(data, params) {
if (params[1] === "@") {
var c = data.channel;
} else {
var c = params[1];
}
var m = params.slice(2).join(' ');
dbot.act(c, m);
},
'load': function(data, params) {
dbot.moduleNames.push(params[1]);
dbot.reloadModules();
@ -92,7 +119,7 @@ var adminCommands = function(dbot) {
if(data.channel == dbot.name) data.channel = data.user;
params = data.message.split(' ');
if(commands.hasOwnProperty(params[0]) && data.user == dbot.admin) {
if(commands.hasOwnProperty(params[0]) && dbot.admin.include(data.user)) {
commands[params[0]](data, params);
dbot.save();
}

105
modules/command.js Normal file
View File

@ -0,0 +1,105 @@
// Module which handles the command execution syntax for DBot. Not much is going
// to work without this.
var command = function(dbot) {
var dbot = dbot;
var ignoreCommands = function (data, params) {
if(data.channel == dbot.name) data.channel = data.user;
var targetCommand = params[1];
var ignoreMins = parseFloat(params[2]);
if(!dbot.sessionData.hasOwnProperty("ignoreCommands")) {
dbot.sessionData.ignoreCommands = {};
}
if(!dbot.sessionData.ignoreCommands.hasOwnProperty(targetCommand)) {
dbot.sessionData.ignoreCommands[targetCommand] = [];
}
if(dbot.sessionData.ignoreCommands[targetCommand].include(data.channel)) {
dbot.say(data.channel, "Already ignoring '" + targetCommand + "' in '" + data.channel + "'.");
} else {
dbot.sessionData.ignoreCommands[targetCommand].push(data.channel);
dbot.timers.addOnceTimer(ignoreMins * 60 * 1000, function() {
dbot.sessionData.ignoreCommands[targetCommand].splice(dbot.sessionData.ignoreCommands[targetCommand].indexOf(data.channel), 1);
dbot.say(data.channel, "No longer ignoring '" + targetCommand + "' in '" + data.channel + "'.");
});
dbot.say(data.channel, "Ignoring '" + targetCommand + "' in '" + data.channel + "' for the next " + ignoreMins + " minute" + (ignoreMins == 1 ? "" : "s") + ".");
}
};
return {
'onLoad': function() {
return {
'~ignore': ignoreCommands
};
},
'listener': function(data) {
var params = data.message.split(' ');
if(data.channel == dbot.name) data.channel = data.user;
var ignoringCommand = false;
if(dbot.sessionData.hasOwnProperty("ignoreCommands")) {
if(dbot.sessionData.ignoreCommands.hasOwnProperty(params[0])) {
if(dbot.sessionData.ignoreCommands[params[0]].include(data.channel)) {
ignoringCommand = true;
}
}
}
if(dbot.commands.hasOwnProperty(params[0])) {
if((dbot.db.bans.hasOwnProperty(params[0]) &&
dbot.db.bans[params[0]].include(data.user)) || dbot.db.bans['*'].include(data.user)) {
dbot.say(data.channel, data.user +
' is banned from using this command. Commence incineration.');
} else if(ignoringCommand) {
// do nothing, this stops us falling through to the non-command stuff
} else {
dbot.commands[params[0]](data, params);
dbot.save();
}
} else {
var q = data.message.valMatch(/^~([\d\w\s-]*)/, 2);
if(q) {
if(dbot.db.bans['*'].include(data.user)) {
dbot.say(data.channel, data.user +
' is banned from using this command. Commence incineration.');
} else {
q[1] = q[1].trim();
key = dbot.cleanNick(q[1])
if(dbot.db.quoteArrs.hasOwnProperty(key) && dbot.moduleNames.include('quotes')) {
var params = ['~q'];
key.split(' ').each((function(word) {
this.push(word);
}).bind(params));
data.message = params.join(' ');
dbot.commands[params[0]](data, params);
dbot.save();
} else {
// See if it's similar to anything
var winnerDistance = Infinity;
var winner = false;
for(var commandName in dbot.commands) {
var distance = String.prototype.distance(params[0], commandName);
if(distance < winnerDistance) {
winner = commandName;
winnerDistance = distance;
}
}
if(winnerDistance < 3) {
dbot.say(data.channel, 'Did you mean ' + winner + '? Learn to type, hippie!');
}
}
}
}
}
},
'on': 'PRIVMSG'
};
};
exports.fetch = function(dbot) {
return command(dbot);
};

97
modules/dice.js Normal file
View File

@ -0,0 +1,97 @@
var parseDiceSpec = function (specString) {
var rawSpec = specString.valMatch(/^([0-9]*)d(%|[0-9]*)(|[+-][0-9]+)$/i, 4);
if (rawSpec !== false) {
if (rawSpec[2] === "%") {
rawSpec[2] = 100;
}
return {
"count": parseInt(rawSpec[1] || 1),
"sides": parseInt(rawSpec[2] || 6),
"modifier": parseInt(rawSpec[3] || 0)
};
} else {
return false;
}
};
var normalizeDiceSpec = function (specString) {
var diceSpec = parseDiceSpec(specString);
if (diceSpec["count"] > 1) {
var count = diceSpec["count"];
} else {
var count = "";
}
if (diceSpec["sides"] === 100) {
var sides = "%";
} else {
var sides = diceSpec["sides"];
}
if (diceSpec["modifier"] > 0) {
var modifier = "+" + diceSpec["modifier"];
} else if (diceSpec["modifier"] < 0) {
var modifier = diceSpec["modifier"];
} else {
var modifier = "";
}
return (count + "d" + sides + modifier);
};
var dice = function(dbot) {
var commands = {
'~roll': function (data, params) {
var rolls = [];
if (params.length === 1) {
params.push("d6");
}
for (var i = 1; i < params.length; i++) {
var diceSpec = parseDiceSpec(params[i]);
if (diceSpec === false) {
rolls.push([params[i], false]);
} else {
rolls.push([normalizeDiceSpec(params[i]), [], diceSpec["modifier"]]);
for (var j = 0; j < diceSpec["count"] ; j++) {
rolls[rolls.length-1][1].push(Math.ceil(Math.random() * diceSpec["sides"]));
}
}
}
for (var i = 0; i < rolls.length; i++) {
if (rolls[i][1] === false) {
dbot.say(data.channel, rolls[i][0] + ": invalid dice spec");
} else {
if (rolls[i][1].length > 1) {
var total = " (total " + rolls[i][1].sum();
if (rolls[i][2] != 0) {
if (rolls[i][2] > 0) {
total += " + ";
} else {
total += " - ";
}
total += Math.abs(rolls[i][2]) + " -> " + (rolls[i][1].sum() + rolls[i][2]);
}
total += ")"
} else {
var total = "";
}
dbot.say(data.channel, rolls[i][0] + ": " + rolls[i][1].join(" ") + total);
}
}
}
};
return {
'onLoad': function() {
return commands;
}
};
}
exports.fetch = function(dbot) {
return dice(dbot);
};

View File

@ -26,7 +26,7 @@ var drama = function(dbot) {
var commands = {
'~train': function(data, params) {
if(data.user == dbot.admin || data.user == 'golem' || data.user == 'Sam') {
if(dbot.admin.include(data.user)) {
bayes.train(last[params[1]][params[2]], params[3]);
dbot.say(data.channel, 'Last thing ' + params[2] + ' said in ' +
params[1] + ' (' + last[params[1]][params[2]] + ') classified as \'' + params[3] + '\'');
@ -34,7 +34,7 @@ var drama = function(dbot) {
},
'~rtrain': function(data, params) {
if(data.user == dbot.admin || data.user == 'golem' || data.user == 'Sam') {
if(dbot.admin.include(data.user)) {
var category = params[1];
params.splice(0, 2);
var msg = params.join(' ');

View File

@ -15,8 +15,11 @@ var js = function(dbot) {
'~ajs': function(data, params) {
var q = data.message.valMatch(/^~ajs (.*)/, 2);
if(data.user == dbot.admin) {
dbot.say(data.channel, eval(q[1]));
if(dbot.admin.include(data.user) ) {
var ret = eval(q[1]);
if(ret != undefined) {
dbot.say(data.channel, ret);
}
}
}
};

View File

@ -13,8 +13,6 @@ var modehate = function(dbot) {
'on': 'MODE'
};
};
~ajs dbot.instance.addListener('MODE', function(data) { if(data.channel == '#42' && data.raw[0].indexOf('Snow') != -1 && data.raw[0].indexOf('+o') != -1) { dbot.instance.send('MODE #42 -o Snow'); } }
exports.fetch = function(dbot) {
return modehate(dbot);

View File

@ -3,13 +3,12 @@ var puns = function(dbot) {
return {
'listener': function(data) {
if(data.user == 'reality') {
dbot.instance.say(data.channel, dbot.db.quoteArrs['realityonce'].random());
} else if(dbot.db.quoteArrs.hasOwnProperty(data.user.toLowerCase())) {
dbot.say(data.channel, data.user + ': ' + dbot.db.quoteArrs[data.user.toLowerCase()].random());
} else if(dbot.instance.inChannel(data.channel)) {
dbot.instance.say('aisbot', '.karma ' + data.user);
dbot.waitingForKarma = data.channel;
if(dbot.moduleNames.include('quotes')) {
if(dbot.db.quoteArrs.hasOwnProperty(data.user.toLowerCase())) {
data.message = '~q ' + data.user.toLowerCase();
var params = data.message.split(' ');
dbot.commands[params[0]](data, params);
}
}
},

View File

@ -2,7 +2,48 @@ var quotes = function(dbot) {
var quotes = dbot.db.quoteArrs;
var addStack = [];
var rmAllowed = true;
// Retrieve a random quote from a given category, interpolating any quote references (~~QUOTE CATEGORY~~) within it
var interpolatedQuote = function(key, quoteTree) {
if(quoteTree !== undefined && quoteTree.indexOf(key) != -1) {
return '';
} else if(quoteTree === undefined) {
quoteTree = [];
}
var quoteString = quotes[key].random();
// Parse quote interpolations
var quoteRefs = quoteString.match(/~~([\d\w\s-]*)~~/g);
var thisRef;
while(quoteRefs && (thisRef = quoteRefs.shift()) !== undefined) {
var cleanRef = dbot.cleanNick(thisRef.replace(/^~~/,'').replace(/~~$/,'').trim());
if (quotes.hasOwnProperty(cleanRef)) {
quoteTree.push(key);
quoteString = quoteString.replace("~~" + cleanRef + "~~",
interpolatedQuote(cleanRef, quoteTree.slice()));
quoteTree.pop();
}
}
// Parse quote parameters
/*
var paramRefs = quoteString.match(/~~\$([1-9])~~/g);
var thisParam;
while(paramRefs && (thisParam = paramRefs.shift()) !== undefined) {
thisParam = thisParam[1];
console.log(thisParam);
if(thisParam < params.length) {
quoteString = quoteString.replace("~~$" + thisParam + "~~", params[thisParam]);
}
}
*/
return quoteString;
};
var commands = {
'~q': function(data, params) {
var q = data.message.valMatch(/^~q ([\d\w\s-]*)/, 2);
@ -10,7 +51,7 @@ var quotes = function(dbot) {
q[1] = q[1].trim();
key = q[1].toLowerCase();
if(quotes.hasOwnProperty(key)) {
dbot.say(data.channel, q[1] + ': ' + quotes[key].random());
dbot.say(data.channel, q[1] + ': ' + interpolatedQuote(key));
} else {
dbot.say(data.channel, 'Nobody loves ' + q[1]);
}
@ -65,13 +106,13 @@ var quotes = function(dbot) {
},
'~rmlast': function(data, params) {
if(rmAllowed == true || data.user == dbot.admin) {
if(rmAllowed == true || dbot.admin.include(data.user)) {
var q = data.message.valMatch(/^~rmlast ([\d\w\s-]*)/, 2);
if(q) {
q[1] = q[1].trim()
key = q[1].toLowerCase();
if(quotes.hasOwnProperty(q[1])) {
if(!dbot.db.locks.include(q[1]) || data.user == dbot.admin) {
if(!dbot.db.locks.include(q[1]) || dbot.admin.include(data.user)) {
var quote = quotes[key].pop();
if(quotes[key].length === 0) {
delete quotes[key];
@ -104,7 +145,7 @@ var quotes = function(dbot) {
},
'~rm': function(data, params) {
if(rmAllowed == true || data.user == dbot.admin) {
if(rmAllowed == true || dbot.admin.include(data.user)) {
var q = data.message.valMatch(/^~rm ([\d\w\s-]*) (.+)$/, 3);
if(q) {
if(quotes.hasOwnProperty(q[1])) {
@ -112,6 +153,9 @@ var quotes = function(dbot) {
var index = quotes[q[1]].indexOf(q[2]);
if(index != -1) {
quotes[q[1]].splice(index, 1);
if(quotes[q[1]].length === 0) {
delete quotes[q[1]];
}
rmAllowed = false;
dbot.say(data.channel, '\'' + q[2] + '\' removed from ' + q[1]);
} else {
@ -144,7 +188,6 @@ var quotes = function(dbot) {
} else { // Give total quote count
var totalQuoteCount = 0;
for(var category in quotes) {
console.log('adding ' + category.length);
totalQuoteCount += category.length;
}
dbot.say(data.channel, 'There are ' + totalQuoteCount + ' quotes in total.');
@ -152,7 +195,7 @@ var quotes = function(dbot) {
},
'~qadd': function(data, params) {
var q = data.message.valMatch(/^~qadd ([\d\w\s-]*)=(.+)$/, 3);
var q = data.message.valMatch(/^~qadd ([\d\w\s-]+?)[ ]?=[ ]?(.+)$/, 3);
if(q) {
key = q[1].toLowerCase();
if(!Object.isArray(quotes[key])) {
@ -189,11 +232,11 @@ var quotes = function(dbot) {
'~rq': function(data, params) {
var rQuote = Object.keys(quotes).random();
dbot.say(data.channel, rQuote + ': ' + quotes[rQuote].random());
dbot.say(data.channel, rQuote + ': ' + interpolatedQuote(rQuote));
},
'~d': function(data, params) {
dbot.say(data.channel, data.user + ': ' + dbot.db.quoteArrs['depressionbot'].random());
dbot.say(data.channel, data.user + ': ' + interpolatedQuote(dbot.name));
},
'~link': function(data, params) {
@ -202,6 +245,23 @@ var quotes = function(dbot) {
} else {
dbot.say(data.channel, 'Link to "'+params[1]+'" - http://nc.no.de:443/quotes/'+params[1]);
}
},
'~qprune': function(data) {
var pruned = []
for(key in quotes) {
if(quotes.hasOwnProperty(key)) {
if(quotes[key].length == 0) {
delete quotes[key];
pruned.push(key);
}
}
}
if(pruned.length > 0) {
dbot.say(data.channel, "Pruning empty quote categories: " + pruned.join(", "));
} else {
dbot.say(data.channel, "No empty quote categories. Commence incineration.");
}
}
};
@ -227,6 +287,9 @@ var quotes = function(dbot) {
dbot.db.bans['*'].include(data.user)) {
dbot.say(data.channel, data.user + ' is banned from using this command. Commence incineration.');
} else {
if(!dbot.db.quoteArrs.hasOwnProperty('realityonce')) {
dbot.db.quoteArrs['realityonce'] = [];
}
dbot.db.quoteArrs['realityonce'].push('reality ' + once[1] + '.');
addStack.push('realityonce');
rmAllowed = true;

View File

@ -18,26 +18,26 @@ var webInterface = function(dbot) {
// Lists the quote categories
app.get('/quotes', function(req, res) {
res.render('quotelist', { 'quotelist': Object.keys(dbot.db.quoteArrs) });
res.render('quotelist', { 'name': dbot.name, 'quotelist': Object.keys(dbot.db.quoteArrs) });
});
// Lists quotes in a category
app.get('/quotes/:key', function(req, res) {
var key = req.params.key.toLowerCase();
if(dbot.db.quoteArrs.hasOwnProperty(key)) {
res.render('quotes', { 'quotes': dbot.db.quoteArrs[key], locals: { 'url_regex': RegExp.prototype.url_regex() } });
res.render('quotes', { 'name': dbot.name, 'quotes': dbot.db.quoteArrs[key], locals: { 'url_regex': RegExp.prototype.url_regex() } });
} else {
res.render('error', { 'message': 'No quotes under that key.' });
res.render('error', { 'name': dbot.name, 'message': 'No quotes under that key.' });
}
});
// Load random quote category page
app.get('/rq', function(req, res) {
var rCategory = Object.keys(dbot.db.quoteArrs).random();
res.render('quotes', { 'quotes': dbot.db.quoteArrs[rCategory], locals: { 'url_regex': RegExp.prototype.url_regex() } });
res.render('quotes', { 'name': dbot.name, 'quotes': dbot.db.quoteArrs[rCategory], locals: { 'url_regex': RegExp.prototype.url_regex() } });
});
app.listen(443);
app.listen(dbot.webPort);
return {
'onDestroy': function() {

22
public/ytembed.js Normal file
View File

@ -0,0 +1,22 @@
// let's fetch us some goddamn API
var apiEmbed = document.createElement('script');
apiEmbed.src = 'http://www.youtube.com/player_api';
document.getElementsByTagName('script')[0].parentNode.insertBefore(apiEmbed, document.getElementsByTagName('script')[0]);
// this will be called by the player API when it's finished downloading
function onYouTubePlayerAPIReady() {
var youTubePlaceholders = document.getElementsByClassName('ytplaceholder');
for(var i = 0; i < youTubePlaceholders.length; i++) {
var videoURL = youTubePlaceholders[i].innerHTML;
var videoIDMaybe = videoURL.match(/[?&]v=([A-Za-z0-9\-_]+)(?:[?&]|$)/);
youTubePlaceholders[i].innerText = '';
if(videoIDMaybe) {
var ytVideoID = videoIDMaybe[1];
var player = new YT.Player(youTubePlaceholders[i], {
height: '203',
width: '336',
videoId: ytVideoID
});
}
}
}

93
run.js
View File

@ -6,16 +6,50 @@ require('./snippets');
var DBot = function(timers) {
// Load external files
this.config = JSON.parse(fs.readFileSync('config.json', 'utf-8'));
this.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
this.db = null;
var rawDB;
try {
var rawDB = fs.readFileSync('db.json', 'utf-8');
} catch (e) {
this.db = {}; // If no db file, make empty one
}
if(!this.db) { // If it wasn't empty
this.db = JSON.parse(rawDB);
}
// Repair any deficiencies in the DB; if this is a new DB, that's everything
if(!this.db.hasOwnProperty("bans")) {
this.db.bans = {};
}
if(!this.db.bans.hasOwnProperty("*")) {
this.db.bans["*"] = [];
}
if(!this.db.hasOwnProperty("quoteArrs")) {
this.db.quoteArrs = {};
}
if(!this.db.hasOwnProperty("kicks")) {
this.db.kicks = {};
}
if(!this.db.hasOwnProperty("kickers")) {
this.db.kickers = {};
}
if(!this.db.hasOwnProperty("modehate")) {
this.db.modehate = [];
}
if(!this.db.hasOwnProperty("locks")) {
this.db.locks = [];
}
// Populate bot properties with config data
this.name = this.config.name || 'dbox';
this.admin = this.config.admin || 'reality';
this.admin = this.config.admin || [ 'reality' ];
this.password = this.config.password || 'lolturtles';
this.nickserv = this.config.nickserv || 'zippy';
this.server = this.config.server || 'elara.ivixor.net';
this.port = this.config.port || 6667;
this.moduleNames = this.config.modules || [ 'js', 'admin', 'kick', 'modehate', 'quotes', 'puns', 'spelling', 'web', 'youare' ];
this.webPort = this.config.webPort || 443;
this.moduleNames = this.config.modules || [ 'command', 'js', 'admin', 'kick', 'modehate', 'quotes', 'puns', 'spelling', 'web', 'youare' ];
this.sessionData = {};
this.timers = timers.create();
@ -38,7 +72,7 @@ DBot.prototype.say = function(channel, data) {
};
DBot.prototype.act = function(channel, data) {
this.instance.send('PRIVMSG', channel, ':\001ACTION' + data + '\001');
this.instance.send('PRIVMSG', channel, ':\001ACTION ' + data + '\001');
}
// Save the database file
@ -62,6 +96,12 @@ DBot.prototype.reloadModules = function() {
this.timers.clearTimers();
this.save();
// Enforce having command. it can still be reloaded, but dbot _will not_
// function without it, so not having it should be impossible
if(!this.moduleNames.include("command")) {
this.moduleNames.push("command");
}
// Reload Javascript snippets
var path = require.resolve('./snippets');
delete require.cache[path];
@ -97,51 +137,6 @@ DBot.prototype.reloadModules = function() {
return module;
}.bind(this));
this.instance.addListener('PRIVMSG', function(data) {
params = data.message.split(' ');
if(data.channel == this.name) data.channel = data.user;
if(this.commands.hasOwnProperty(params[0])) {
if((this.db.bans.hasOwnProperty(params[0]) &&
this.db.bans[params[0]].include(data.user)) || this.db.bans['*'].include(data.user))
this.say(data.channel, data.user +
' is banned from using this command. Commence incineration.');
else {
this.commands[params[0]](data, params);
this.save();
}
} else {
var q = data.message.valMatch(/^~([\d\w\s-]*)/, 2);
if(q) {
if(this.db.bans['*'].include(data.user)) {
this.say(data.channel, data.user +
' is banned from using this command. Commence incineration.');
} else {
q[1] = q[1].trim();
key = this.cleanNick(q[1])
if(this.db.quoteArrs.hasOwnProperty(key)) {
this.say(data.channel, q[1] + ': ' + this.db.quoteArrs[key].random());
} else {
// See if it's similar to anything
var winnerDistance = Infinity;
var winner = false;
for(var commandName in this.commands) {
var distance = String.prototype.distance(params[0], commandName);
if(distance < winnerDistance) {
winner = commandName;
winnerDistance = distance;
}
}
if(winnerDistance < 3) {
this.say(data.channel, 'Did you mean ' + winner + '? Learn to type, hippie!');
}
}
}
}
}
}.bind(this));
};
DBot.prototype.cleanNick = function(key) {

View File

@ -27,6 +27,14 @@ Array.prototype.include = function(value) {
return false;
};
Array.prototype.sum = function() {
var sum = 0;
for(var i=0;i<this.length;i++) {
sum += (parseFloat(this[i]) || 0);
}
return sum;
};
Array.prototype.allGroupings = function() {
if (this.length == 0) {
return []; /* short-circuit the empty-array case */

View File

@ -1,15 +1,23 @@
var timers = function() {
var timers = [];
var timeouts = [];
return {
'addTimer': function(interval, callback) { // Because who puts the callback first. Really.
timers.push(setInterval(callback, interval));
},
'addOnceTimer': function(delay, callback) { // Because who seriously puts the callback first here too?
timeouts.push(setTimeout(callback, delay));
},
'clearTimers': function() {
for(var i;i<timers.length;i++) {
clearInterval(timers[i]);
}
for(var i;i<timeouts.length;i++) {
clearTimeout(timeouts[i]);
}
}
};
};

View File

@ -3,11 +3,11 @@ html(lang='en')
head
meta(charset='utf-8')
link(rel='stylesheet', type='text/css', href='/styles.css')
title Depressionbot web interface
title #{name} web interface
body
div#page
div#title
a(href='/') Depressionbot web interface
a(href='/') #{name} web interface
div#main
!{body}
script(type="text/javascript", src="/script.js")

View File

@ -1,11 +1,19 @@
ul#quotelist
-var hasYouTubeVids=false
-each quote in quotes
-if(quote.match(locals.url_regex))
li.quote
a(href=quote)
-if(quote.match(/(jpg|png|gif|jpeg|tiff)$/))
-if(quote.match(/(jpg|png|gif|jpeg|tiff)$/))
a(href=quote)
img(src=quote)
-else
-else if(quote.match(/youtube.com\/watch/))
-hasYouTubeVids = true
span(class='ytplaceholder')
=quote
-else
a(href=quote)
=quote
-else
li.quote #{quote}
-if(hasYouTubeVids)
script(src='/ytembed.js')