This commit is contained in:
reality 2013-05-17 13:32:41 +00:00
commit df2b484a66
9 changed files with 132 additions and 94 deletions

View File

@ -252,6 +252,7 @@ var commands = function(dbot) {
event.reply(dbot.t("no_config_key")); event.reply(dbot.t("no_config_key"));
} }
}); });
} else { } else {
event.reply(dbot.t("config_keys_location", { event.reply(dbot.t("config_keys_location", {
"path": "root", "path": "root",

45
modules/api/README.md Normal file
View File

@ -0,0 +1,45 @@
## API
Creates external REST APIs for module API functions.
### Description
This module uses the web module to expose module API functionality externally
through a REST API. As it stands, it's only really useful for viewing various
information returned by API functions, as there is no system for API keys or
anything like that to protect against misuse of functionality which modifies
data.
To externalise an API function, two properties must be set on a particular API
function, like so:
api['resolveUser'].external = true;
api['resolveUser'].extMap = [ 'server', 'nick', 'callback' ];
The first, 'external' flag simply lets the API module know that this function is
intended to be exposed externally - and functions will always be considered not
to be externally available unless this flag is explicitly set.
The second is a mapping of parameters to the module. This should match the
function prototype given when the function is declared (unfortunately these
can't be mapped automatically because the closure use means we get 'native code'
returned and can't scan the function headers for the parameter names).
Then, to access this function remotely we can simply make a GET request to the
web counterpart to the internal API function path. So, internally you'd access
the resolveUser function at _dbot.api.users.resolveUser_, we can get to it
externally with _/api/users/resolveUser_ - supplying parameters as they are
named in the extMap.
The response to the API call will be given in the form of JSON:
{
err: Error, such as 'API function not enabled for external access'
data: API call response
}
If there is a _callback_ parameter named in the extMap, then the API module
automatically hijacks this parameter and uses the data it's called with to
supply the response to the API call with data. If there is no callback
parameter, then it's a blocking API request and the response will be the return
value of the call.

View File

@ -5,6 +5,22 @@
var _ = require('underscore')._; var _ = require('underscore')._;
var api = function(dbot) { var api = function(dbot) {
this.pages = {
'/api': function(req, res) {
var externalApi = {};
_.each(dbot.api, function(moduleApi, moduleName) {
externalApi[moduleName] = {};
_.each(moduleApi, function(method, methodName) {
if(method.external == true) {
externalApi[moduleName][methodName] = method.extMap;
}
});
});
res.render('api', { 'name': dbot.config.name, 'api': externalApi });
}
};
this.onLoad = function() { this.onLoad = function() {
dbot.modules.web.app.get('/api/:module/:method', function(req, res) { dbot.modules.web.app.get('/api/:module/:method', function(req, res) {
var module = req.params.module, var module = req.params.module,

34
modules/imgur/README.md Normal file
View File

@ -0,0 +1,34 @@
## imgur
Various imgur functionality.
### Description
Posts information on imgur links which are pasted into the channel and provides
functionality to generate a random imgur link.
### Commands
#### ~ri
Generate a random imgur image and post a link to it in the channel.
### API
#### getRandomImage(callback)
Generate a random imgur image by generating random slugs and then testing for
their existence until it finds one which exists (and hasn't been deleted).
Callback is given with two parameters, the URL of the generated image, and the
slug for the generated image.
#### getImageInfoString(slug, callback)
Return a string containing info about the image with the given slug from the
imgur API. Callback is called with one argument, the info string.
#### getImageInfo(slug, callback)
Return data from the imgur API on an image with the given slug. Callback is
called with one argument, the information returned by the API.
### Hooks
#### link
Posts information about an imgur link when one is linked in the channel.

View File

@ -39,41 +39,6 @@ var commands = function(dbot) {
})); }));
}, },
/*'~cquiet': function(event) {
var server = event.server,
quieter = event.user,
quietee = event.input[2],
channel = event.input[1],
reason = event.input[3];
this.api.quiet(server, quietee, channel);
dbot.api.report(server, channel, dbot.t('cquieted', {
'quieter': quieter,
'quietee': quietee,
'channel': channel,
'reason': reason
}));
},
'~nquiet': function(event) {
var server = event.server,
quieter = event.user,
quietee = event.input[1],
channels = dbot.config.servers[server].channels,
reason = event.input[2];
_.each(channels, function(channel) {
this.api.quiet(server, quietee, channel);
}, this);
dbot.api.report(server, channel, dbot.t('nquieted', {
'quieter': quieter,
'quietee': quietee,
'reason': reason
}));
},*/
// Kick and ban from all channels on the network. // Kick and ban from all channels on the network.
'~nban': function(event) { '~nban': function(event) {
var server = event.server, var server = event.server,
@ -107,7 +72,11 @@ var commands = function(dbot) {
notifyString += ' ' + dbot.t('quote_recorded', { 'user': banee }); notifyString += ' ' + dbot.t('quote_recorded', { 'user': banee });
} }
dbot.api.report.notify(server, this.config.admin_channels[event.server], notifyString); var notifyChannel = event.channel.name;
if(this.config.admin_channels[event.server]) {
notifyChannel = this.config.admin_channels[event.server];
}
dbot.api.report.notify(server, notifyChannel, notifyString);
}, },
/*** Kick Stats ***/ /*** Kick Stats ***/
@ -164,6 +133,9 @@ var commands = function(dbot) {
command.access = 'moderator'; command.access = 'moderator';
}); });
commands['~kickcount'].access = 'regular';
commands['~kickstats'].access = 'regular';
commands['~ckick'].regex = [/^~ckick ([^ ]+) ([^ ]+) (.+)$/, 4]; commands['~ckick'].regex = [/^~ckick ([^ ]+) ([^ ]+) (.+)$/, 4];
commands['~nban'].regex = [/^~nban ([^ ]+) (.+)$/, 3]; commands['~nban'].regex = [/^~nban ([^ ]+) (.+)$/, 3];

View File

@ -10,12 +10,14 @@
"en": "Thou shalt not kick {botname}", "en": "Thou shalt not kick {botname}",
"es": "No expulsás {botname}", "es": "No expulsás {botname}",
"na'vi": "Ngal {botname}it ke tsun tsrive'i", "na'vi": "Ngal {botname}it ke tsun tsrive'i",
"cy": "Ni ddylech cicio {botname}" "cy": "Ni ddylech cicio {botname}",
"nl": "Gij zult {botname} niet kicken"
}, },
"ckicked": { "ckicked": {
"en": "Attention: {kicker} has kicked {kickee} from {channel}. The reason given was: \"{reason}.\"", "en": "Attention: {kicker} has kicked {kickee} from {channel}. The reason given was: \"{reason}.\"",
"cy": "Ni ddylech cicio {botname}", },
"nl": "Gij zult {botname} niet kicken" "cbanned": {
"en": "Attention: {banner} has banned {banee} from {channel}. The reason given was \"{reason}.\""
}, },
"nbanned": { "nbanned": {
"en": "Attention: {banner} has banned {banee} network-wide. The reason given was \"{reason}.\"" "en": "Attention: {banner} has banned {banee} network-wide. The reason given was \"{reason}.\""

2
run.js
View File

@ -143,7 +143,7 @@ DBot.prototype.reloadModules = function() {
// Enforce having command. it can still be reloaded, but dbot _will not_ // Enforce having command. it can still be reloaded, but dbot _will not_
// function without it, so not having it should be impossible // function without it, so not having it should be impossible
if(!moduleNames.include("command")) { if(!_.include(moduleNames, 'command')) {
moduleNames.push("command"); moduleNames.push("command");
} }

View File

@ -1,49 +1,4 @@
/*** Array ***/ /*** String ***/
Array.prototype.each = function(fun) {
for(var i=0;i<this.length;i++) {
fun(this[i]);
}
};
Array.prototype.collect = function(fun) {
var collect = [];
for(var i=0;i<this.length;i++) {
collect.push(fun(this[i]));
}
return collect;
};
Array.prototype.include = function(value) {
for(var i=0;i<this.length;i++) {
if(this[i] == value) {
return true;
}
}
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.uniq = function() {
var hash = {}
var result = [];
this.each(function(item) {
if(!hash.hasOwnProperty(item)){
hash[item] = true;
result.push(item);
}
});
return result;
}
/*** String ***/
String.prototype.valMatch = function(regex, expLength) { String.prototype.valMatch = function(regex, expLength) {
var key = this.match(regex); var key = this.match(regex);
@ -54,14 +9,6 @@ String.prototype.valMatch = function(regex, expLength) {
} }
}; };
String.prototype.endsWith = function(needle) {
return needle === this.slice(this.length - needle.length);
};
String.prototype.startsWith = function(needle) {
return needle === this.slice(0, needle.length);
};
String.prototype.format = function() { // format takes either multiple indexed arguments, or a single object, whose keys/values will be used String.prototype.format = function() { // format takes either multiple indexed arguments, or a single object, whose keys/values will be used
var targetStr = this; var targetStr = this;
var replacements = [].splice.call(arguments, 0); var replacements = [].splice.call(arguments, 0);

21
views/api/api.jade Normal file
View File

@ -0,0 +1,21 @@
extends ../layout
block content
div#backlink
a(href='/') &laquo; Home
div#row
h4 External API Functions
div#row
table.tabe.table-hover.data
thead
tr
th Module
th Path
th Arguments
tbody
-each module,moduleName in api
-each func,funcName in api[moduleName]
tr
td #{moduleName}
td /api/#{moduleName}/#{funcName}
td #{func}