mirror of
https://github.com/reality/dbot.git
synced 2024-11-24 04:49:25 +01:00
Merge branch 'master' of github.com:reality/depressionbot
This commit is contained in:
commit
8c81842643
118
README.md
Normal file
118
README.md
Normal 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.
|
@ -1,4 +1,6 @@
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var sys = require('sys')
|
||||||
|
var exec = require('child_process').exec;
|
||||||
|
|
||||||
var adminCommands = function(dbot) {
|
var adminCommands = function(dbot) {
|
||||||
var dbot = dbot;
|
var dbot = dbot;
|
||||||
@ -6,17 +8,28 @@ var adminCommands = function(dbot) {
|
|||||||
var commands = {
|
var commands = {
|
||||||
'join': function(data, params) {
|
'join': function(data, params) {
|
||||||
dbot.instance.join(params[1]);
|
dbot.instance.join(params[1]);
|
||||||
dbot.say(dbot.admin, 'Joined ' + params[1]);
|
dbot.say(data.channel, 'Joined ' + params[1]);
|
||||||
},
|
},
|
||||||
|
|
||||||
'opme': function(data, params) {
|
'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) {
|
'part': function(data, params) {
|
||||||
dbot.instance.part(params[1]);
|
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) {
|
'reload': function(data, params) {
|
||||||
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
|
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
|
||||||
dbot.reloadModules();
|
dbot.reloadModules();
|
||||||
@ -24,11 +37,25 @@ var adminCommands = function(dbot) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'say': function(data, params) {
|
'say': function(data, params) {
|
||||||
|
if (params[1] === "@") {
|
||||||
|
var c = data.channel;
|
||||||
|
} else {
|
||||||
var c = params[1];
|
var c = params[1];
|
||||||
|
}
|
||||||
var m = params.slice(2).join(' ');
|
var m = params.slice(2).join(' ');
|
||||||
dbot.say(c, m);
|
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) {
|
'load': function(data, params) {
|
||||||
dbot.moduleNames.push(params[1]);
|
dbot.moduleNames.push(params[1]);
|
||||||
dbot.reloadModules();
|
dbot.reloadModules();
|
||||||
@ -92,7 +119,7 @@ var adminCommands = function(dbot) {
|
|||||||
if(data.channel == dbot.name) data.channel = data.user;
|
if(data.channel == dbot.name) data.channel = data.user;
|
||||||
|
|
||||||
params = data.message.split(' ');
|
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);
|
commands[params[0]](data, params);
|
||||||
dbot.save();
|
dbot.save();
|
||||||
}
|
}
|
||||||
|
105
modules/command.js
Normal file
105
modules/command.js
Normal 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
97
modules/dice.js
Normal 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);
|
||||||
|
};
|
@ -26,7 +26,7 @@ var drama = function(dbot) {
|
|||||||
|
|
||||||
var commands = {
|
var commands = {
|
||||||
'~train': function(data, params) {
|
'~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]);
|
bayes.train(last[params[1]][params[2]], params[3]);
|
||||||
dbot.say(data.channel, 'Last thing ' + params[2] + ' said in ' +
|
dbot.say(data.channel, 'Last thing ' + params[2] + ' said in ' +
|
||||||
params[1] + ' (' + last[params[1]][params[2]] + ') classified as \'' + params[3] + '\'');
|
params[1] + ' (' + last[params[1]][params[2]] + ') classified as \'' + params[3] + '\'');
|
||||||
@ -34,7 +34,7 @@ var drama = function(dbot) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'~rtrain': function(data, params) {
|
'~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];
|
var category = params[1];
|
||||||
params.splice(0, 2);
|
params.splice(0, 2);
|
||||||
var msg = params.join(' ');
|
var msg = params.join(' ');
|
||||||
|
@ -15,8 +15,11 @@ var js = function(dbot) {
|
|||||||
|
|
||||||
'~ajs': function(data, params) {
|
'~ajs': function(data, params) {
|
||||||
var q = data.message.valMatch(/^~ajs (.*)/, 2);
|
var q = data.message.valMatch(/^~ajs (.*)/, 2);
|
||||||
if(data.user == dbot.admin) {
|
if(dbot.admin.include(data.user) ) {
|
||||||
dbot.say(data.channel, eval(q[1]));
|
var ret = eval(q[1]);
|
||||||
|
if(ret != undefined) {
|
||||||
|
dbot.say(data.channel, ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -13,8 +13,6 @@ var modehate = function(dbot) {
|
|||||||
'on': 'MODE'
|
'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) {
|
exports.fetch = function(dbot) {
|
||||||
return modehate(dbot);
|
return modehate(dbot);
|
||||||
|
@ -3,13 +3,12 @@ var puns = function(dbot) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'listener': function(data) {
|
'listener': function(data) {
|
||||||
if(data.user == 'reality') {
|
if(dbot.moduleNames.include('quotes')) {
|
||||||
dbot.instance.say(data.channel, dbot.db.quoteArrs['realityonce'].random());
|
if(dbot.db.quoteArrs.hasOwnProperty(data.user.toLowerCase())) {
|
||||||
} else if(dbot.db.quoteArrs.hasOwnProperty(data.user.toLowerCase())) {
|
data.message = '~q ' + data.user.toLowerCase();
|
||||||
dbot.say(data.channel, data.user + ': ' + dbot.db.quoteArrs[data.user.toLowerCase()].random());
|
var params = data.message.split(' ');
|
||||||
} else if(dbot.instance.inChannel(data.channel)) {
|
dbot.commands[params[0]](data, params);
|
||||||
dbot.instance.say('aisbot', '.karma ' + data.user);
|
}
|
||||||
dbot.waitingForKarma = data.channel;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,6 +3,47 @@ var quotes = function(dbot) {
|
|||||||
var addStack = [];
|
var addStack = [];
|
||||||
var rmAllowed = true;
|
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 = {
|
var commands = {
|
||||||
'~q': function(data, params) {
|
'~q': function(data, params) {
|
||||||
var q = data.message.valMatch(/^~q ([\d\w\s-]*)/, 2);
|
var q = data.message.valMatch(/^~q ([\d\w\s-]*)/, 2);
|
||||||
@ -10,7 +51,7 @@ var quotes = function(dbot) {
|
|||||||
q[1] = q[1].trim();
|
q[1] = q[1].trim();
|
||||||
key = q[1].toLowerCase();
|
key = q[1].toLowerCase();
|
||||||
if(quotes.hasOwnProperty(key)) {
|
if(quotes.hasOwnProperty(key)) {
|
||||||
dbot.say(data.channel, q[1] + ': ' + quotes[key].random());
|
dbot.say(data.channel, q[1] + ': ' + interpolatedQuote(key));
|
||||||
} else {
|
} else {
|
||||||
dbot.say(data.channel, 'Nobody loves ' + q[1]);
|
dbot.say(data.channel, 'Nobody loves ' + q[1]);
|
||||||
}
|
}
|
||||||
@ -65,13 +106,13 @@ var quotes = function(dbot) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'~rmlast': function(data, params) {
|
'~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);
|
var q = data.message.valMatch(/^~rmlast ([\d\w\s-]*)/, 2);
|
||||||
if(q) {
|
if(q) {
|
||||||
q[1] = q[1].trim()
|
q[1] = q[1].trim()
|
||||||
key = q[1].toLowerCase();
|
key = q[1].toLowerCase();
|
||||||
if(quotes.hasOwnProperty(q[1])) {
|
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();
|
var quote = quotes[key].pop();
|
||||||
if(quotes[key].length === 0) {
|
if(quotes[key].length === 0) {
|
||||||
delete quotes[key];
|
delete quotes[key];
|
||||||
@ -104,7 +145,7 @@ var quotes = function(dbot) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'~rm': function(data, params) {
|
'~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);
|
var q = data.message.valMatch(/^~rm ([\d\w\s-]*) (.+)$/, 3);
|
||||||
if(q) {
|
if(q) {
|
||||||
if(quotes.hasOwnProperty(q[1])) {
|
if(quotes.hasOwnProperty(q[1])) {
|
||||||
@ -112,6 +153,9 @@ var quotes = function(dbot) {
|
|||||||
var index = quotes[q[1]].indexOf(q[2]);
|
var index = quotes[q[1]].indexOf(q[2]);
|
||||||
if(index != -1) {
|
if(index != -1) {
|
||||||
quotes[q[1]].splice(index, 1);
|
quotes[q[1]].splice(index, 1);
|
||||||
|
if(quotes[q[1]].length === 0) {
|
||||||
|
delete quotes[q[1]];
|
||||||
|
}
|
||||||
rmAllowed = false;
|
rmAllowed = false;
|
||||||
dbot.say(data.channel, '\'' + q[2] + '\' removed from ' + q[1]);
|
dbot.say(data.channel, '\'' + q[2] + '\' removed from ' + q[1]);
|
||||||
} else {
|
} else {
|
||||||
@ -144,7 +188,6 @@ var quotes = function(dbot) {
|
|||||||
} else { // Give total quote count
|
} else { // Give total quote count
|
||||||
var totalQuoteCount = 0;
|
var totalQuoteCount = 0;
|
||||||
for(var category in quotes) {
|
for(var category in quotes) {
|
||||||
console.log('adding ' + category.length);
|
|
||||||
totalQuoteCount += category.length;
|
totalQuoteCount += category.length;
|
||||||
}
|
}
|
||||||
dbot.say(data.channel, 'There are ' + totalQuoteCount + ' quotes in total.');
|
dbot.say(data.channel, 'There are ' + totalQuoteCount + ' quotes in total.');
|
||||||
@ -152,7 +195,7 @@ var quotes = function(dbot) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'~qadd': function(data, params) {
|
'~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) {
|
if(q) {
|
||||||
key = q[1].toLowerCase();
|
key = q[1].toLowerCase();
|
||||||
if(!Object.isArray(quotes[key])) {
|
if(!Object.isArray(quotes[key])) {
|
||||||
@ -189,11 +232,11 @@ var quotes = function(dbot) {
|
|||||||
|
|
||||||
'~rq': function(data, params) {
|
'~rq': function(data, params) {
|
||||||
var rQuote = Object.keys(quotes).random();
|
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) {
|
'~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) {
|
'~link': function(data, params) {
|
||||||
@ -202,6 +245,23 @@ var quotes = function(dbot) {
|
|||||||
} else {
|
} else {
|
||||||
dbot.say(data.channel, 'Link to "'+params[1]+'" - http://nc.no.de:443/quotes/'+params[1]);
|
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.db.bans['*'].include(data.user)) {
|
||||||
dbot.say(data.channel, data.user + ' is banned from using this command. Commence incineration.');
|
dbot.say(data.channel, data.user + ' is banned from using this command. Commence incineration.');
|
||||||
} else {
|
} else {
|
||||||
|
if(!dbot.db.quoteArrs.hasOwnProperty('realityonce')) {
|
||||||
|
dbot.db.quoteArrs['realityonce'] = [];
|
||||||
|
}
|
||||||
dbot.db.quoteArrs['realityonce'].push('reality ' + once[1] + '.');
|
dbot.db.quoteArrs['realityonce'].push('reality ' + once[1] + '.');
|
||||||
addStack.push('realityonce');
|
addStack.push('realityonce');
|
||||||
rmAllowed = true;
|
rmAllowed = true;
|
||||||
|
@ -18,26 +18,26 @@ var webInterface = function(dbot) {
|
|||||||
|
|
||||||
// Lists the quote categories
|
// Lists the quote categories
|
||||||
app.get('/quotes', function(req, res) {
|
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
|
// Lists quotes in a category
|
||||||
app.get('/quotes/:key', function(req, res) {
|
app.get('/quotes/:key', function(req, res) {
|
||||||
var key = req.params.key.toLowerCase();
|
var key = req.params.key.toLowerCase();
|
||||||
if(dbot.db.quoteArrs.hasOwnProperty(key)) {
|
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 {
|
} 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
|
// Load random quote category page
|
||||||
app.get('/rq', function(req, res) {
|
app.get('/rq', function(req, res) {
|
||||||
var rCategory = Object.keys(dbot.db.quoteArrs).random();
|
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 {
|
return {
|
||||||
'onDestroy': function() {
|
'onDestroy': function() {
|
||||||
|
22
public/ytembed.js
Normal file
22
public/ytembed.js
Normal 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
run.js
91
run.js
@ -6,16 +6,50 @@ require('./snippets');
|
|||||||
var DBot = function(timers) {
|
var DBot = function(timers) {
|
||||||
// Load external files
|
// Load external files
|
||||||
this.config = JSON.parse(fs.readFileSync('config.json', 'utf-8'));
|
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
|
// Populate bot properties with config data
|
||||||
this.name = this.config.name || 'dbox';
|
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.password = this.config.password || 'lolturtles';
|
||||||
this.nickserv = this.config.nickserv || 'zippy';
|
this.nickserv = this.config.nickserv || 'zippy';
|
||||||
this.server = this.config.server || 'elara.ivixor.net';
|
this.server = this.config.server || 'elara.ivixor.net';
|
||||||
this.port = this.config.port || 6667;
|
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();
|
this.timers = timers.create();
|
||||||
|
|
||||||
@ -62,6 +96,12 @@ DBot.prototype.reloadModules = function() {
|
|||||||
this.timers.clearTimers();
|
this.timers.clearTimers();
|
||||||
this.save();
|
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
|
// Reload Javascript snippets
|
||||||
var path = require.resolve('./snippets');
|
var path = require.resolve('./snippets');
|
||||||
delete require.cache[path];
|
delete require.cache[path];
|
||||||
@ -97,51 +137,6 @@ DBot.prototype.reloadModules = function() {
|
|||||||
|
|
||||||
return module;
|
return module;
|
||||||
}.bind(this));
|
}.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) {
|
DBot.prototype.cleanNick = function(key) {
|
||||||
|
@ -27,6 +27,14 @@ Array.prototype.include = function(value) {
|
|||||||
return false;
|
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() {
|
Array.prototype.allGroupings = function() {
|
||||||
if (this.length == 0) {
|
if (this.length == 0) {
|
||||||
return []; /* short-circuit the empty-array case */
|
return []; /* short-circuit the empty-array case */
|
||||||
|
8
timer.js
8
timer.js
@ -1,15 +1,23 @@
|
|||||||
var timers = function() {
|
var timers = function() {
|
||||||
var timers = [];
|
var timers = [];
|
||||||
|
var timeouts = [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'addTimer': function(interval, callback) { // Because who puts the callback first. Really.
|
'addTimer': function(interval, callback) { // Because who puts the callback first. Really.
|
||||||
timers.push(setInterval(callback, interval));
|
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() {
|
'clearTimers': function() {
|
||||||
for(var i;i<timers.length;i++) {
|
for(var i;i<timers.length;i++) {
|
||||||
clearInterval(timers[i]);
|
clearInterval(timers[i]);
|
||||||
}
|
}
|
||||||
|
for(var i;i<timeouts.length;i++) {
|
||||||
|
clearTimeout(timeouts[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,11 +3,11 @@ html(lang='en')
|
|||||||
head
|
head
|
||||||
meta(charset='utf-8')
|
meta(charset='utf-8')
|
||||||
link(rel='stylesheet', type='text/css', href='/styles.css')
|
link(rel='stylesheet', type='text/css', href='/styles.css')
|
||||||
title Depressionbot web interface
|
title #{name} web interface
|
||||||
body
|
body
|
||||||
div#page
|
div#page
|
||||||
div#title
|
div#title
|
||||||
a(href='/') Depressionbot web interface
|
a(href='/') #{name} web interface
|
||||||
div#main
|
div#main
|
||||||
!{body}
|
!{body}
|
||||||
script(type="text/javascript", src="/script.js")
|
script(type="text/javascript", src="/script.js")
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
ul#quotelist
|
ul#quotelist
|
||||||
|
-var hasYouTubeVids=false
|
||||||
-each quote in quotes
|
-each quote in quotes
|
||||||
-if(quote.match(locals.url_regex))
|
-if(quote.match(locals.url_regex))
|
||||||
li.quote
|
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)
|
img(src=quote)
|
||||||
|
-else if(quote.match(/youtube.com\/watch/))
|
||||||
|
-hasYouTubeVids = true
|
||||||
|
span(class='ytplaceholder')
|
||||||
|
=quote
|
||||||
-else
|
-else
|
||||||
|
a(href=quote)
|
||||||
=quote
|
=quote
|
||||||
-else
|
-else
|
||||||
li.quote #{quote}
|
li.quote #{quote}
|
||||||
|
-if(hasYouTubeVids)
|
||||||
|
script(src='/ytembed.js')
|
||||||
|
Loading…
Reference in New Issue
Block a user