forked from GitHub/dbot
Signed-off-by: Georg <georg@lysergic.dev>
Init - Initial fixes in modules - Bundled adapted jsbot - Elaborative README.md
This commit is contained in:
parent
77a9c346d8
commit
70a21d2b18
44
README.md
44
README.md
@ -1,3 +1,47 @@
|
|||||||
|
### dbot - revived
|
||||||
|
|
||||||
|
The motivation for this fork came from trying to make the trusty bot of our IRC network work with new infrastructure, until all features made their way into its successor.
|
||||||
|
|
||||||
|
Note that this project is not production worthy - it contains a lot of hardcoded debug lines and untidy code. Whether it will see prettification anytime soon, I cannot tell.
|
||||||
|
|
||||||
|
So far, the following issues were tackled successfully:
|
||||||
|
|
||||||
|
- NickServ identification
|
||||||
|
- NickServ user retrieval
|
||||||
|
- Quote shortcuts (~foo)
|
||||||
|
- Channel statistics (cstats)
|
||||||
|
|
||||||
|
This makes the bot usable on an IRC network powered by the Ergo IRCd, which is dbot was not happy communicate with at all when I first tried to run it.
|
||||||
|
|
||||||
|
The following issues are known, but are assigned low priority:
|
||||||
|
|
||||||
|
- No PASS/SASL authentication - it is hackable with a PASS line right after the USER command, however that prevents some parts of the bot from initializing. To use NickServ authentication, allowing the bot to start properly, the strict username checking feature in Ergo needs to be disabled
|
||||||
|
- Lots of ENOENT errors on startup
|
||||||
|
- Channel mode detection and assignment
|
||||||
|
- Some sites in the webinterface don't load / show errors
|
||||||
|
|
||||||
|
The following issues are known, and await investigation:
|
||||||
|
|
||||||
|
- Upon issuing some administrative commands, apparently a sort of "queue flush" gets trigered, causing dozens of unban events to make the bot unresponsive for a while
|
||||||
|
|
||||||
|
The following issues are resolved, but are not available in this repository, due to files not being accessible:
|
||||||
|
|
||||||
|
- WHO parsing for user management commands
|
||||||
|
- nban/nunban, with newly added ipban
|
||||||
|
|
||||||
|
The directory modules-stock/ is the stock modules directory, kept for safekeeping.
|
||||||
|
The directory modules/ contains changed modules.
|
||||||
|
|
||||||
|
The jsbot/ directory is not populated in the stock repository, however its contents are included here, as a few essential changes have been implemented there as well.
|
||||||
|
|
||||||
|
Tested with node v10.
|
||||||
|
|
||||||
|
Note: TripSit IRC related modules are to be found in a seperate repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Original README.md
|
||||||
|
|
||||||
# DBot IRC Bot
|
# DBot IRC Bot
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
28
modules-stock/SAMPLE/README.md
Normal file
28
modules-stock/SAMPLE/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
## FOO
|
||||||
|
|
||||||
|
bar.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module provides a command which allows users to foobar.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
It has following dependencies:
|
||||||
|
+ [foo](link)
|
||||||
|
|
||||||
|
### config.json
|
||||||
|
|
||||||
|
foo
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
|
||||||
|
#### ~foo [(bar]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
+ ~foo bar
|
||||||
|
|
||||||
|
### TODO
|
4
modules-stock/SAMPLE/config
Normal file
4
modules-stock/SAMPLE/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"foo": true,
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
39
modules-stock/SAMPLE/foo
Normal file
39
modules-stock/SAMPLE/foo
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: foo
|
||||||
|
* Description: bar.
|
||||||
|
* Requires: foo [bar]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
bar = require('foo');//dependencies
|
||||||
|
|
||||||
|
var foo = function(dbot) { //name of module
|
||||||
|
|
||||||
|
this.ApiRoot = 'API_ROOT_HERE';
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
//code for internal api here
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
//code for api here
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
//code for commands here
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
//code for stuff to be done on load here
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onDestroy = function() {
|
||||||
|
//stuff to be done on destroy here
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new foo(dbot); //name of module
|
||||||
|
};
|
8
modules-stock/SAMPLE/strings
Normal file
8
modules-stock/SAMPLE/strings
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"en": "{output} bar"
|
||||||
|
},
|
||||||
|
"foo2": {
|
||||||
|
"en": "Something went wrong :( Example:'~foo bar'"
|
||||||
|
}
|
||||||
|
}
|
3
modules-stock/SAMPLE/usage
Normal file
3
modules-stock/SAMPLE/usage
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"~foo": "~foo [bar]"
|
||||||
|
}
|
72
modules-stock/admin/README.md
Normal file
72
modules-stock/admin/README.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
## Admin
|
||||||
|
|
||||||
|
Administrator functionality.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
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 everything is thoroughly sanity checked.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### join [#channel]
|
||||||
|
Join the given channel.
|
||||||
|
|
||||||
|
#### part [#channel]
|
||||||
|
Leave the given channel.
|
||||||
|
|
||||||
|
#### opme [#channel]
|
||||||
|
Gives the caller ops in a given channel if possible. If called without a
|
||||||
|
channel, it will attempt to give the caller ops in the current channel.
|
||||||
|
|
||||||
|
#### greload
|
||||||
|
Perform a git pull, and then execute the 'reload' command. Saves a lot of time
|
||||||
|
updating!
|
||||||
|
|
||||||
|
#### version [module]
|
||||||
|
Shows the git version of the currently loaded revision of DBot. If module is
|
||||||
|
provided, it will attempt to get the revision of the module (this is only useful
|
||||||
|
for submodules).
|
||||||
|
|
||||||
|
#### status [module]
|
||||||
|
Show the recorded status for a given module, this is helpful for debugging
|
||||||
|
issues when loading or for checking if a module is loaded.
|
||||||
|
|
||||||
|
#### reload
|
||||||
|
Reload all of the modules currently in use by DBot. By using this, all module
|
||||||
|
functionality should be reloadable and replaceable without having to restart the
|
||||||
|
bot or interrupt the connection to the server.
|
||||||
|
|
||||||
|
#### say [#channel] [message]
|
||||||
|
Have DBot post the given message in the given channel (uses the server from
|
||||||
|
which you are sending the message). You may replace channel with '@' to have him
|
||||||
|
post the message in the current channel. Channel may also be replaced with a
|
||||||
|
nick on the server.
|
||||||
|
|
||||||
|
#### load [module]
|
||||||
|
Load a new module. This works by adding a module name to the roster and then
|
||||||
|
triggering a reload of all modules, at which point the new module is actually
|
||||||
|
loaded by the standard DBot process.
|
||||||
|
|
||||||
|
#### unload [module]
|
||||||
|
Unload a currently loaded module. This removes the module, and then triggers a
|
||||||
|
reload of all modules.
|
||||||
|
|
||||||
|
#### setconfig [path] [value]
|
||||||
|
Set a config value at path to be a certain value persistently. For example, if
|
||||||
|
you want to change the web module to listen on port 9001, you can run _setconfig
|
||||||
|
web.webPort 9001_.
|
||||||
|
|
||||||
|
#### pushconfig [path] [value]
|
||||||
|
Push a new value to an existing config array. For example, if you want to add
|
||||||
|
the user 'batman62' to the DBot moderators, you can run _pushconfig moderators
|
||||||
|
batman62_.
|
||||||
|
|
||||||
|
#### showconfig [path]
|
||||||
|
Use this to explore and view the DBot configuration. If called without a path,
|
||||||
|
it will display the config keys in the root; if the path is a subkey, it will
|
||||||
|
show all config keys under that key. If you give it an actual key, it'll show you
|
||||||
|
the currently effective config value.
|
64
modules-stock/admin/admin.js
Normal file
64
modules-stock/admin/admin.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Admin
|
||||||
|
* Description: Set of commands which only one who is a DepressionBot
|
||||||
|
* administrator can run.
|
||||||
|
*/
|
||||||
|
var fs = require('fs'),
|
||||||
|
_ = require('underscore')._;
|
||||||
|
|
||||||
|
var admin = function(dbot) {
|
||||||
|
this.internalAPI = {
|
||||||
|
'getCurrentConfig': function(configKey, callback) {
|
||||||
|
var configPath = dbot.config;
|
||||||
|
configKey = configKey.split('.');
|
||||||
|
|
||||||
|
for(var i=0;i<configKey.length;i++) {
|
||||||
|
if(_.has(configPath, configKey[i])) {
|
||||||
|
configPath = configPath[configKey[i]];
|
||||||
|
} else {
|
||||||
|
configPath = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(configPath);
|
||||||
|
},
|
||||||
|
|
||||||
|
'setConfig': function(configKey, newOption, callback) {
|
||||||
|
var configPath = dbot.customConfig,
|
||||||
|
oldOption = null;
|
||||||
|
configKey = configKey.split('.');
|
||||||
|
|
||||||
|
for(var i=0;i<configKey.length-1;i++) {
|
||||||
|
if(!_.has(configPath, configKey[i])) {
|
||||||
|
configPath[configKey[i]] = {};
|
||||||
|
}
|
||||||
|
configPath = configPath[configKey[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_.has(configPath, configKey[i])) {
|
||||||
|
oldOption = configPath[configKey[i]];
|
||||||
|
}
|
||||||
|
configPath[configKey[i]] = newOption;
|
||||||
|
|
||||||
|
this.internalAPI.saveConfig();
|
||||||
|
dbot.reloadModules();
|
||||||
|
callback(null, oldOption);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
'saveConfig': function() {
|
||||||
|
var config = dbot.customConfig;
|
||||||
|
fs.writeFileSync('config.json', JSON.stringify(config, null, ' '));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
dbot.api.timers.addTimer(60000, function() {
|
||||||
|
dbot.save(function() {});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new admin(dbot);
|
||||||
|
};
|
311
modules-stock/admin/commands.js
Normal file
311
modules-stock/admin/commands.js
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
var fs = require('fs'),
|
||||||
|
_ = require('underscore')._,
|
||||||
|
sys = require('sys'),
|
||||||
|
process = require('process'),
|
||||||
|
exec = require('child_process').exec;
|
||||||
|
|
||||||
|
var commands = function(dbot) {
|
||||||
|
var noChangeConfig = [ 'servers', 'name', 'moduleNames' ];
|
||||||
|
|
||||||
|
var commands = {
|
||||||
|
// Join a channel
|
||||||
|
'~join': function(event) {
|
||||||
|
var channel = event.params[1];
|
||||||
|
if(_.has(event.allChannels, channel)) {
|
||||||
|
event.reply(dbot.t('already_in_channel', {'channel': channel}));
|
||||||
|
} else {
|
||||||
|
dbot.instance.join(event, channel);
|
||||||
|
event.reply(dbot.t('join', {'channel': channel}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Leave a channel
|
||||||
|
'~part': function(event) {
|
||||||
|
var channel = event.params[1];
|
||||||
|
if(!_.has(event.allChannels, channel)) {
|
||||||
|
event.reply(dbot.t('not_in_channel', {'channel': channel}));
|
||||||
|
} else {
|
||||||
|
event.instance.part(event, channel);
|
||||||
|
event.reply(dbot.t('part', {'channel': channel}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Op admin caller in given channel
|
||||||
|
'~opme': function(event) {
|
||||||
|
var channel = event.params[1];
|
||||||
|
|
||||||
|
// If given channel isn't valid just op in current one.
|
||||||
|
if(!_.has(event.allChannels, channel)) {
|
||||||
|
channel = event.channel.name;
|
||||||
|
}
|
||||||
|
dbot.instance.mode(event, channel, ' +o ' + event.user);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Do a git pull and reload
|
||||||
|
'~greload': function(event) {
|
||||||
|
exec("git pull", function (error, stdout, stderr) {
|
||||||
|
exec("git submodule update", function (error, stdout, stderr) {
|
||||||
|
event.reply(dbot.t('gpull'));
|
||||||
|
commands['~reload'](event);
|
||||||
|
commands['~version'](event);
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Display commit information for part of dbot
|
||||||
|
'~version': function(event){
|
||||||
|
var cmd = "git log --pretty=format:'%h (%s): %ar' -n 1 -- ";
|
||||||
|
if(event.params[1]){
|
||||||
|
var input = event.params[1].trim();
|
||||||
|
if(_.has(dbot.modules, input.split("/")[0])){
|
||||||
|
cmd += "modules/"+input;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
cmd += input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(cmd, function(error, stdout, stderr){
|
||||||
|
if(stdout.length > 0){
|
||||||
|
event.reply(stdout);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
event.reply(dbot.t("no_version"));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~status': function(event) {
|
||||||
|
var moduleName = event.params[1];
|
||||||
|
if(_.has(dbot.status, moduleName)) {
|
||||||
|
var status = dbot.status[moduleName];
|
||||||
|
if(status === true) {
|
||||||
|
event.reply(dbot.t('status_good', {
|
||||||
|
'module': moduleName,
|
||||||
|
'reason': status
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('status_bad', {
|
||||||
|
'module': moduleName,
|
||||||
|
'reason': status
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("status_unloaded"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Reload DB, translations and modules.
|
||||||
|
'~reload': function(event) {
|
||||||
|
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
|
||||||
|
dbot.reloadModules();
|
||||||
|
process.nextTick(function() {
|
||||||
|
event.reply(dbot.t('reload'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Say something in a channel
|
||||||
|
'~say': function(event) {
|
||||||
|
var channel = event.params[1];
|
||||||
|
if(event.params[1] === "@") {
|
||||||
|
channel = event.channel.name;
|
||||||
|
}
|
||||||
|
var message = event.params.slice(2).join(' ');
|
||||||
|
dbot.say(event.server, channel, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Load new module
|
||||||
|
'~load': function(event) {
|
||||||
|
var moduleName = event.params[1];
|
||||||
|
if(!_.include(dbot.config.moduleNames, moduleName)) {
|
||||||
|
dbot.customConfig.moduleNames.push(moduleName);
|
||||||
|
this.internalAPI.saveConfig();
|
||||||
|
dbot.reloadModules();
|
||||||
|
process.nextTick(function() {
|
||||||
|
if(dbot.status[moduleName] === true) {
|
||||||
|
event.reply(dbot.t('load_module', { 'moduleName': moduleName }));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('load_failed', { 'module': moduleName }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if(moduleName == 'web') {
|
||||||
|
event.reply(dbot.t('already_loaded_web'));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('already_loaded', { 'moduleName': moduleName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Unload a loaded module
|
||||||
|
'~unload': function(event) {
|
||||||
|
var moduleNames = dbot.config.moduleNames;
|
||||||
|
var moduleName = event.params[1];
|
||||||
|
if(_.include(moduleNames, moduleName)) {
|
||||||
|
var moduleDir = '../' + moduleName + '/';
|
||||||
|
try {
|
||||||
|
var cacheKey = require.resolve(moduleDir + moduleName);
|
||||||
|
delete require.cache[cacheKey];
|
||||||
|
} catch(err) { }
|
||||||
|
|
||||||
|
dbot.customConfig.moduleNames = _.without(dbot.config.moduleNames, moduleName);
|
||||||
|
this.internalAPI.saveConfig();
|
||||||
|
dbot.reloadModules();
|
||||||
|
|
||||||
|
process.nextTick(function() {
|
||||||
|
event.reply(dbot.t('unload_module', { 'moduleName': moduleName }));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('unload_error', { 'moduleName': moduleName }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*** Config options ***/
|
||||||
|
|
||||||
|
'~setconfig': function(event) {
|
||||||
|
var configPath = event.input[1],
|
||||||
|
newOption = event.input[2];
|
||||||
|
|
||||||
|
event.reply(event.input[1]);
|
||||||
|
|
||||||
|
if(!_.include(noChangeConfig, configPath)) {
|
||||||
|
this.internalAPI.getCurrentConfig(configPath, function(config) {
|
||||||
|
if(config !== null) {
|
||||||
|
// Convert to boolean type if config item boolean
|
||||||
|
if(_.isBoolean(config)) {
|
||||||
|
newOption = (newOption == "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to integer type is config item integer
|
||||||
|
if(_.isNumber(config)) {
|
||||||
|
newOption = parseInt(newOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_.isArray(config)) {
|
||||||
|
event.reply(dbot.t("config_array", { "alternate": "pushconfig" }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("no_config_key", {'path': configPath}));
|
||||||
|
configPath = configPath.split('.');
|
||||||
|
if(_.has(dbot.config.modules, configPath[0])) {
|
||||||
|
configPath.splice(0, 0, 'modules');
|
||||||
|
event.input[1] = configPath.join('.');
|
||||||
|
commands['~setconfig'](event);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('new_config_key', { 'key': configPath }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalAPI.setConfig(configPath, newOption, function(err) {
|
||||||
|
event.reply(configPath + ": " + config + " -> " + newOption);
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("config_lock"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~pushconfig': function(event) {
|
||||||
|
var configPath = event.input[1],
|
||||||
|
newOption = event.input[2];
|
||||||
|
|
||||||
|
if(!_.include(noChangeConfig, configPath)) {
|
||||||
|
this.internalAPI.getCurrentConfig(configPath, function(config) {
|
||||||
|
if(config !== null) {
|
||||||
|
if(_.isArray(config)) {
|
||||||
|
event.reply(configPath + ": " + config + " << " + newOption);
|
||||||
|
config.push(newOption);
|
||||||
|
this.internalAPI.setConfig(configPath, config, function(err) {});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("config_array", { "alternate": "setconfig" }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("no_config_key", { 'path': configPath }));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("config_lock"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~showconfig': function(event) {
|
||||||
|
var configPath = event.params[1];
|
||||||
|
if(configPath) {
|
||||||
|
this.internalAPI.getCurrentConfig(configPath, function(config) {
|
||||||
|
if(config !== null) {
|
||||||
|
if(_.isArray(config)) {
|
||||||
|
event.reply(dbot.t("config_keys_location", {
|
||||||
|
"path": configPath,
|
||||||
|
"value": config
|
||||||
|
}));
|
||||||
|
} else if(_.isObject(config)) {
|
||||||
|
event.reply(dbot.t("config_keys_location", {
|
||||||
|
"path": configPath,
|
||||||
|
"value": _.keys(config)
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("config_keys_location", {
|
||||||
|
"path": configPath,
|
||||||
|
"value": config
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("no_config_key", {'path': configPath}));
|
||||||
|
|
||||||
|
configPath = configPath.split('.');
|
||||||
|
if(_.has(dbot.config.modules, configPath[0])) {
|
||||||
|
configPath.splice(0, 0, 'modules');
|
||||||
|
} else {
|
||||||
|
configPath.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.params[1] = configPath.join('.');
|
||||||
|
commands['~showconfig'](event);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("config_keys_location", {
|
||||||
|
"path": "root",
|
||||||
|
"value": _.keys(dbot.config)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~savemodules': function(event) {
|
||||||
|
fs.readFile('config.json', 'utf-8', function(err, config) {
|
||||||
|
config = JSON.parse(config);
|
||||||
|
config.moduleNames = _.keys(dbot.modules);
|
||||||
|
fs.writeFile('config.json', JSON.stringify(config, null, ' '), function() {
|
||||||
|
event.reply(dbot.t('modules_saved', { 'modules': _.keys(dbot.modules) }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~die': function(event) {
|
||||||
|
event.reply('BRB coconut hunting...');
|
||||||
|
setTimeout(3000, function() {
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_.each(commands, function(command) {
|
||||||
|
command.access = 'admin';
|
||||||
|
});
|
||||||
|
|
||||||
|
commands['~showconfig'].access = 'moderator';
|
||||||
|
commands['~join'].access = 'moderator';
|
||||||
|
commands['~part'].access = 'moderator';
|
||||||
|
commands['~opme'].access = 'moderator';
|
||||||
|
commands['~say'].access = 'moderator';
|
||||||
|
|
||||||
|
commands['~pushconfig'].regex = [/pushconfig ([^ ]+) ([^ ]+)/, 3];
|
||||||
|
commands['~setconfig'].regex = [/setconfig ([^ ]+) ([^ ]+)/, 3];
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return commands(dbot);
|
||||||
|
}
|
5
modules-stock/admin/config.json
Normal file
5
modules-stock/admin/config.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ignorable": false,
|
||||||
|
"dbType": "redis",
|
||||||
|
"dependencies": [ "command" ]
|
||||||
|
}
|
230
modules-stock/admin/strings.json
Normal file
230
modules-stock/admin/strings.json
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
{
|
||||||
|
"join": {
|
||||||
|
"en": "Joined {channel}",
|
||||||
|
"es" : "Entrado en {channel}",
|
||||||
|
"na'vi": "fpxäkìm {channel}(nemfa)",
|
||||||
|
"cy": "Wedi ymuno {channel}",
|
||||||
|
"nl": "{channel} binnengekomen",
|
||||||
|
"de": "{channel} beigetreten",
|
||||||
|
"fr": "{channel} rejoint",
|
||||||
|
"it": "Aderito a {channel}"
|
||||||
|
},
|
||||||
|
"part": {
|
||||||
|
"en": "Left {channel}",
|
||||||
|
"es" : "Abandonada {channel}",
|
||||||
|
"na'vi": "Hum {channel}",
|
||||||
|
"cy": "Wedi gadael {channel}",
|
||||||
|
"nl": "{channel} verlaten",
|
||||||
|
"de": "{channel} verlassen",
|
||||||
|
"fr": "{channel} quitté",
|
||||||
|
"it": "uscito da {channel}"
|
||||||
|
},
|
||||||
|
"gpull": {
|
||||||
|
"en": "Git pulled that shit.",
|
||||||
|
"es": "Hecho git pull en esta mierda.",
|
||||||
|
"na'vi": "Gìtìl fì'uti stamarsìm.",
|
||||||
|
"cy": "Wedi tynnu git yr cach na i gyd",
|
||||||
|
"nl": "Git heeft die zooi binnengehaald.",
|
||||||
|
"de": "Git hat es gezogen",
|
||||||
|
"fr": "Git a pullé cette merde",
|
||||||
|
"it": "Git ha pullato questa coglionata"
|
||||||
|
},
|
||||||
|
"reload": {
|
||||||
|
"en": "Reloaded that shit.",
|
||||||
|
"es": "Recargado esta mierda.",
|
||||||
|
"na'vi": "Oel fìuti stìyeftxaw.",
|
||||||
|
"cy": "Ail-lwytho'r cach na",
|
||||||
|
"nl": "Die zooi opnieuw geladen.",
|
||||||
|
"de": "Neu geladen",
|
||||||
|
"fr": "Bordel rechargé",
|
||||||
|
"it": "Ricaricato questa puttanata"
|
||||||
|
},
|
||||||
|
"load_module": {
|
||||||
|
"en": "Loaded new module: {moduleName}",
|
||||||
|
"es": "Cargado módulo nuevo: {moduleName}",
|
||||||
|
"na'vi": "Oel {moduleName}it amip stìyeftxaw.",
|
||||||
|
"cy": "Wedi llwytho modiwl newydd: {moduleName}",
|
||||||
|
"nl": "Nieuwe module geladen: {moduleName}",
|
||||||
|
"de": "Neues Modul geladen: {moduleName}",
|
||||||
|
"fr": "Nouveau module chargé : {moduleName}",
|
||||||
|
"it": "Caricato nuovo modulo: {moduleName}"
|
||||||
|
},
|
||||||
|
"unload_module": {
|
||||||
|
"en": "Turned off module: {moduleName}",
|
||||||
|
"es": "Descargado módulo: {moduleName}",
|
||||||
|
"na'vi": "Oel {moduleName} tswìya'.",
|
||||||
|
"cy": "Wedi troi ffwrdd y modiwl: {moduleName}",
|
||||||
|
"nl": "Module uitgeschakeld: {moduleName}",
|
||||||
|
"de": "Modul ausgeschaltet: {moduleName}",
|
||||||
|
"fr": "Module déchargé : {moduleName}",
|
||||||
|
"it": "Inabilitato modulo: {moduleName}"
|
||||||
|
},
|
||||||
|
"unload_error": {
|
||||||
|
"en": "{moduleName} isn't loaded. Idiot.",
|
||||||
|
"es": "{moduleName} no está cargado. Idiota.",
|
||||||
|
"na'vi": "Oel {moduleName}it omum. Nga skxawng lu.",
|
||||||
|
"cy": "Di {moduleName} ddim wedi llwytho. Twpsyn",
|
||||||
|
"nl": "{moduleName} is niet geladen. Idioot.",
|
||||||
|
"de": "{moduleName} ist nicht geladen, du Idiot.",
|
||||||
|
"fr": "{moduleName} n'est pas chargé. Idiot.",
|
||||||
|
"it": "{moduleName} non è caricato. Idiota"
|
||||||
|
},
|
||||||
|
"banned": {
|
||||||
|
"en": "{user} banned from {command}",
|
||||||
|
"es": "{user} está prohibido de usar {command}",
|
||||||
|
"na'vi": "{command}ìri {user} ke tung.",
|
||||||
|
"cy": "{user} wedi ei gohurio o {command}",
|
||||||
|
"nl": "{user} mag {command} niet meer gebruiken",
|
||||||
|
"de": "{user} wurde von {command} gebannt",
|
||||||
|
"fr": "{user} a été interdit d'utiliser {command}",
|
||||||
|
"it": "{user} bandito da {command}"
|
||||||
|
},
|
||||||
|
"unbanned": {
|
||||||
|
"en": "{user} unbanned from {command}",
|
||||||
|
"es": "{user} no está prohibido de user {command}",
|
||||||
|
"na'vi": "{command}ìri {user} tung set.",
|
||||||
|
"cy": "{user} wedi ei dad-wahardd o {command}",
|
||||||
|
"nl": "{user} mag {command} weer gebruiken",
|
||||||
|
"de": "{user} wurde von {command} entbannt",
|
||||||
|
"fr": "{user} peut de nouveau utiliser {command}",
|
||||||
|
"it": "{user} riammesso da {command}"
|
||||||
|
},
|
||||||
|
"unban_error": {
|
||||||
|
"en": "{user} wasn't banned from that command, fool.",
|
||||||
|
"es": "{user} no fue prohibido de esta instrucción, tont@.",
|
||||||
|
"na'vi": "{user} fìtsu'oti tamung srekrr, nga skxawng lu.",
|
||||||
|
"cy": "Nid oedd {user} wedi ei wahardd o'r gorchymyn yna, twpsyn",
|
||||||
|
"nl": "{user} mag dat commando sowieso al gebruiken, mafketel.",
|
||||||
|
"de": "{user} wurde nicht von {command} gebannt, du Trottel",
|
||||||
|
"fr": "{user} n'a pas été interdit d'utiliser cette commande, imbécile.",
|
||||||
|
"it": "{user} non è stato bandito da utilizzare questo commando, imbecille."
|
||||||
|
},
|
||||||
|
"qlock": {
|
||||||
|
"en": "Locked quote category: {category}",
|
||||||
|
"es": "Cerrado la categoría: {category}",
|
||||||
|
"na'vi": "{category}ìri oel 'upxareti fmoli",
|
||||||
|
"cy": "Categori wedi cloi: {category}",
|
||||||
|
"nl": "Quote categorie vergrendeld: {category}",
|
||||||
|
"de": "Zitat-Kategorie geschlossen: {category}",
|
||||||
|
"fr": "Catégorie de citations verrouillée : {category}",
|
||||||
|
"it": "Categoria di citazione bloccata : {category}"
|
||||||
|
},
|
||||||
|
"already_in_channel": {
|
||||||
|
"en": "I'm already in {channel}",
|
||||||
|
"na'vi": "Oel {channel}it tok li",
|
||||||
|
"cy": "Rydw i eisoes yn {channel}",
|
||||||
|
"nl": "Ik ben al in {channel}",
|
||||||
|
"de": "Ich bin schon in {channel}",
|
||||||
|
"fr": "Je suis déjà dans {channel}",
|
||||||
|
"it": "Sono già in {channel}"
|
||||||
|
},
|
||||||
|
"not_in_channel": {
|
||||||
|
"en": "I'm not in {channel}",
|
||||||
|
"na'vi": "Oel {channel}it ke tok",
|
||||||
|
"cy": "Rydw i ddim yn {channel}",
|
||||||
|
"nl": "Ik ben niet aanwezig in {channel}",
|
||||||
|
"de": "Ich bin noch nicht in {channel}",
|
||||||
|
"fr": "Je ne suis pas dans {channel}",
|
||||||
|
"it": "Non sono in {channel}"
|
||||||
|
},
|
||||||
|
"already_loaded_web": {
|
||||||
|
"en": "WHY CAN'T I LOAD ALL THIS WEB? (web already loaded)",
|
||||||
|
"na'vi": "PELUN OEL KE TSUN OMUM FÌWETIT NÌWOTX (wetìri oe omum li)",
|
||||||
|
"cy": "PAM ALLA I DDIM YN LWYTHO POB Y WE? (We eisoes yn lwytho)",
|
||||||
|
"nl": "AL DIT WEB WORDT ME TOCH EEN BEETJE TE VEEL! (web is al geladen)",
|
||||||
|
"de": "WARUM KANN DAS NICHT GELADEN WERDEN? (bereits geladen)",
|
||||||
|
"fr": "POURQUOI EST-CE QUE JE PEUX PAS CHARGER TOUT CE WEB? (web déjà chargé)",
|
||||||
|
"it": "PERCHÉ NON POSSO CARICARE TUTTE QUESTO WEB? (web già caricato)"
|
||||||
|
},
|
||||||
|
"already_loaded": {
|
||||||
|
"en": "{moduleName} is already loaded.",
|
||||||
|
"na'vi": "Oel omum teri {moduleName}it li.",
|
||||||
|
"cy": "{moduleName} eisoes yn lwytho",
|
||||||
|
"nl": "{moduleName} is al geladen.",
|
||||||
|
"de": "{moduleName} ist bereits geladen.",
|
||||||
|
"fr": "{moduleName} est déjà chargé.",
|
||||||
|
"it": "{moduleName} già caricato."
|
||||||
|
},
|
||||||
|
"no_version": {
|
||||||
|
"en": "No version information or queried module not loaded.",
|
||||||
|
"cy": "Dim gwybodaeth fersiwn neu modiwl holodd dim yn lwytho",
|
||||||
|
"de": "Keine Informationen verfügbar oder gewünschtes Modul wurde noch nicht geladen.",
|
||||||
|
"fr": "Aucune information de version ou module demandé non chargé.",
|
||||||
|
"it": "Informazione sulla versione non è disponibile o modulo richiesto non ancora caricato"
|
||||||
|
},
|
||||||
|
"status_good": {
|
||||||
|
"en": "{module} status: Shit looks good",
|
||||||
|
"cy": "{module} statws: Cachu yn edrych yn dda",
|
||||||
|
"de": "Sieht gut aus",
|
||||||
|
"fr": "Statut de {module}: Cette merde tourne bien",
|
||||||
|
"it": "Stato di {modulo}: Funky gallo come sono bello stamattina"
|
||||||
|
},
|
||||||
|
"status_bad": {
|
||||||
|
"en": "{module} status: Failed to load: {reason}",
|
||||||
|
"cy": "{module} statws: Methu i lwytho: {reason}",
|
||||||
|
"de": "{module} Status: Konnte nicht geladen werden: {reason}",
|
||||||
|
"fr": "Statut de {module}: échec de chargement : {reason}",
|
||||||
|
"it": "Stato di {module}: Caricamento fallito: {reason}"
|
||||||
|
},
|
||||||
|
"status_unloaded": {
|
||||||
|
"en": "Either that module wasn't on the roster or shit is totally fucked.",
|
||||||
|
"cy": "Naill ai heb fod modiwl oedd ar y rhestr, neu cachu yn gwbl torrodd",
|
||||||
|
"de": "Entweder ist das Modul nicht auf der Liste oder die Kacke ist am dampfen",
|
||||||
|
"fr": "Soit ce module n'est pas sur la liste, soit tout est complètement niqué",
|
||||||
|
"it": "O questo modulo non è sulla lista, o sono cazzi amari"
|
||||||
|
},
|
||||||
|
"load_failed": {
|
||||||
|
"en": "Failed to load {module}. See 'status {module}'.",
|
||||||
|
"cy": "Methu i lwytho {module}. Gwelwch 'status {module}'.",
|
||||||
|
"de": "Konnte {module} nicht laden. Siehe 'status {module}'.",
|
||||||
|
"fr": "Echec du chargement de {module}. Voir 'status {module}'.",
|
||||||
|
"it": "Caricamento di {module} non riuscito. Vedi 'stato {module}'."
|
||||||
|
},
|
||||||
|
"no_config_key": {
|
||||||
|
"en": "{path} doesn't exist bro",
|
||||||
|
"cy": "{path} cyfluniad yn bodoli, fy mrawd",
|
||||||
|
"de": "{path} existiert nicht, Bruder",
|
||||||
|
"fr": "{path} n'existe pas, fréro",
|
||||||
|
"it": "{path} non esiste, fratello"
|
||||||
|
},
|
||||||
|
"config_array": {
|
||||||
|
"en": "Config option is an array. Try '{alternate}'.",
|
||||||
|
"cy": "Opsiwn cyfluniad ydy'r amrywiaeth. Rhowch gynnig ar '{alternate}'.",
|
||||||
|
"de": "Einstellung ist ein Array, probiere '{alternate}' aus.",
|
||||||
|
"fr": "L'option de configuration est un array. Essaye '{alternate}'.",
|
||||||
|
"it": "L'opzione di configurazione è un array. Prova '{alternate}'."
|
||||||
|
},
|
||||||
|
"config_lock": {
|
||||||
|
"en": "This config option cannot be altered while the bot is running.",
|
||||||
|
"cy": "Ni all yr opsiwn cyfluniad yn cael ei newid tra bod y bot yn rhedeg.",
|
||||||
|
"de": "Diese Option kann während der Benutzung des Bots nicht verändert werden",
|
||||||
|
"fr": "Cette option de configuration ne peut pas être changée pendant que le bot est activé.",
|
||||||
|
"it": "Questa opzione di configurazione non può essere alterata mentre il bot è attivo."
|
||||||
|
},
|
||||||
|
"no_config_path": {
|
||||||
|
"en": "Config path doesn't exist bro",
|
||||||
|
"cy": "Nid yw llwybr cyfluniad yn bodoli, fy mrawd",
|
||||||
|
"de": "Konfigurationspfad nicht vorhanden, Bruder",
|
||||||
|
"fr": "Le chemin de configuration n'existe pas, fréro",
|
||||||
|
"it": "Percorso di configurazione non esiste, fratello"
|
||||||
|
},
|
||||||
|
"new_config_key": {
|
||||||
|
"en": "Warning: Creating new config key: {key}.",
|
||||||
|
"fr": "Attention : création d'une nouvelle clé de configuration : {key}.",
|
||||||
|
"it": "Attenzione : Creazione di una nuova chiave di configurazione: {key}.",
|
||||||
|
"de": "Achtung: Neuer Konfigurationsschlüssel erstellt: {key}."
|
||||||
|
},
|
||||||
|
"config_keys_location": {
|
||||||
|
"en": "Config keys in {path}: {value}",
|
||||||
|
"cy": "Allweddi cyfluniad yn {path}: {value}",
|
||||||
|
"de": "Die Konfiguration in {path}: {value}",
|
||||||
|
"fr": "Clés de configuration dans {path}: {value}",
|
||||||
|
"it": "Chiave di configurazione in {path}: {value}"
|
||||||
|
},
|
||||||
|
"modules_saved": {
|
||||||
|
"en": "Currently loaded modules now default: {modules}",
|
||||||
|
"fr": "Les modules actuellement chargés sont maintenant chargés par défaut : {modules}",
|
||||||
|
"it": "I moduli attualmente caricati sono adesso predefiniti: {modules}",
|
||||||
|
"de": "Die derzeit geladenen Module sind nun der Standart: {modules}"
|
||||||
|
}
|
||||||
|
}
|
38
modules-stock/announce/announce.js
Normal file
38
modules-stock/announce/announce.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Name: Announce
|
||||||
|
* Description: Announce things every now and again
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var announce = function(dbot) {
|
||||||
|
this.announces = dbot.config.modules.announce.announces;
|
||||||
|
this.lineCount = 0;
|
||||||
|
this.lastAnnounce = {};
|
||||||
|
_.each(dbot.config.servers, function(v, k) {
|
||||||
|
this.lastAnnounce[k] = {};
|
||||||
|
_.each(this.announces[k], function(announce, channel) {
|
||||||
|
this.lastAnnounce[k][channel] = announce.distance;
|
||||||
|
}, this)
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
if(_.has(this.lastAnnounce[event.server], event.channel)) {
|
||||||
|
this.lastAnnounce[event.server][event.channel]--;
|
||||||
|
if(this.lastAnnounce[event.server][event.channel] == 0) {
|
||||||
|
var announce = this.config.announces[event.server][event.channel];
|
||||||
|
this.lastAnnounce[event.server][event.channel] = announce.distance;
|
||||||
|
|
||||||
|
dbot.api.quotes.getQuote(announce.category, function(quote) {
|
||||||
|
if(quote) {
|
||||||
|
dbot.say(event.server, event.channel, quote);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new announce(dbot);
|
||||||
|
};
|
10
modules-stock/announce/config.json
Normal file
10
modules-stock/announce/config.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"announces": {
|
||||||
|
"aberwiki": {
|
||||||
|
"#test": {
|
||||||
|
"category": "test",
|
||||||
|
"distance": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
modules-stock/api/README.md
Normal file
45
modules-stock/api/README.md
Normal 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.
|
73
modules-stock/api/api.js
Normal file
73
modules-stock/api/api.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Name: API
|
||||||
|
* Description: Expose DBot API functionality with a REST API
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var api = function(dbot) {
|
||||||
|
this.onLoad = function() {
|
||||||
|
dbot.modules.web.app.get('/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/api', { 'name': dbot.config.name, 'api': externalApi });
|
||||||
|
});
|
||||||
|
|
||||||
|
dbot.modules.web.app.get('/api/:module/:method', function(req, res) {
|
||||||
|
var module = req.params.module,
|
||||||
|
method = req.params.method,
|
||||||
|
reqArgs = req.query,
|
||||||
|
body = { 'err': null, 'data': null };
|
||||||
|
|
||||||
|
if(!_.has(dbot.api, module)) {
|
||||||
|
body.err = 'No such API module';
|
||||||
|
} else if(!_.has(dbot.api[module], method)) {
|
||||||
|
body.err = 'No such API function in ' + module;
|
||||||
|
} else if(dbot.api[module][method].external !== true) {
|
||||||
|
body.err = 'API function ' + module + '.' + method +
|
||||||
|
' not enabled for external access';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!body.err) {
|
||||||
|
var func = dbot.api[module][method],
|
||||||
|
paramNames = func.extMap,
|
||||||
|
args = [];
|
||||||
|
|
||||||
|
_.each(reqArgs, function(arg, name) {
|
||||||
|
var callbackIndex = paramNames.indexOf(name);
|
||||||
|
if(callbackIndex != -1) {
|
||||||
|
args[callbackIndex] = decodeURIComponent(arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var callbackIndex = paramNames.indexOf('callback');
|
||||||
|
if(callbackIndex != -1) {
|
||||||
|
args[callbackIndex] = function() {
|
||||||
|
body.data = Array.prototype.slice.call(arguments, 0);
|
||||||
|
if(_.isObject(body.data[0]) && _.has(body.data[0], 'err')) {
|
||||||
|
body.err = body.data[0].err;
|
||||||
|
}
|
||||||
|
res.json(body);
|
||||||
|
};
|
||||||
|
func.apply(null, args);
|
||||||
|
} else {
|
||||||
|
body.data = func.apply(null, args);
|
||||||
|
res.json(body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.json(body);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new api(dbot);
|
||||||
|
};
|
16
modules-stock/april/april.js
Normal file
16
modules-stock/april/april.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
var april = function(dbot) {
|
||||||
|
this.listener = function(event) {
|
||||||
|
var match = event.message.match(/^i'?( a)?m (an? )?([^ ]+)/i);
|
||||||
|
if(match) {
|
||||||
|
dbot.say(event.server, 'operserv', 'svsnick ' + event.user + ' ' + match[3]);
|
||||||
|
setTimeout(function() {
|
||||||
|
event.reply('Hi ' + match[3] + ', I\'m ' + dbot.config.name + '!');
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new april(dbot);
|
||||||
|
};
|
157
modules-stock/atheme/atheme.js
Normal file
157
modules-stock/atheme/atheme.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: atheme
|
||||||
|
* Description: atheme mode references & retrieve channel flags
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
async = require('async');
|
||||||
|
|
||||||
|
var atheme = function(dbot) {
|
||||||
|
this.flagStack = {};
|
||||||
|
this.hostStack = {};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'getChannelFlags': function(server, channel, callback) {
|
||||||
|
if(!_.has(this.flagStack, server)) this.flagStack[server] = {};
|
||||||
|
if(_.has(this.flagStack[server], channel)) { // Already an active flag call
|
||||||
|
this.flagStack[server][channel].callbacks.push(callback);
|
||||||
|
} else {
|
||||||
|
this.flagStack[server][channel] = {
|
||||||
|
'flags': {},
|
||||||
|
'callbacks': [ callback ],
|
||||||
|
'timeout': null,
|
||||||
|
'working': false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.say(server, 'chanserv', 'FLAGS ' + channel);
|
||||||
|
this.flagStack[server][channel].timeout = setTimeout(function() { // Delete callback if no response
|
||||||
|
if(_.has(this.flagStack[server], channel) && this.flagStack[server][channel].working == false) {
|
||||||
|
_.each(this.flagStack[server][channel].callbacks, function(callback) {
|
||||||
|
callback(true, null);
|
||||||
|
});
|
||||||
|
delete this.flagStack[server][channel];
|
||||||
|
}
|
||||||
|
}.bind(this), 20000);
|
||||||
|
},
|
||||||
|
|
||||||
|
'getVHosts': function(server, mask, callback) {
|
||||||
|
if(!_.has(this.hostStack, server)) this.hostStack[server] = {};
|
||||||
|
if(_.has(this.hostStack[server], mask)) { // Already an active host call
|
||||||
|
this.hostStack[server][channel].callbacks.push(callback);
|
||||||
|
} else {
|
||||||
|
this.hostStack[server][mask] = {
|
||||||
|
'users': [],
|
||||||
|
'callbacks': [ callback ],
|
||||||
|
'timeout': null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.say(server, 'hostserv', 'LISTVHOST ' + mask);
|
||||||
|
this.hostStack[server][mask].timeout = setTimeout(function() { // Delete callback if no response
|
||||||
|
if(_.has(this.hostStack[server], mask)) {
|
||||||
|
_.each(this.hostStack[server][mask].callbacks, function(callback) {
|
||||||
|
callback(true, null);
|
||||||
|
});
|
||||||
|
delete this.hostStack[server][mask];
|
||||||
|
}
|
||||||
|
}.bind(this), 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~chanserv': function(event) {
|
||||||
|
if(_.has(this.config.chanserv, event.input[1])) {
|
||||||
|
event.reply('ChanServ flag ' + event.input[1] + ': ' + this.config.chanserv[event.input[1]]);
|
||||||
|
} else {
|
||||||
|
event.reply('I don\'t know anything about ' + event.input[1]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~chanmode': function(event) {
|
||||||
|
if(_.has(this.config.chanmodes, event.input[1])) {
|
||||||
|
event.reply('Channel Mode ' + event.input[1] + ': ' + this.config.chanmodes[event.input[1]]);
|
||||||
|
} else {
|
||||||
|
event.reply('I don\'t know anything about ' + event.input[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands['~chanserv'].regex = [/^chanserv (\+.)/, 2];
|
||||||
|
this.commands['~chanmode'].regex = [/^chanmode (\+.)/, 2];
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
if(event.action === 'NOTICE') {
|
||||||
|
if(event.user === 'ChanServ') {
|
||||||
|
var flags = event.params.match(/(\d+)\s+([^ ]+)\s+(\+\w+)\s+\((\#[\w\.]+)\)/),
|
||||||
|
end = event.params.match(/end of \u0002(\#[\w\.]+)\u0002 flags listing/i);
|
||||||
|
|
||||||
|
if(flags && _.has(this.flagStack[event.server], flags[4])) {
|
||||||
|
this.flagStack[event.server][flags[4]].flags[flags[2]] = flags[3];
|
||||||
|
} else if(end) {
|
||||||
|
if(_.has(this.flagStack[event.server], end[1])) {
|
||||||
|
this.flagStack[event.server][end[1]].working = true;
|
||||||
|
// Parse wildcard hostmasks to nicks
|
||||||
|
var allFlags = this.flagStack[event.server][end[1]].flags,
|
||||||
|
hostMasks = {};
|
||||||
|
|
||||||
|
_.each(allFlags, function(f, u) { // TODO: combine to one loop
|
||||||
|
if(u.indexOf('*!*@') !== -1) {
|
||||||
|
hostMasks[u] = f;
|
||||||
|
delete allFlags[u];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
async.each(_.keys(hostMasks), function(hostMask, done) {
|
||||||
|
this.api.getVHosts(event.server, hostMask.split('@')[1], function(err, users) {
|
||||||
|
_.each(users, function(user) {
|
||||||
|
allFlags[user] = hostMasks[hostMask];
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}.bind(this), function() {
|
||||||
|
_.each(this.flagStack[event.server][end[1]].callbacks, function(callback) {
|
||||||
|
callback(null, this.flagStack[event.server][end[1]].flags);
|
||||||
|
}.bind(this));
|
||||||
|
clearTimeout(this.flagStack[event.server][end[1]].timeout);
|
||||||
|
delete this.flagStack[event.server][end[1]];
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(event.user === 'HostServ') {
|
||||||
|
_.each(this.hostStack[event.server], function(el, mask) {
|
||||||
|
if(event.params.match(mask)) {
|
||||||
|
var user = event.params.match(/- ([^ ]+)/),
|
||||||
|
end = event.params.match(/matches for pattern/);
|
||||||
|
|
||||||
|
if(user) {
|
||||||
|
this.hostStack[event.server][mask].users.push(user[1]);
|
||||||
|
} else if(end) {
|
||||||
|
_.each(this.hostStack[event.server][mask].callbacks, function(callback) {
|
||||||
|
callback(null, this.hostStack[event.server][mask].users);
|
||||||
|
}, this);
|
||||||
|
clearTimeout(this.hostStack[event.server][mask].timeout);
|
||||||
|
delete this.hostStack[event.server][mask];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
} else { // PRIVMSG
|
||||||
|
console.log(event.message);
|
||||||
|
var akill = event.message.match(/([^ ]+) AKILL:ADD: ([^ ]+) \(reason: (.+)(\) )\(duration: ([^,)]+)/);
|
||||||
|
if(event.channel == '#services' && akill) {
|
||||||
|
console.log(akill);
|
||||||
|
var channel = dbot.config.servers[event.server].admin_channel;
|
||||||
|
dbot.api.users.getUser(akill[1] + '.' + event.server, function(err, user) {
|
||||||
|
dbot.api.report.notify('ban', 'tripsit', user, channel, dbot.t('akill', {
|
||||||
|
'host': akill[2],
|
||||||
|
'reason': akill[3],
|
||||||
|
'duration': akill[5]
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
this.on = ['NOTICE', 'PRIVMSG'];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new atheme(dbot);
|
||||||
|
};
|
50
modules-stock/atheme/config.json
Normal file
50
modules-stock/atheme/config.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"chanserv": {
|
||||||
|
"+v": "Enables use of the voice/devoice commands.",
|
||||||
|
"+V": "Enables automatic voice.",
|
||||||
|
"+h": "Enables use of the halfop/dehalfop commands.",
|
||||||
|
"+H": "Enables automatic halfop.",
|
||||||
|
"+o": "Enables use of the op/deop commands.",
|
||||||
|
"+O": "Enables automatic op.",
|
||||||
|
"+a": "Enables use of the protect/deprotect commands.",
|
||||||
|
"+q": "Enables use of the owner/deowner commands.",
|
||||||
|
"+s": "Enables use of the set command.",
|
||||||
|
"+i": "Enables use of the invite and getkey commands.",
|
||||||
|
"+r": "Enables use of the kick, kickban, ban and unban commands.",
|
||||||
|
"+r": "Enables use of the ban and unban commands.",
|
||||||
|
"+r": "Enables use of the unban command.",
|
||||||
|
"+R": "Enables use of the recover and clear commands.",
|
||||||
|
"+f": "Enables modification of channel access lists.",
|
||||||
|
"+t": "Enables use of the topic and topicappend commands.",
|
||||||
|
"+A": "Enables viewing of channel access lists.",
|
||||||
|
"+S": "Marks the user as a successor.",
|
||||||
|
"+F": "Grants full founder access.",
|
||||||
|
"+b": "Enables automatic kickban."
|
||||||
|
},
|
||||||
|
"chanmodes": {
|
||||||
|
"+b": "channel ban",
|
||||||
|
"+c": "colour filter",
|
||||||
|
"+e": "ban exemption",
|
||||||
|
"+f": "channel forwarding",
|
||||||
|
"+F": "allow anybody to forward to this",
|
||||||
|
"+g": "allow anybody to invite",
|
||||||
|
"+i": "invite only",
|
||||||
|
"+I": "invite exception (invex)",
|
||||||
|
"+j": "join throttling",
|
||||||
|
"+k": "key (channel password)",
|
||||||
|
"+l": "channel member limit",
|
||||||
|
"+L": "large ban list",
|
||||||
|
"+m": "moderated",
|
||||||
|
"+n": "no external messages",
|
||||||
|
"+o": "channel operator",
|
||||||
|
"+p": "paranoid channel",
|
||||||
|
"+P": "permanent channel",
|
||||||
|
"+q": "quiet",
|
||||||
|
"+Q": "block forwarded users",
|
||||||
|
"+r": "block unidentified",
|
||||||
|
"+s": "secret channel",
|
||||||
|
"+t": "topic limit",
|
||||||
|
"+v": "voice",
|
||||||
|
"+z": "reduced moderation"
|
||||||
|
}
|
||||||
|
}
|
5
modules-stock/atheme/strings.json
Normal file
5
modules-stock/atheme/strings.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"akill": {
|
||||||
|
"en": "{host} has been AKilled for {duration} due to \"{reason}\""
|
||||||
|
}
|
||||||
|
}
|
78
modules-stock/command/README.md
Normal file
78
modules-stock/command/README.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
## Command
|
||||||
|
|
||||||
|
Handles the command execution logic for DBot.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
Command flow:
|
||||||
|
|
||||||
|
1. Does the input match a command key in the loaded commands?
|
||||||
|
* If command not found and quotes is loaded, attempt to print quote of given
|
||||||
|
command name
|
||||||
|
2. Is the user banned from running the given command?
|
||||||
|
3. Is the user ignoring the command?
|
||||||
|
4. Is the channel ignoring the command?
|
||||||
|
5. Does the use have the access level to run the command?
|
||||||
|
6. Is the command set as disabled?
|
||||||
|
7. Apply regex to the command, pass into event object.
|
||||||
|
* If regex does not apply, show usage info.
|
||||||
|
8. Run the command.
|
||||||
|
|
||||||
|
This is the only module which is force loaded, even if it's not specified in
|
||||||
|
the configuration file.
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
#### useNickserv: false
|
||||||
|
Use the nickserv module to ensure a user is logged into their account before
|
||||||
|
running any elevated commands. Note you will still have to load and configure
|
||||||
|
the nickserv module yourself.
|
||||||
|
|
||||||
|
#### accessOutput: false
|
||||||
|
Show a message to a user if they attempt to run a command they don't have the
|
||||||
|
access level for.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~usage [command]
|
||||||
|
Show usage information for a given command.
|
||||||
|
|
||||||
|
#### ~help [command|module]
|
||||||
|
Link module help for a module given either the module name or the name of a
|
||||||
|
command belonging to a module.
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
#### isBanned(user, command)
|
||||||
|
Return whether a user is currently banned from a given commands.
|
||||||
|
|
||||||
|
#### hasAccess(user, command)
|
||||||
|
Return whether a user has the access level (moderator, admin) to run a given
|
||||||
|
command.
|
||||||
|
|
||||||
|
#### isIgnoring(user, command)
|
||||||
|
Return whether a user is currently marked as ignoring a given command.
|
||||||
|
|
||||||
|
#### addHook(command, callback)
|
||||||
|
This API function allows you to hook functions into DBot commands. For example,
|
||||||
|
you may add a hook to post on Identica when a new quote is added to the database
|
||||||
|
with the ~qadd command. As a less useful example, here is how you might add a
|
||||||
|
hook to log to the console every time someone uses the reload command:
|
||||||
|
|
||||||
|
dbot.api.command.addHook('reload', function() {
|
||||||
|
console.log('Reload run!');
|
||||||
|
});
|
||||||
|
|
||||||
|
Hook arguments are populated by the return values of the functions they are
|
||||||
|
hooked into, and command hooks are not run if the command explicitly returns
|
||||||
|
'false.' For example, the ~qadd command returns *[ key, quote ]*, and the hook
|
||||||
|
function will be called with these variables given in the order they were
|
||||||
|
returned, so you would retrieve the key and the quote from a hook to ~qadd like
|
||||||
|
this:
|
||||||
|
|
||||||
|
dbot.api.command.addHook('~qadd', function(key, quote) { ...
|
||||||
|
|
||||||
|
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. If
|
||||||
|
the target command does not exist (for example if its module was not loaded),
|
||||||
|
the hook will not be added and no errors will be thrown.
|
73
modules-stock/command/api.js
Normal file
73
modules-stock/command/api.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var api = function(dbot) {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Does the user have the correct access level to use the command?
|
||||||
|
*/
|
||||||
|
'hasAccess': function(event, command, callback) {
|
||||||
|
var accessNeeded = dbot.commands[command].access,
|
||||||
|
allowedNicks,
|
||||||
|
user = event.rUser;
|
||||||
|
|
||||||
|
if(_.isUndefined(accessNeeded) || accessNeeded == null) {
|
||||||
|
return callback(true);
|
||||||
|
} else if(!_.isFunction(accessNeeded)) {
|
||||||
|
if(_.has(dbot.access, accessNeeded)) {
|
||||||
|
accessNeeded = dbot.access[accessNeeded];
|
||||||
|
} else {
|
||||||
|
return callback(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allowedNicks = accessNeeded(event);
|
||||||
|
|
||||||
|
if(!_.include(allowedNicks, user.primaryNick) && !_.include(allowedNicks, user.currentNick)) {
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
if(_.has(dbot.modules, 'nickserv') && this.config.useNickserv == true) {
|
||||||
|
dbot.api.nickserv.auth(user.server, user.currentNick, function(result, primary) {
|
||||||
|
if(result == true && primary == user.primaryNick) {
|
||||||
|
callback(true);
|
||||||
|
} else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply Regex to event message, store result. Return false if it doesn't
|
||||||
|
* apply.
|
||||||
|
*/
|
||||||
|
'applyRegex': function(commandName, event) {
|
||||||
|
var applies = false;
|
||||||
|
event.message = event.message.substring(1);
|
||||||
|
if(_.has(dbot.commands[commandName], 'regex')) {
|
||||||
|
var cRegex = dbot.commands[commandName].regex;
|
||||||
|
if(_.isArray(cRegex) && cRegex.length === 2) {
|
||||||
|
var q = event.message.valMatch(cRegex[0], cRegex[1]);
|
||||||
|
if(q) {
|
||||||
|
applies = true;
|
||||||
|
event.input = q;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var q = event.message.match(cRegex);
|
||||||
|
if(q) {
|
||||||
|
applies = true;
|
||||||
|
event.input = q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applies = true;
|
||||||
|
}
|
||||||
|
return applies;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return api(dbot);
|
||||||
|
};
|
163
modules-stock/command/command.js
Normal file
163
modules-stock/command/command.js
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Command
|
||||||
|
* Description: An essential module which maps PRIVMSG input to an appropriate
|
||||||
|
* command and then runs that command, given the user isn't banned from or
|
||||||
|
* ignoring that command.
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
cDomain = require('domain').create();
|
||||||
|
|
||||||
|
var command = function(dbot) {
|
||||||
|
/**
|
||||||
|
* Run the appropriate command given the input.
|
||||||
|
*/
|
||||||
|
this.listener = function(event) {
|
||||||
|
var commandName = event.params[0];
|
||||||
|
if(commandName.charAt(0) != this.config.commandPrefix || this.config.passiveMode == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
commandName = commandName.substring(1);
|
||||||
|
|
||||||
|
this.api.hasAccess(event, commandName, function(hasAccess) {
|
||||||
|
dbot.api.ignore.isUserIgnoring(event.rUser, commandName, function(isIgnoring) {
|
||||||
|
dbot.api.ignore.isUserBanned(event.rUser, commandName, function(isBanned) {
|
||||||
|
|
||||||
|
if(isBanned) {
|
||||||
|
if(this.config.banOutput && commandName != this.config.commandPrefix) {
|
||||||
|
event.reply(dbot.t('command_ban', {'user': event.user}));
|
||||||
|
}
|
||||||
|
} else if(!hasAccess) {
|
||||||
|
if(this.config.accessOutput) {
|
||||||
|
event.reply(dbot.t('access_denied', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
} else if(!isIgnoring && _.has(dbot.commands, commandName) && !dbot.commands[commandName].disabled) {
|
||||||
|
if(!_.has(dbot.commands, commandName)) {
|
||||||
|
if(_.has(dbot.modules, 'quotes')) {
|
||||||
|
var key = event.message.substring(1);
|
||||||
|
dbot.api.quotes.getInterpolatedQuote(event.server,
|
||||||
|
event.channel.name, event.user, key, function(quote) {
|
||||||
|
if(quote) {
|
||||||
|
event.reply(key + ': ' + quote);
|
||||||
|
} else if(_.has(dbot.modules, 'spelling')) {
|
||||||
|
var commands = _.keys(dbot.commands),
|
||||||
|
winner = false,
|
||||||
|
closestMatch = Infinity;
|
||||||
|
|
||||||
|
_.each(commands, function(command) {
|
||||||
|
var distance = dbot.api.spelling.distance(commandName, command);
|
||||||
|
if(distance < closestMatch) {
|
||||||
|
closestMatch = distance;
|
||||||
|
winner = command;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(closestMatch < 1) {
|
||||||
|
event.reply(commandName + ' not found. Did you mean ' + winner + '?');
|
||||||
|
return;
|
||||||
|
} else if(_.has(dbot.modules, 'quotes')) {
|
||||||
|
dbot.api.link.udLookup(key, function(word, definition) {
|
||||||
|
if(word) {
|
||||||
|
event.reply(key + '[UD]: ' + definition);
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('category_not_found', { 'category': key }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.api.applyRegex(commandName, event)) {
|
||||||
|
try {
|
||||||
|
cDomain.run(function() {
|
||||||
|
var command = dbot.commands[commandName],
|
||||||
|
results;
|
||||||
|
if(_.has(command, 'resolver')) {
|
||||||
|
event.res = [];
|
||||||
|
command.resolver(event, function(err) {
|
||||||
|
if(!err) {
|
||||||
|
results = command.apply(dbot.modules[command.module], [event]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
results = command.apply(dbot.modules[command.module], [event]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
if(dbot.config.debugMode == true) {
|
||||||
|
var stack = err.stack.split('\n').slice(1, dbot.config.debugLevel + 1);
|
||||||
|
|
||||||
|
event.reply('- Error in ' + commandName + ':');
|
||||||
|
event.reply('- Message: ' + err);
|
||||||
|
|
||||||
|
_.each(stack, function(stackLine, index) {
|
||||||
|
event.reply('- Stack[' + index + ']: ' +
|
||||||
|
stackLine.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!_.include(['reload', 'load', 'unload', 'setconfig'], commandName)) dbot.api.event.emit('command', [ event ]);
|
||||||
|
} else {
|
||||||
|
if(commandName !== this.config.commandPrefix) {
|
||||||
|
if(_.has(dbot.usage, commandName)) {
|
||||||
|
event.reply('Usage: ' + dbot.usage[commandName]);
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('syntax_error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this);
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
// Not sure this is the right place for this. Perhaps they should be in
|
||||||
|
// another file?
|
||||||
|
|
||||||
|
cDomain.on('error', function(err) {
|
||||||
|
console.log(err); // Hmm
|
||||||
|
if(_.has(dbot.modules, 'log')) {
|
||||||
|
// can't really get context
|
||||||
|
var server = _.keys(dbot.config.servers)[0];
|
||||||
|
dbot.api.log.log(server, dbot.config.name, '[\u00034ERR\u000f] ' + err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dbot.access = {
|
||||||
|
'admin': function(event) {
|
||||||
|
return dbot.config.admins;
|
||||||
|
},
|
||||||
|
|
||||||
|
'moderator': function(event) {
|
||||||
|
return [].concat(dbot.access.admin(event), dbot.config.moderators);
|
||||||
|
},
|
||||||
|
|
||||||
|
'power_user': function(event) {
|
||||||
|
return [].concat(dbot.access.admin(event), dbot.access.moderator(event), dbot.config.power_users);
|
||||||
|
},
|
||||||
|
|
||||||
|
'voice': function(event) {
|
||||||
|
return [].concat(dbot.access.admin(event), dbot.access.moderator(event), dbot.access.power_user(event),
|
||||||
|
_.chain(event.channel.nicks)
|
||||||
|
.filter(function(nick) {
|
||||||
|
return nick.op == true || nick.voice == true;
|
||||||
|
})
|
||||||
|
.pluck('name')
|
||||||
|
.value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new command(dbot);
|
||||||
|
};
|
70
modules-stock/command/commands.js
Normal file
70
modules-stock/command/commands.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
var _ = require('underscore')._,
|
||||||
|
request = require('request');
|
||||||
|
|
||||||
|
var commands = function(dbot) {
|
||||||
|
var commands = {
|
||||||
|
'usage': function(event) {
|
||||||
|
var commandName = event.params[1];
|
||||||
|
if(_.has(dbot.usage, commandName)) {
|
||||||
|
event.reply(dbot.t('usage', {
|
||||||
|
'command': commandName,
|
||||||
|
'usage': dbot.usage[commandName]
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('no_usage_info', {
|
||||||
|
'command': commandName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~commands': function(event) {
|
||||||
|
var name = event.params[1];
|
||||||
|
if(_.has(dbot.modules, name)) {
|
||||||
|
var commands = _.keys(dbot.commands);
|
||||||
|
commands = _.filter(commands, function(cName) {
|
||||||
|
return dbot.commands[cName].module == name;
|
||||||
|
});
|
||||||
|
event.reply(dbot.t('module_commands', {
|
||||||
|
'module': name,
|
||||||
|
'commands': commands.join(', ')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('loaded_modules', {
|
||||||
|
'modules': _.keys(dbot.modules).join(', ')
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~help': function(event) {
|
||||||
|
var moduleName = event.params[1];
|
||||||
|
if(!moduleName || !_.has(dbot.modules, moduleName)) {
|
||||||
|
event.reply(dbot.t('usage', {
|
||||||
|
'command': this.config.commandPrefix + 'help',
|
||||||
|
'usage': this.config.commandPrefix + 'help [module]'
|
||||||
|
}));
|
||||||
|
event.reply(dbot.t('loaded_modules', {
|
||||||
|
'modules': _.keys(dbot.modules).join(', ')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
var helpLink = dbot.config.repoRoot +
|
||||||
|
'blob/master/modules/' + moduleName + '/README.md';
|
||||||
|
if(dbot.config.modules[moduleName].help) {
|
||||||
|
helpLink = dbot.config.modules[moduleName].help;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check it exists
|
||||||
|
event.reply(dbot.t('help_link', {
|
||||||
|
'module': moduleName,
|
||||||
|
'link': helpLink
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
commands['usage'].regex = [/usage ([^ ]+)/, 2];
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return commands(dbot);
|
||||||
|
};
|
9
modules-stock/command/config.json
Normal file
9
modules-stock/command/config.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ignorable": false,
|
||||||
|
"dependencies": [ "event", "ignore", "users" ],
|
||||||
|
"useNickserv": false,
|
||||||
|
"accessOutput": false,
|
||||||
|
"banOutput": false,
|
||||||
|
"passiveMode": false,
|
||||||
|
"commandPrefix": "~"
|
||||||
|
}
|
78
modules-stock/command/strings.json
Normal file
78
modules-stock/command/strings.json
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"command_ban": {
|
||||||
|
"en": "{user} is banned from using this command. Commence incineration.",
|
||||||
|
"es": "{user} está prohibido de usar esta instrucción. urrently loaded modules now default: {modules}.",
|
||||||
|
"na'vi": "Tsu'ori {user} ke tung. Nga skxawng lu.",
|
||||||
|
"cy": "Mae {user} wedi ei gohurio gan ddefnyddio'r gorchymun yma. Cychwyn orfflosgiad",
|
||||||
|
"nl": "{user} mag dit commando niet meer gebruiken. Bereid het verbrandingsritueel voor.",
|
||||||
|
"de": "{user} darf diesen Befehl nicht benutzen. Verbrennung einleiten",
|
||||||
|
"fr": "{user} est interdit d'utiliser cette commande. Commencer l'incinération.",
|
||||||
|
"it": "A {user} è stato interdetto di utilizzare questo commando. Iniziare incenerimento."
|
||||||
|
},
|
||||||
|
"syntax_error": {
|
||||||
|
"en": "Invalid syntax. Initiate incineration.",
|
||||||
|
"es": "Sintaxis no válida. Iniciar incineración.",
|
||||||
|
"na'vi": "Ngeyä pamrel keyawr lu. Nga skxawng lu.",
|
||||||
|
"cy": "Cystrawen annilys. Cychwyn orfflosgiad",
|
||||||
|
"nl": "Ongeldige syntax. Bereid het verbrandingsritueel voor.",
|
||||||
|
"de": "Syntax ungültig. Verbrennung einleiten",
|
||||||
|
"fr": "Syntaxe invalide. Initier l'incinération.",
|
||||||
|
"it": "Sintassi non valida. Iniziare incenerimento"
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"en": "Usage for {command}: {usage}.",
|
||||||
|
"na'vi": "Nga tsun sivar ìlä {command}: {usage}.",
|
||||||
|
"cy": "Defnydd o {command}: {usage}.",
|
||||||
|
"nl": "{command} wordt op de volgende manier gebruikt: {usage}.",
|
||||||
|
"de": "Benutzung von {command}: [usage}.",
|
||||||
|
"fr": "Utilisation de {command}: {usage}.",
|
||||||
|
"it": "Utilizzo di {command}: {usage}."
|
||||||
|
},
|
||||||
|
"no_usage_info": {
|
||||||
|
"en": "No usage information found for {command}.",
|
||||||
|
"na'vi": "Oel ke tsun sivar {comamnd}it",
|
||||||
|
"cy": "Ni chanfuwyd gwybodaeth am ddefnydd o {command}",
|
||||||
|
"nl": "Geen gebruiksinformatie gevonden voor {command}.",
|
||||||
|
"de": "Keine Gebrauchsanweisung gefunden für {command}.",
|
||||||
|
"fr": "Aucune information d'utilisation trouvée pour {command}",
|
||||||
|
"it": "Nessuna informazione d' utilizzo trovata per {command}"
|
||||||
|
},
|
||||||
|
"help_link": {
|
||||||
|
"en": "Help for {module}: {link}",
|
||||||
|
"na'vi": "{module}ä srungìl {link} it tok",
|
||||||
|
"cy": "Cymorth am {module}: {link}",
|
||||||
|
"nl": "Hulp voor {module}: {link}",
|
||||||
|
"de": "Hilfe für {module}: {link}",
|
||||||
|
"fr": "Aide pour {module}: {link}",
|
||||||
|
"it": "Aiuto per {module}: {link}"
|
||||||
|
},
|
||||||
|
"no_help": {
|
||||||
|
"en": "No help found for {module}.",
|
||||||
|
"na'vi": "Fì{module}ìri oel ke tsun run srungit",
|
||||||
|
"cy": "Ni chanfuwyd cymorth am {module}",
|
||||||
|
"nl": "Geen hulp gevonden voor {module}.",
|
||||||
|
"de": "Keine Hilfe gefunden für {module}.",
|
||||||
|
"fr": "Aucune aide trouvée pour {module}.",
|
||||||
|
"it": "Nessun aiuto trovato per {module}."
|
||||||
|
},
|
||||||
|
"loaded_modules": {
|
||||||
|
"en": "Loaded modules: {modules}.",
|
||||||
|
"cy": "Modiwlau sy'n lwythodd: {modules}.",
|
||||||
|
"nl": "Geladen modules: {modules}.",
|
||||||
|
"de": "Module geladen: {modules}.",
|
||||||
|
"fr": "Modules chargés: {modules}.",
|
||||||
|
"it": "Moduli caricati: {modules}."
|
||||||
|
},
|
||||||
|
"access_denied": {
|
||||||
|
"en": "{user}: You don't have the access level to run this command.",
|
||||||
|
"fr": "{user}: Vous n'avez pas le niveau d'accès requis pour utiliser cette commande.",
|
||||||
|
"it": "{user}: Non hai il livello d'accesso neccessario per utilizzare questo commando.",
|
||||||
|
"de": "{user}: Du hast nicht die notwendigen Rechte um diesen Befehl zu benutzen."
|
||||||
|
},
|
||||||
|
"module_commands": {
|
||||||
|
"en": "Commands in {module}: {commands}.",
|
||||||
|
"fr": "Commandes de {module}: {commands}.",
|
||||||
|
"it": "Commandi di {module}: {commands}.",
|
||||||
|
"de": "Befehle in {module}: {commands}."
|
||||||
|
}
|
||||||
|
}
|
4
modules-stock/command/usage.json
Normal file
4
modules-stock/command/usage.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"~usage": "~usage [command]",
|
||||||
|
"~help": "~help [module]"
|
||||||
|
}
|
15
modules-stock/crypto/README.md
Normal file
15
modules-stock/crypto/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## Crypto
|
||||||
|
|
||||||
|
Cryptography!
|
||||||
|
|
||||||
|
### Description
|
||||||
|
This module calculates different hashes or ciphertexts for some algorithms.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~hash [algorithm] [text]
|
||||||
|
Calculate the hash of the given text using [algorithm].
|
||||||
|
|
||||||
|
#### ~random [number]
|
||||||
|
Gives [number] bytes of cryptographically strong pseudo-random data as hex string.
|
||||||
|
|
44
modules-stock/crypto/crypto.js
Normal file
44
modules-stock/crypto/crypto.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Crypto
|
||||||
|
* Description: Allows the magic of cryptography to take place.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var cr = require('crypto');
|
||||||
|
|
||||||
|
var crypto = function(dbot) {
|
||||||
|
this.commands = {
|
||||||
|
'~hash': function(event) {
|
||||||
|
var hash = event.params[1];
|
||||||
|
try {
|
||||||
|
var h = cr.createHash(hash);
|
||||||
|
var tohash = event.params.splice(2, event.params.length-1).join(' ');
|
||||||
|
h.update(tohash);
|
||||||
|
event.reply(hash+" of \""+tohash+"\" is: "+h.digest('hex'));
|
||||||
|
} catch(err) {
|
||||||
|
event.reply(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'~randomdata': function(event) {
|
||||||
|
try {
|
||||||
|
var count = parseInt(event.params[1]);
|
||||||
|
if(count > 222) {
|
||||||
|
event.reply("Sorry man, I can't paste that much random data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cr.randomBytes(count, function(err,buf) {
|
||||||
|
if(err) {
|
||||||
|
event.reply(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.reply(buf.toString('hex'));
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
event.reply(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new crypto(dbot);
|
||||||
|
};
|
6
modules-stock/crypto/usage.json
Normal file
6
modules-stock/crypto/usage.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"~md5": "~md5 [text]",
|
||||||
|
"~sha1": "~sha1 [text]",
|
||||||
|
"~sha256": "~sha256 [text]",
|
||||||
|
"~aes": "~aes \"[text]\" \"[key]\""
|
||||||
|
}
|
3
modules-stock/cspeed/config.json
Normal file
3
modules-stock/cspeed/config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"outputChannel": "#realitree"
|
||||||
|
}
|
62
modules-stock/cspeed/cspeed.js
Normal file
62
modules-stock/cspeed/cspeed.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var cspeed = function(dbot) {
|
||||||
|
this.watches = dbot.db.cspeed;
|
||||||
|
this.outputChannel = dbot.config.modules.cspeed.outputChannel;
|
||||||
|
this.counts = {};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'getCounts': function(callback) {
|
||||||
|
callback(this.counts);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.api['getCounts'].external = true;
|
||||||
|
this.api['getCounts'].extMap = [ 'callback' ];
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'addlpmwatch': function(event) {
|
||||||
|
var channel = event.params[1];
|
||||||
|
var key = event.server + '.' + channel;
|
||||||
|
|
||||||
|
if(!_.has(this.watches, key)) {
|
||||||
|
this.watches[key] = {
|
||||||
|
'server': event.server,
|
||||||
|
'channel': channel
|
||||||
|
}; // to be extended with warn nums etc
|
||||||
|
|
||||||
|
this.counts[key] = 0;
|
||||||
|
dbot.api.timers.addTimer(60000, function() {
|
||||||
|
dbot.say(event.server, this.outputChannel, channel + ' currently : ' + this.counts[key] + ' LPM');
|
||||||
|
this.counts[key] = 0;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
event.reply('Added speed watch for ' + channel);
|
||||||
|
} else {
|
||||||
|
event.reply('Already watching that channel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
var key = event.server + '.' + event.channel;
|
||||||
|
if(_.has(this.watches, key)) {
|
||||||
|
this.counts[key]++;
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
var watches = dbot.db.cspeed;
|
||||||
|
_.each(watches, function(watch) {
|
||||||
|
var key = watch.server + '.' + watch.channel;
|
||||||
|
this.counts[key] = 0;
|
||||||
|
dbot.api.timers.addTimer(60000, function() {
|
||||||
|
dbot.say(watch.server, dbot.db.cspeed.outputChannel, watch.channel + ': ' + this.counts[key] + 'LPM');
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new cspeed(dbot);
|
||||||
|
};
|
5
modules-stock/ctcp/README.md
Normal file
5
modules-stock/ctcp/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## CTCP
|
||||||
|
Responds to CTCP commands.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
At the moment only CTCP VERSION, CTCP PING is handled in JSBot.
|
3
modules-stock/ctcp/config.json
Normal file
3
modules-stock/ctcp/config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignorable": true
|
||||||
|
}
|
39
modules-stock/ctcp/ctcp.js
Normal file
39
modules-stock/ctcp/ctcp.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
var ctcp = function(dbot) {
|
||||||
|
this.listener = function(event) {
|
||||||
|
var matches = event.message.match(/\u0001[\w]+\u0001/);
|
||||||
|
if(matches) {
|
||||||
|
// We need the CTCP command
|
||||||
|
var question = matches[0];
|
||||||
|
// Cut \u0001 characters from command
|
||||||
|
question = question.slice(1,question.length-1);
|
||||||
|
switch(question) {
|
||||||
|
case 'CLIENTINFO':
|
||||||
|
event.replyNotice("\u0001CLIENTINFO SOURCE VERSION USERINFO\u0001");
|
||||||
|
break;
|
||||||
|
case 'FINGER':
|
||||||
|
event.replyNotice("\u0001FINGER STOP FINGERING ME BRO\u0001");
|
||||||
|
break;
|
||||||
|
case 'SOURCE':
|
||||||
|
event.replyNotice("\u0001SOURCE "+dbot.config.repoRoot+"\u0001");
|
||||||
|
break;
|
||||||
|
case 'TIME':
|
||||||
|
var d = new Date();
|
||||||
|
event.replyNotice("\u0001TIME "+d.toISOString()+"\u0001");
|
||||||
|
break;
|
||||||
|
case 'USERINFO':
|
||||||
|
event.replyNotice("\u0001USERINFO "+dbot.config.name+"\u0001");
|
||||||
|
break;
|
||||||
|
case 'VERSION':
|
||||||
|
event.replyNotice("\u0001VERSION "+dbot.config.version+"\u0001");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
event.replyNotice("\u0001"+question+" Idk what you want. Try CLIENTINFO.\u0001");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new ctcp(dbot);
|
||||||
|
};
|
31
modules-stock/dent/README.md
Normal file
31
modules-stock/dent/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
## Dent
|
||||||
|
|
||||||
|
Post dents.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
Allows the posting of dents to Identica. Easily abused for posting status
|
||||||
|
messages to Twitter by linking the Identica account.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~dent [text]
|
||||||
|
Post the given text to Identica.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
#### username
|
||||||
|
Identica username to post with.
|
||||||
|
|
||||||
|
#### password
|
||||||
|
Identica password to post with.
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
#### post(content)
|
||||||
|
Post the given content to Identica.
|
||||||
|
|
||||||
|
### Hooks
|
||||||
|
|
||||||
|
#### ~qadd
|
||||||
|
Posts new quote additions.
|
8
modules-stock/dent/config.json
Normal file
8
modules-stock/dent/config.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"username": "youruserhere",
|
||||||
|
"password": "yourpasswordhere",
|
||||||
|
"dependencies": [ "link" ],
|
||||||
|
"ignorable": true,
|
||||||
|
"dentQuotes": true,
|
||||||
|
"api": "http://quitter.se/"
|
||||||
|
}
|
77
modules-stock/dent/dent.js
Normal file
77
modules-stock/dent/dent.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
var request = require('request');
|
||||||
|
_ = require('underscore')._;
|
||||||
|
|
||||||
|
var dent = function(dbot) {
|
||||||
|
this.StatusRegex = {
|
||||||
|
identica: /\bhttps?:\/\/identi\.ca\/notice\/(\d+)\b/ig,
|
||||||
|
twitter: /\bhttps?:\/\/twitter\.com\/\w+\/status\/(\d+)\b/ig
|
||||||
|
};
|
||||||
|
|
||||||
|
this.StatusAPI = {
|
||||||
|
identica: "http://identi.ca/api/statuses/show.json",
|
||||||
|
twitter: "https://api.twitter.com/1/statuses/show.json"
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'post': function(content) {
|
||||||
|
var username = this.config.username,
|
||||||
|
password = this.config.password,
|
||||||
|
info,
|
||||||
|
auth = "Basic " +
|
||||||
|
new Buffer(username + ":" + password).toString("base64");
|
||||||
|
|
||||||
|
request.post({
|
||||||
|
'url': this.config.api + '/statuses/update.json?status=' +
|
||||||
|
escape(content),
|
||||||
|
'headers': {
|
||||||
|
'Authorization': auth
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error, response, body) {
|
||||||
|
console.log(body);
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.lookup = function(id, service, callback) {
|
||||||
|
request({
|
||||||
|
url: this.StatusAPI[service],
|
||||||
|
qs: {"id": id},
|
||||||
|
json: true
|
||||||
|
}, function(error, response, body) {
|
||||||
|
if (!error && response.statusCode == 200) {
|
||||||
|
if (_.has(body, 'text')) {
|
||||||
|
callback(service + " [" + body.user.screen_name + '] ' + body.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~dent': function(event) {
|
||||||
|
this.api.post(event.input[1]);
|
||||||
|
event.reply('Dent posted (probably).');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands['~dent'].regex = [/^dent (.+)$/, 2];
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
if(this.config.dentQuotes === true && _.has(dbot.modules, 'quotes')) {
|
||||||
|
dbot.api.event.addHook('qadd', function(key, text) {
|
||||||
|
if(text.indexOf('~~') == -1) {
|
||||||
|
this.api.post(key + ': ' + text);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(s in this.StatusRegex) {
|
||||||
|
dbot.api.link.addHandler(s, this.StatusRegex[s], function(matches, name, callback) {
|
||||||
|
this.lookup(matches[1], name, callback);
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new dent(dbot);
|
||||||
|
};
|
9
modules-stock/dice/README.md
Normal file
9
modules-stock/dice/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
## Dice
|
||||||
|
Rolls virtual dice.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
Rolls a virtual die and outputs the result to the channel.
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~roll <die type>
|
||||||
|
Rolls a die. 1d6 will be rolled by default.
|
4
modules-stock/dice/config.json
Normal file
4
modules-stock/dice/config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"ignorable": true,
|
||||||
|
"dependencies": [ "command" ]
|
||||||
|
}
|
100
modules-stock/dice/dice.js
Normal file
100
modules-stock/dice/dice.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
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["sides"] > 10000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diceSpec["count"] > 1000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (event) {
|
||||||
|
var rolls = [];
|
||||||
|
|
||||||
|
if (event.params.length === 1) {
|
||||||
|
event.params.push("d6");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i < event.params.length; i++) {
|
||||||
|
var diceSpec = parseDiceSpec(event.params[i]);
|
||||||
|
if (diceSpec === false) {
|
||||||
|
rolls.push([event.params[i], false]);
|
||||||
|
} else {
|
||||||
|
rolls.push([normalizeDiceSpec(event.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) {
|
||||||
|
event.reply(rolls[i][0] + ": invalid dice spec");
|
||||||
|
} else {
|
||||||
|
if (rolls[i][1].length > 1) {
|
||||||
|
var total = " (total " + _.reduce(rolls[i][1], function(memo, num){ return memo + num; }, 0);
|
||||||
|
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 = "";
|
||||||
|
}
|
||||||
|
event.reply(rolls[i][0] + ": " + rolls[i][1].join(" ") + total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands = commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new dice(dbot);
|
||||||
|
};
|
17
modules-stock/dns/README.md
Normal file
17
modules-stock/dns/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
## DNS
|
||||||
|
|
||||||
|
Performs and reports upon basic DNS functions.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module utilises the domain name system to discover basic information about
|
||||||
|
domain names and IP addresses.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~lookup [domain name]
|
||||||
|
Looks up the specified domain name in the domain name system. If a match is found,
|
||||||
|
the first corresponding A or AAAA record is displayed.
|
||||||
|
#### ~rdns [IP address]
|
||||||
|
Looks up the specified IP address in the domain name system. If a match is found,
|
||||||
|
the first corresponding rDNS domain name is displayed.
|
96
modules-stock/dns/dns.js
Normal file
96
modules-stock/dns/dns.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: DNS
|
||||||
|
* Description: Performs and reports on basic DNS functions.
|
||||||
|
*/
|
||||||
|
var dnsm = require('dns'),
|
||||||
|
request = require('request'),
|
||||||
|
http = require('http');
|
||||||
|
|
||||||
|
var dns = function(dbot) {
|
||||||
|
if(!_.has(dbot.db, 'ip')) {
|
||||||
|
dbot.db.ip = {};
|
||||||
|
}
|
||||||
|
var ips = dbot.db.ip;
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'getGeoIp': function(ip, callback) {
|
||||||
|
if(_.has(ips, ip)) {
|
||||||
|
body = ips[ip];
|
||||||
|
callback(ip + ' is located in '+ body.city + ', ' + body.country + '. Hostname: ' + body.hostname + '. ISP: ' + body.org);
|
||||||
|
} else {
|
||||||
|
request.get('http://ipinfo.io/'+ip, {
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if(!err && body) {
|
||||||
|
callback(ip + ' is located in '+ body.city + ', ' + body.country + '. Hostname: ' + body.hostname + '. ISP: ' + body.org);
|
||||||
|
} else {
|
||||||
|
callback('No info about ' + ip);
|
||||||
|
}
|
||||||
|
ips[ip] = body;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
var commands = {
|
||||||
|
'~lookup': function(event) {
|
||||||
|
domain = event.params[1];
|
||||||
|
dnsm.lookup(domain, function (error, addr) {
|
||||||
|
if (error) {
|
||||||
|
event.reply(dbot.t("lookup-error",{"domain": domain, "code": error.code}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("lookup",{"domain": domain, "address": addr}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~rdns': function(event) {
|
||||||
|
ip = event.params[1];
|
||||||
|
dnsm.reverse(ip, function (error, domain) {
|
||||||
|
if (error) {
|
||||||
|
event.reply(dbot.t("rdns-error",{"domain": domain, "ip": ip, "error": error.code}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("rdns",{"domain": domain, "ip": ip}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~geoip': function(event) {
|
||||||
|
var ip = event.params[1];
|
||||||
|
this.api.getGeoIp(ip, function(result) { event.reply(result); });
|
||||||
|
},
|
||||||
|
|
||||||
|
'~dnsbl': function(event) {
|
||||||
|
var revIp = event.input[1].trim().split('.').reverse().join('.');
|
||||||
|
dnsm.lookup(revIp + '.cbl.abuseat.org', function(err, res) {
|
||||||
|
if(!err && res) {
|
||||||
|
event.reply(event.input[1] + ' is listed as an abusive IP.');
|
||||||
|
} else {
|
||||||
|
event.reply(event.input[1] + ' does not seem to be a Naughty Nancy.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
commands['~dnsbl'].regex = [/^dnsbl ([\d\w\s\.-]*)/, 2];
|
||||||
|
this.commands = commands;
|
||||||
|
|
||||||
|
if(dbot.config.modules.dns.dnsblconn == true) {
|
||||||
|
this.listener = function(event) {
|
||||||
|
if(event.message.match('CLICONN')) {
|
||||||
|
var ip = event.message.match('CLICONN ([^ ]+).*?((?:[0-9]{1,3}\.){3}[0-9]{1,3}) users');
|
||||||
|
revIp = ip[2].trim().split('.').reverse().join('.');
|
||||||
|
dbot.say(event.server, '#dnsbl', 'DEBUG: Looking up ' + ip[2] + ' for ' + ip[1] + ' @ ' + revIp);
|
||||||
|
dnsm.lookup(revIp + '.cbl.abuseat.org', function(err, res) {
|
||||||
|
if(!err && res) {
|
||||||
|
dbot.say(event.server, '#dnsbl', 'ALERT: ' + ip[1] + ' connecting from ' + ip[2] + ' may well be NAUGHTY.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.on = 'NOTICE';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new dns(dbot);
|
||||||
|
};
|
32
modules-stock/dns/strings.json
Normal file
32
modules-stock/dns/strings.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"lookup-error": {
|
||||||
|
"en": "{domain} is \u000303AVAILABLE! \u000314({code})",
|
||||||
|
"cy": "{domain} \u000303AR GAEL! \u000314({code})",
|
||||||
|
"nl": "{domain} is \u000303BESCHIKBAAR! \u000314({code})",
|
||||||
|
"de": "{domain} ist \u000303VERFÜGBAR! \u000314({code})",
|
||||||
|
"fr": "{domain} est \u000303DISPONIBLE! \u000314({code})",
|
||||||
|
"it": "{domain} è \u000303DISPONIBILE! \u000314({code})"
|
||||||
|
},
|
||||||
|
"lookup": {
|
||||||
|
"en": "{domain} is \u000305TAKEN! \u000314({address})",
|
||||||
|
"cy": "Dydy {domain} \u000305DDIM AR GAEL! \u000314({address})",
|
||||||
|
"nl": "{domain} is \u000305BEZET! \u000314({address})",
|
||||||
|
"de": "{domain} ist \u000305BELEGT! \u000314({address})",
|
||||||
|
"fr": "{domain} est \u000305PRIS! \u000314({address})",
|
||||||
|
"it": "{domain} èt \u000305RISERVATO! \u000314({address})"
|
||||||
|
},
|
||||||
|
"rdns": {
|
||||||
|
"en": "{ip} \u2192 {domain}",
|
||||||
|
"fr": "{ip} \u2192 {domain}",
|
||||||
|
"it": "{ip} \u2192 {domain}",
|
||||||
|
"de":"{ip} \u2192 {domain}"
|
||||||
|
},
|
||||||
|
"rdns-error": {
|
||||||
|
"en": "Unable to lookup {ip}. \u000314({error})",
|
||||||
|
"cy": "Methu am-edrych {ip}. \u000314({error})",
|
||||||
|
"nl": "{ip} kan niet worden opgezocht. \u000314({error})",
|
||||||
|
"de": "Kann {ip} nicht auflösen. \u000314({error})",
|
||||||
|
"fr": "Impossible de rechercher {ip}. \u000314({error})",
|
||||||
|
"it": "Non è possibile cercare {ip}. \u000314({error})"
|
||||||
|
}
|
||||||
|
}
|
30
modules-stock/event/README.md
Normal file
30
modules-stock/event/README.md
Normal 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-stock/event/event.js
Normal file
28
modules-stock/event/event.js
Normal 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);
|
||||||
|
};
|
9
modules-stock/flashy/README.md
Normal file
9
modules-stock/flashy/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
## Flashy
|
||||||
|
|
||||||
|
Make big flashy text at people.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~flashy [color] [message]
|
||||||
|
Give a link to a page hosted by the flashy module which produces big flashing
|
||||||
|
text in the given colour.
|
45
modules-stock/flashy/flashy.js
Normal file
45
modules-stock/flashy/flashy.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Flashy
|
||||||
|
* Description: Makes pages with flashing text and that innit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var flashy = function(dbot) {
|
||||||
|
this.colourMap = {
|
||||||
|
'white': 'FFFFFF',
|
||||||
|
'red': 'FF0000',
|
||||||
|
'green': '00FF00',
|
||||||
|
'blue': '0000FF',
|
||||||
|
'yellow': 'FFFF00',
|
||||||
|
'pink': 'FFC0CB',
|
||||||
|
'magenta': 'FF00FF',
|
||||||
|
'purple': 'AA00FF',
|
||||||
|
'cyan': '00FFFF',
|
||||||
|
'orange': 'FFAA00',
|
||||||
|
'lime': 'AAFF00',
|
||||||
|
'grey': 'AAAAAA',
|
||||||
|
'infrared': '000000'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~flashy': function(event) {
|
||||||
|
var colour = event.input[1];
|
||||||
|
var text = event.input[2].trim().toUpperCase();
|
||||||
|
|
||||||
|
if(_.has(this.colourMap, colour)) {
|
||||||
|
event.reply(dbot.api.web.getUrl('flashy/' + colour + '/' +
|
||||||
|
encodeURIComponent(text)));
|
||||||
|
} else {
|
||||||
|
var possibleColours = _.keys(this.colourMap).join(', ') + '.';
|
||||||
|
event.reply('No such colour, brah. Available colours are: ' + possibleColours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands['~flashy'].regex = [/^flashy ([^ ]+) (.+)$/, 3];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new flashy(dbot);
|
||||||
|
};
|
21
modules-stock/flashy/pages.js
Normal file
21
modules-stock/flashy/pages.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var pages = function(dbot) {
|
||||||
|
return {
|
||||||
|
'/flashy/:colour/:text': function(req, res) {
|
||||||
|
if(!_.has(this.colourMap, req.params.colour)) {
|
||||||
|
req.params.colour = 'red';
|
||||||
|
}
|
||||||
|
var colour = this.colourMap[req.params.colour];
|
||||||
|
res.render('flashy', {
|
||||||
|
'name': dbot.config.name,
|
||||||
|
'colour': colour,
|
||||||
|
'text': decodeURIComponent(req.params.text)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return pages(dbot);
|
||||||
|
};
|
4
modules-stock/food/config.json
Normal file
4
modules-stock/food/config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"outputPrefix": "\u000311food\u000f",
|
||||||
|
"api_key": "http://food2fork.com/about/api"
|
||||||
|
}
|
60
modules-stock/food/food.js
Normal file
60
modules-stock/food/food.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Module name: Food
|
||||||
|
* Description: recipe search
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
request = require('request');
|
||||||
|
|
||||||
|
var food = function(dbot) {
|
||||||
|
this.commands = {
|
||||||
|
'~recipe': function(event) {
|
||||||
|
request.get('http://food2fork.com/api/search', {
|
||||||
|
'qs': {
|
||||||
|
'key': this.config.api_key,
|
||||||
|
'q': event.input[1]
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(error, response, body) {
|
||||||
|
if(_.isObject(body) && _.has(body, 'recipes') && body.recipes.length > 0) {
|
||||||
|
var num = _.random(0, body.recipes.length - 1),
|
||||||
|
recipe = body.recipes[num];
|
||||||
|
|
||||||
|
event.reply(dbot.t('recipe', {
|
||||||
|
'title': recipe.title,
|
||||||
|
'link': recipe.source_url
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('no_recipe'));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands['~recipe'].regex = [/^recipe (.+)$/, 2];
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
var match = event.message.match(new RegExp(dbot.config.name + ': what should i (have|eat|make)\\??( for (dinner|lunch|breakfast))?\\??', 'i'));
|
||||||
|
if(match) {
|
||||||
|
var page = _.random(0, 200);
|
||||||
|
request.get('http://food2fork.com/api/search', {
|
||||||
|
'qs': {
|
||||||
|
'key': this.config.api_key,
|
||||||
|
'page': page
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(error, response, body) {
|
||||||
|
if(_.isObject(body) && _.has(body, 'recipes') && body.recipes.length > 0) {
|
||||||
|
var num = _.random(0, body.recipes.length - 1),
|
||||||
|
recipe = body.recipes[num];
|
||||||
|
|
||||||
|
event.reply(event.user + ': You should make ' + recipe.title + '. See: ' + recipe.source_url);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new food(dbot);
|
||||||
|
};
|
8
modules-stock/food/strings.json
Normal file
8
modules-stock/food/strings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"recipe": {
|
||||||
|
"en": "{title} - {link}"
|
||||||
|
},
|
||||||
|
"no_recipe": {
|
||||||
|
"en": "No recipes found."
|
||||||
|
}
|
||||||
|
}
|
34
modules-stock/fpx/README.md
Normal file
34
modules-stock/fpx/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
## 500px
|
||||||
|
|
||||||
|
Adds various 500px functionality.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module provides a command which allows users to search for a random popular 500px photo.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
It has following dependencies:
|
||||||
|
+ [node-500px](https://github.com/ro-ka/node-500px)
|
||||||
|
|
||||||
|
### config.json
|
||||||
|
|
||||||
|
ignorable and consumerKey has to be configurated. It can be obtained at http://developers.500px.com
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"ignorable": true,
|
||||||
|
"api_key": "CONSUMERKEY_HERE"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
|
||||||
|
~r500px
|
||||||
|
Responds with a random popular 500px photo.
|
||||||
|
Example:
|
||||||
|
+ ~r500px
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
Photo by user etc.
|
4
modules-stock/fpx/config.json
Normal file
4
modules-stock/fpx/config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"ignorable": true,
|
||||||
|
"api_key": "CONSUMERKEY_HERE"
|
||||||
|
}
|
33
modules-stock/fpx/fpx.js
Normal file
33
modules-stock/fpx/fpx.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: 500px
|
||||||
|
* Description: Adds various 500px functionality.
|
||||||
|
* Requires: node-500px [http://mjgil.github.io/five-px/]
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
API500px = require('500px').API500px;
|
||||||
|
|
||||||
|
var fpx = function(dbot) {
|
||||||
|
this.commands = {
|
||||||
|
'~r500px': function(event) {
|
||||||
|
var random = Math.floor(Math.random() * 30);
|
||||||
|
this.api500px.photos.getPopular({'sort': 'created_at', 'rpp': '30'}, function(error, results) {
|
||||||
|
if (error) {
|
||||||
|
event.reply(dbot.t('5px_error'));
|
||||||
|
console.log(error);
|
||||||
|
} else {
|
||||||
|
var name = results.photos[random].name,
|
||||||
|
id = results.photos[random].id;
|
||||||
|
event.reply(dbot.t('5px_result',{'name':name,'id':id}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.onLoad = function() {
|
||||||
|
this.api500px = new API500px(this.config.api_key);
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new fpx(dbot);
|
||||||
|
};
|
8
modules-stock/fpx/strings.json
Normal file
8
modules-stock/fpx/strings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"5px_result":{
|
||||||
|
"en": "{name} - http://500px.com/photo/{id}"
|
||||||
|
},
|
||||||
|
"5px_error": {
|
||||||
|
"en": "Something went wrong :( Example: '~r500px'"
|
||||||
|
}
|
||||||
|
}
|
3
modules-stock/fpx/usage.json
Normal file
3
modules-stock/fpx/usage.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"~r500px": "~r500px"
|
||||||
|
}
|
18
modules-stock/github/LICENSE
Normal file
18
modules-stock/github/LICENSE
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Copyright (c) 2013 Douglas Gardner <douglas@chippy.ch>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
modules-stock/github/README.md
Normal file
28
modules-stock/github/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
## Github
|
||||||
|
|
||||||
|
Grabs interesting data from the GitHub API.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module for [depressionbot](https://github.com/reality/depressionbot) takes some interesting information about Github and parses it in a pleasing manner.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
#### defaultrepo
|
||||||
|
When repository information is lacking from the command, this repository will be used.
|
||||||
|
#### sortorder
|
||||||
|
Defines the behaviour of ~issue when no arguments are given. Options are ``created``, ``updated``, or ``comments``.
|
||||||
|
### Commands
|
||||||
|
#### ~commits
|
||||||
|
Returns the number of commits in the repository of the current depressionbot instance.
|
||||||
|
#### ~gstatus
|
||||||
|
Returns the [current status of Github](https://status.github.com), and a message explaining the current state of affairs.
|
||||||
|
#### ~issue (user/repo) [id]
|
||||||
|
Gives information about the isse pecified, from the default repository if one is not explicitly stated.
|
||||||
|
#### ~milestone [milestone name]
|
||||||
|
Returns milestone progress for any given milestone, with a link to the milestone in question.
|
||||||
|
#### ~repo (repo name)
|
||||||
|
Returns information about the repo given as a parameter. The repo should be specified as ``user/name``; for example, ``twitter/snowflake``.
|
||||||
|
#### ~repocount [user]
|
||||||
|
Returns the number of public Github repositories for the specified user.
|
||||||
|
### Dependencies
|
||||||
|
* [request](https://github.com/mikeal/request/):``$ npm install request``
|
8
modules-stock/github/config.json
Normal file
8
modules-stock/github/config.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"dependencies": [ "command" ],
|
||||||
|
"ignorable": true,
|
||||||
|
"help": "http://github.com/zuzak/dbot-github/blob/master/README.md",
|
||||||
|
"defaultrepo": "reality/dbot",
|
||||||
|
"sortorder": "updated",
|
||||||
|
"useragent": "reality/depressionbot github module"
|
||||||
|
}
|
192
modules-stock/github/github.js
Normal file
192
modules-stock/github/github.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Github
|
||||||
|
* Description: Retrieves interesting Github information
|
||||||
|
*/
|
||||||
|
var request = require('request'),
|
||||||
|
exec = require('child_process').exec;
|
||||||
|
|
||||||
|
var github = function(dbot) {
|
||||||
|
this.api = {
|
||||||
|
"githubStatus": function(callback){
|
||||||
|
var reqUrl = "https://status.github.com/api/last-message.json";
|
||||||
|
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
callback(JSON.parse(body));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var commands = {
|
||||||
|
'~repocount': function(event) {
|
||||||
|
var reqUrl = "https://api.github.com/users/" + event.params[1] + "/repos";
|
||||||
|
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
if(response.statusCode == "200") {
|
||||||
|
var result = JSON.parse(body);
|
||||||
|
event.reply(dbot.t("repocount",{"user": event.params[1], "count": result.length}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("usernotfound"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~repo': function(event) {
|
||||||
|
var repo = event.params[1];
|
||||||
|
if (typeof repo == 'undefined') {
|
||||||
|
repo = this.config.defaultrepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqUrl = "https://api.github.com/";
|
||||||
|
reqUrl += "repos/" + repo;
|
||||||
|
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
|
||||||
|
var data = JSON.parse(body);
|
||||||
|
if (data["fork"]) {
|
||||||
|
event.reply(dbot.t("forkedrepo",data));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("unforkedrepo",data));
|
||||||
|
}
|
||||||
|
// TODO: move this shizz into an api call
|
||||||
|
var longurl = "http://github.com/" + repo;
|
||||||
|
event.reply(dbot.t('location')+" "+longurl);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~gstatus': function(event) {
|
||||||
|
data = this.api.githubStatus(function(data){
|
||||||
|
event.reply(dbot.t("status"+data["status"]));
|
||||||
|
event.reply(data["body"]);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
'~milestone': function(event) {
|
||||||
|
var repo = this.config.defaultrepo;
|
||||||
|
var name = event.params[1];
|
||||||
|
if (event.params[2]){
|
||||||
|
repo = name;
|
||||||
|
name = event.params[2];
|
||||||
|
}
|
||||||
|
var reqUrl = "https://api.github.com/repos/";
|
||||||
|
reqUrl += repo + "/milestones";
|
||||||
|
|
||||||
|
request({"url": reqUrl, "headers":{"User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
var data = JSON.parse(body);
|
||||||
|
for (var section in data) {
|
||||||
|
var milestone = data[section];
|
||||||
|
if (milestone["title"] == name){
|
||||||
|
var str = "Milestone " + milestone["title"];
|
||||||
|
var progress = milestone["closed_issues"] / (milestone["open_issues"] + milestone["closed_issues"]);
|
||||||
|
progress = Math.round(progress*100);
|
||||||
|
var bar = "[";
|
||||||
|
for (var i = 10; i < 100; i += 10) {
|
||||||
|
if ((progress/i) > 1) {
|
||||||
|
bar += "█";
|
||||||
|
} else {
|
||||||
|
bar += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bar += "]";
|
||||||
|
str += " is " + bar + progress + "% complete";
|
||||||
|
|
||||||
|
event.reply(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~repocount': function(event) {
|
||||||
|
// TODO: add handling for non existent user
|
||||||
|
var reqUrl = "https://api.github.com/users/" + event.params[1] + "/repos";
|
||||||
|
request({"url": reqUrl,"headers": { "User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
var result = JSON.parse(body);
|
||||||
|
event.reply(event.params[1] + " has " + result.length + " public repositories.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~grate': function(event) {
|
||||||
|
request.get({"url":"https://api.github.com/rate_limit", "headers":{"User-Agent": this.config.useragent}}, function(error, response, body) {
|
||||||
|
var data = JSON.parse(body);
|
||||||
|
if (data.message){
|
||||||
|
event.reply(data.message);
|
||||||
|
} else {
|
||||||
|
event.reply(data.rate.remaining + " requests of " + data.rate.limit + " remaining.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~issue': function(event) {
|
||||||
|
var repo, issue, randflag;
|
||||||
|
if (isNaN(event.params[1]) && event.params[1]){ // if ~issue foo/bar
|
||||||
|
repo = event.params[1];
|
||||||
|
issue = event.params[2];
|
||||||
|
} else {
|
||||||
|
repo = this.config.defaultrepo;
|
||||||
|
issue = event.params[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issue == "*" || issue == "random" || issue == "0") {
|
||||||
|
issue = "";
|
||||||
|
randflag = true;
|
||||||
|
} else if (!issue) { // issue is undefined
|
||||||
|
issue = "";
|
||||||
|
} else {
|
||||||
|
issue = "/" + issue; // got to be a better way
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqUrl = "https://api.github.com/repos/" + repo + "/issues" +
|
||||||
|
issue + "?sort=" + this.config.sortorder;
|
||||||
|
request.get({"url": reqUrl, headers: { "User-Agent": this.config.useragent}}, function(error,response, body) {
|
||||||
|
if (response.statusCode == "200") {
|
||||||
|
var data = JSON.parse(body);
|
||||||
|
if (!issue){
|
||||||
|
if (randflag) {
|
||||||
|
data = data[Math.floor(Math.random() * data.length)];
|
||||||
|
} else {
|
||||||
|
data = data[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_.has(data["pull_request"], "html_url")){
|
||||||
|
data["pull_request"] = " with code";
|
||||||
|
} else {
|
||||||
|
data["pull_request"] = "";
|
||||||
|
}
|
||||||
|
if (data["state"]=="open") {
|
||||||
|
data["state"] = "\u000303" + data["state"];
|
||||||
|
} else {
|
||||||
|
data["state"] = "\u000304" + data["state"];
|
||||||
|
}
|
||||||
|
var labels = "";
|
||||||
|
for (var i=0; i < data["labels"].length; i++) { // for-in doesn't like me
|
||||||
|
var color = "\u0003" + (parseInt(data["labels"][i]["color"],16) % 15);
|
||||||
|
labels += " " + color + data["labels"][i]["name"];
|
||||||
|
}
|
||||||
|
data["label"] = labels;
|
||||||
|
event.reply(dbot.t("issue",data));
|
||||||
|
event.reply(data["html_url"]);
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("issuenotfound"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'~commits': function(event) {
|
||||||
|
exec("git rev-list --all | wc -l", function(error, stdout, stderr) {
|
||||||
|
stdout = stdout.trim();
|
||||||
|
request({"url":"http://numbersapi.com/" + stdout + "?fragment&default=XXX"}, function(error, response, body){
|
||||||
|
if (body != "XXX"){
|
||||||
|
event.reply(dbot.t("commitcountfun",{"fact": body, "count": stdout}));
|
||||||
|
} else {
|
||||||
|
// nothing fun about the number, let's try the year
|
||||||
|
request({"url":"http://numbersapi.com/" + stdout + "/year?fragment&default=XXX"}, function(error, response, body){
|
||||||
|
if (body != "XXX"){
|
||||||
|
event.reply(dbot.t("commitcountyear",{"fact": body, "count": stdout}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t("commitcountboring",{"count": stdout}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands = commands;
|
||||||
|
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new github(dbot);
|
||||||
|
};
|
94
modules-stock/github/strings.json
Normal file
94
modules-stock/github/strings.json
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"repocount": {
|
||||||
|
"en": "{user} has {count} public repos.",
|
||||||
|
"cy": "Mae {count} archifdai cyhoeddus gan {user}.",
|
||||||
|
"de": "{user} hat {count} öffnetliche Repos.",
|
||||||
|
"fr": "{user} a {count} dépôt(s) public(s).",
|
||||||
|
"it": "{user} ha {count} deposito/i pubblico/i."
|
||||||
|
},
|
||||||
|
"statusgood": {
|
||||||
|
"en": "\u000309Shit's fine",
|
||||||
|
"cy": "\u000309Cachu'n ddirwy",
|
||||||
|
"de": "\u000309Alles in Ordnung",
|
||||||
|
"fr": "\u000309Cette merde tourne bien",
|
||||||
|
"it": "\u000309Funziona a meraviglia."
|
||||||
|
},
|
||||||
|
"statusminor": {
|
||||||
|
"en": "\u000308Shit's touchy",
|
||||||
|
"cy": "\u000308Cachu'n fregus",
|
||||||
|
"de": "\u000308Kleinere Probleme vorhanden",
|
||||||
|
"fr": "\u000308Cette merde a un petit problème",
|
||||||
|
"it": "\u000308Piccoli problemi all' orizzonte"
|
||||||
|
},
|
||||||
|
"statusmajor": {
|
||||||
|
"en": "\u000304Shit's fucked:",
|
||||||
|
"cy": "\u000304Cachu wedi cyrraedd y ffan:",
|
||||||
|
"de": "\u000304Du bist am Arsch",
|
||||||
|
"fr": "\u000304Cette merde est foutue : ",
|
||||||
|
"it": "\u000304Sei nella merda : "
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"en": "You can find that shit at:",
|
||||||
|
"cy": "Gallwch ddod o hyd y cachu yn:",
|
||||||
|
"de": "Kann gefunden werden unter:",
|
||||||
|
"fr": "Tu peux trouver cette merde ici : ",
|
||||||
|
"it": "Puoi trovare questa coglionata a: "
|
||||||
|
},
|
||||||
|
"forkedrepo": {
|
||||||
|
"en": "{name} is a forked {language} repo with {open_issues} unresolved issues [{forks}F {watchers}W]",
|
||||||
|
"cy": "{name} ydy archif {language} fforchog gyda {open_issues} materion heb eu datrys [{forks}F {watchers}W]",
|
||||||
|
"de": "{name} ist eine geteilte {language} Repo mit {open_issues} ungelösten Problemen [{forks}F {watchers}W]",
|
||||||
|
"fr": "{name} est un dépôt fourché {language} avec {open_issues} problème(s) non résolu(s) [{forks}F {watchers}W]",
|
||||||
|
"it": "{name} è un deposito biforcato {language} con {open_issues} problema/i irrisolto/i [{forks}F {watchers}W]"
|
||||||
|
},
|
||||||
|
"unforkedrepo": {
|
||||||
|
"en": "{name} is a {language} repo with {open_issues} unresolved issues [{forks}F {watchers}W]",
|
||||||
|
"cy": "{name} ydy archif {language} gyda {open_issues} materion heb eu datrys [{forks}F {watchers}W]",
|
||||||
|
"de": "{name} ist eine {language} Repo mit {open_issues} ungelösten Problemen [{forks}F {watchers}W]",
|
||||||
|
"fr": "{name} est un dépôt {language} avec {open_issues} problème(s) non résolu(s) [{forks}F {watchers}W]",
|
||||||
|
"it": "{name} è un deposito {language} con {open_issues} problema/i irrisolto/i [{forks}F {watchers}W]"
|
||||||
|
},
|
||||||
|
"usernotfound": {
|
||||||
|
"en": "User not found.",
|
||||||
|
"cy": "Defnyddiwr heb ei ganfod.",
|
||||||
|
"de": "Benutzer nicht gefunden.",
|
||||||
|
"fr": "Utilisateur non trouvé.",
|
||||||
|
"it": "Utente non trovato."
|
||||||
|
},
|
||||||
|
"issuenotfound": {
|
||||||
|
"en": "Unable to find that issue.",
|
||||||
|
"cy": "Wedi methu dod o hyd mater hwnnw",
|
||||||
|
"de": "Kann dieses Problem nicht finden.",
|
||||||
|
"fr": "Impossible de trouver ce problème.",
|
||||||
|
"it": "Impossibile trovare questo problema."
|
||||||
|
},
|
||||||
|
"issue": {
|
||||||
|
"en": "Issue \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} comments]{label}",
|
||||||
|
"cy": "Mater \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} sylwadau]{label}",
|
||||||
|
"de": "Problem \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} comments]{label}",
|
||||||
|
"fr": "Problème \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} commentaires]{label}",
|
||||||
|
"it": "Problema \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} commento/i]{label}"
|
||||||
|
},
|
||||||
|
"commitcountboring": {
|
||||||
|
"en": "My code has been committed {count} times.",
|
||||||
|
"cy": "Mae fy cod wedi cael ei gyflawni ar {count} adegau.",
|
||||||
|
"de": "Mein Code wurde {count} mal bestätigt.",
|
||||||
|
"fr": "Mon code a été modifié {count} fois.",
|
||||||
|
"it": "Il mio codice è stato modificato {count} volta/e."
|
||||||
|
},
|
||||||
|
"commitcountfun": {
|
||||||
|
"en": "My repository has the same number of commits as {fact} ({count}).",
|
||||||
|
"cy": "Yr un nifer o ymrwymo fel {fact} gan fy archif ({count}).",
|
||||||
|
"de": "Meine Repository hat die gleiche Anzahl Commits wie {fact} ({count}).",
|
||||||
|
"fr": "Mon dépôt a le même nombre de modifications que {fact} ({count}).",
|
||||||
|
"it": "Il mio deposito ha lo stesso numero di modifiche come {fact} ({count})."
|
||||||
|
},
|
||||||
|
"commitcountyear": {
|
||||||
|
"en": "My repository's commits number {count}, the year that {fact}.",
|
||||||
|
"cy": "Nifer o ymrwymo gan fy archif: {count}, y flwyddyn y {fact}.",
|
||||||
|
"de": "Anzahl der Commits in meinem Repository {count}, des Jahres {fact}",
|
||||||
|
"fr": "Mon dépot compte {count} modifications, l'année où {fact}.",
|
||||||
|
"it": "Il mio deposito ha {count} modifica/che, l' anno che {fact}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
modules-stock/gmaps/gmaps.js
Normal file
29
modules-stock/gmaps/gmaps.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Google Maps
|
||||||
|
* Description: GMaps and ting
|
||||||
|
*/
|
||||||
|
var gm = require('googlemaps'),
|
||||||
|
_ = require('underscore')._;
|
||||||
|
|
||||||
|
var gmaps = function(dbot) {
|
||||||
|
this.commands = {
|
||||||
|
'~from': function(event) {
|
||||||
|
var from = event.input[1],
|
||||||
|
to = event.input[2],
|
||||||
|
departureNow = Math.floor((new Date()).getTime()/1000);
|
||||||
|
|
||||||
|
gm.directions(from, to, function(err, result) {
|
||||||
|
if(!err && result && result.status !== 'ZERO_RESULTS') {
|
||||||
|
event.reply('If you leave right now, it will take ' + result.routes[0].legs[0].duration.text + ' to get from ' + from + ' to ' + to + ' via public transport.');
|
||||||
|
} else {
|
||||||
|
event.reply('Apparently one cannot get from ' + from + ' to ' + to + ' using public transport. Do you accept the challenge?');
|
||||||
|
}
|
||||||
|
}, 'false', 'transit', null, null,null, null, null, departureNow);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands['~from'].regex = [/^from (.*) to (.*)/, 3];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new gmaps(dbot);
|
||||||
|
};
|
4
modules-stock/goodreads/config.json
Normal file
4
modules-stock/goodreads/config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"api_key": "sethere",
|
||||||
|
"outputPrefix": "\u00033goodreads\u000f"
|
||||||
|
}
|
293
modules-stock/goodreads/goodreads.js
Normal file
293
modules-stock/goodreads/goodreads.js
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: GoodReads
|
||||||
|
* Description: Interacts with the GoodReads API to provide book-oriented functionality to dbot
|
||||||
|
*/
|
||||||
|
|
||||||
|
var util = require('util'),
|
||||||
|
_ = require('underscore')._,
|
||||||
|
rp = require('request-promise-native'),
|
||||||
|
parseString = util.promisify(require('xml2js').parseString);
|
||||||
|
|
||||||
|
var GoodReads = function(dbot) {
|
||||||
|
this.apiRoot = 'https://www.goodreads.com';
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
'outputError': (evt, e) => {
|
||||||
|
switch(e) {
|
||||||
|
case 'goodreads-error': evt.reply('Error talking to GoodReads.'); return;
|
||||||
|
case 'book-not-found': evt.reply(dbot.t('gr_nobook')); return;
|
||||||
|
case 'no-description': evt.reply('No description was found for the book you asked for.'); return;
|
||||||
|
case 'author-not-found': evt.reply(dbot.t('gr_noauthor')); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(e);
|
||||||
|
evt.reply('Something went wrong and I don\'t know what.');
|
||||||
|
},
|
||||||
|
|
||||||
|
'formatProfile': profile => {
|
||||||
|
var shelves = {};
|
||||||
|
_.each(profile.user_shelves.user_shelf, shelf => {
|
||||||
|
shelves[shelf.name] = shelf.book_count['_'];
|
||||||
|
});
|
||||||
|
profile.user_shelves = shelves;
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'findBook': async term => {
|
||||||
|
//https://www.goodreads.com/search/index.xml
|
||||||
|
var body = await rp({
|
||||||
|
uri: this.apiRoot + '/search/index.xml',
|
||||||
|
qs: {
|
||||||
|
key: this.config.api_key,
|
||||||
|
q: term.split(' ').join('+')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await parseString(body, { explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
var result = response.GoodreadsResponse.search.results;
|
||||||
|
if(!result || !_.has(result, 'work')) throw 'book-not-found';
|
||||||
|
if(!result.work[0]) throw 'book-not-found';
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: result.work[0].best_book.id['_'],
|
||||||
|
title: result.work[0].best_book.title,
|
||||||
|
author: result.work[0].best_book.author.name,
|
||||||
|
rating: result.work[0].average_rating
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
'getSummaryForBook': async id => {
|
||||||
|
//https://www.goodreads.com/book/show.xml
|
||||||
|
var body = await rp({
|
||||||
|
uri: this.apiRoot + '/book/show.xml',
|
||||||
|
qs: {
|
||||||
|
key: this.config.api_key,
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await parseString(body, { explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
var result = response.GoodreadsResponse.book;
|
||||||
|
if(!result) throw 'book-not-found';
|
||||||
|
if(!_.has(result, 'description')) throw 'no-description';
|
||||||
|
|
||||||
|
return result.description;
|
||||||
|
},
|
||||||
|
|
||||||
|
'findAuthor': async term => {
|
||||||
|
//https://www.goodreads.com/api/author_url/<ID>
|
||||||
|
var body = await rp({
|
||||||
|
url: this.apiRoot + '/api/author_url/' + term,
|
||||||
|
qs: {
|
||||||
|
key: this.config.api_key
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await parseString(body, {explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
var result = response.GoodreadsResponse.author;
|
||||||
|
if(!result) throw 'author-not-found';
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: result['$'].id,
|
||||||
|
author: result.name
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
'getProfileById': async id => {
|
||||||
|
//https://www.goodreads.com/user/show.xml
|
||||||
|
try {
|
||||||
|
var body = await rp({
|
||||||
|
url: this.apiRoot + '/user/show.xml',
|
||||||
|
qs: {
|
||||||
|
key: this.config.api_key,
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if(e.statusCode && e.statusCode == 404) {
|
||||||
|
throw 'user-not-found';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await parseString(body, { explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
var result = response.GoodreadsResponse.user;
|
||||||
|
if(!result) throw 'user-not-found';
|
||||||
|
|
||||||
|
return this.internalAPI.formatProfile(result);
|
||||||
|
},
|
||||||
|
|
||||||
|
'getProfileByName': async username => {
|
||||||
|
//https://www.goodreads.com/user/show.xml
|
||||||
|
try {
|
||||||
|
var body = await rp({
|
||||||
|
url: this.apiRoot + '/user/show.xml',
|
||||||
|
qs: {
|
||||||
|
key: this.config.api_key,
|
||||||
|
username: username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if(e.statusCode && e.statusCode == 404) {
|
||||||
|
throw 'user-not-found';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await parseString(body, { explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
var result = response.GoodreadsResponse.user;
|
||||||
|
if(!result) throw 'user-not-found';
|
||||||
|
|
||||||
|
return this.internalAPI.formatProfile(result);
|
||||||
|
},
|
||||||
|
|
||||||
|
'getShelfForUserId': async (id, shelf) => {
|
||||||
|
//https://www.goodreads.com/review/list.xml?v=2
|
||||||
|
var body = await rp({
|
||||||
|
url: this.apiRoot + '/review/list.xml',
|
||||||
|
qs: {
|
||||||
|
v: '2',
|
||||||
|
key: this.config.api_key,
|
||||||
|
id: id,
|
||||||
|
shelf: shelf
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await parseString(body, { explicitArray: false });
|
||||||
|
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
|
||||||
|
|
||||||
|
let result = response.GoodreadsResponse.reviews.review;
|
||||||
|
if(!result) return [];
|
||||||
|
|
||||||
|
if(!_.isArray(result)) {
|
||||||
|
result = [result];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.map(result, r => {
|
||||||
|
return {
|
||||||
|
id: r.book.id['_'],
|
||||||
|
title: r.book.title_without_series
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~book' : async evt => {
|
||||||
|
try {
|
||||||
|
var book = await this.api.findBook(evt.input[1]);
|
||||||
|
evt.reply(dbot.t('gr_book', {
|
||||||
|
author: book.author,
|
||||||
|
title: book.title,
|
||||||
|
rating: book.rating,
|
||||||
|
link: this.apiRoot + '/book/show/' + book.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch(e) { this.internalAPI.outputError(evt, e); }
|
||||||
|
},
|
||||||
|
|
||||||
|
'~booksummary': async evt => {
|
||||||
|
try {
|
||||||
|
var book = await this.api.findBook(evt.input[1]);
|
||||||
|
var summary = await this.api.getSummaryForBook(book.id);
|
||||||
|
evt.reply(dbot.t('gr_summary', {
|
||||||
|
title: book.title,
|
||||||
|
summary: summary,
|
||||||
|
link: this.apiRoot + '/book/show/' + book.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch(e) { this.internalAPI.outputError(evt, e); }
|
||||||
|
},
|
||||||
|
|
||||||
|
'~author' : async evt => {
|
||||||
|
try {
|
||||||
|
evt.reply(dbot.t('gr_author', await this.api.findAuthor(evt.input[1])));
|
||||||
|
}
|
||||||
|
catch(e) { this.internalAPI.outputError(evt, e); }
|
||||||
|
},
|
||||||
|
|
||||||
|
'~reading': async (evt, profile) => {
|
||||||
|
try {
|
||||||
|
let books = await this.api.getShelfForUserId(profile.id, 'currently-reading');
|
||||||
|
var booksCount = books.length;
|
||||||
|
if(!booksCount) {
|
||||||
|
evt.reply(dbot.t('gr_not_reading', { user: evt.rUser.currentNick }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tooMany = booksCount > 5;
|
||||||
|
if (tooMany) books = _.sample(books, 5);
|
||||||
|
|
||||||
|
evt.reply(dbot.t('gr_is_reading', { user: evt.rUser.currentNick, count: booksCount }));
|
||||||
|
_.each(books, b => {
|
||||||
|
evt.reply(ostr = b.title + ' - https://www.goodreads.com/book/show/' + b.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tooMany) {
|
||||||
|
evt.reply('... And ' + (booksCount - 5) + ' more - https://www.goodreads.com/review/list/' + profile.id + '?shelf=currently-reading');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) { this.internalAPI.outputError(evt, e); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands['~book'].regex = [/^book (.*)/, 2];
|
||||||
|
this.commands['~booksummary'].regex = [/^booksummary (.*)/, 2];
|
||||||
|
this.commands['~author'].regex = [/^author ([\d\w\s-]*)/, 2];
|
||||||
|
|
||||||
|
this.commands['~reading'].requiresProfile = true;
|
||||||
|
|
||||||
|
_.each(this.commands, ((cmd, cmdName) => {
|
||||||
|
if(cmd.requiresProfile) {
|
||||||
|
this.commands[cmdName] = (async evt => {
|
||||||
|
var grUsername = evt.rProfile.goodreads;
|
||||||
|
|
||||||
|
if(!grUsername) {
|
||||||
|
evt.reply(evt.rUser.currentNick + ': Set a Goodreads username with "~set goodreads username"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let grId = evt.rProfile.goodreads_id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var profile;
|
||||||
|
if(grId) {
|
||||||
|
profile = await this.api.getProfileById(grId);
|
||||||
|
} else {
|
||||||
|
profile = await this.api.getProfileByName(grUsername);
|
||||||
|
grId = profile.id;
|
||||||
|
dbot.api.profile.setProperty(evt.server, evt.user, 'goodreads_id', grId, function(){});
|
||||||
|
}
|
||||||
|
|
||||||
|
await cmd(evt, profile);
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
if(e === 'user-not-found') evt.reply('User not found. Is your GoodReads username set correctly?');
|
||||||
|
else this.internalAPI.outputError(evt, e);
|
||||||
|
}
|
||||||
|
}).bind(this);
|
||||||
|
}
|
||||||
|
}).bind(this))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.fetch = dbot => new GoodReads(dbot);
|
23
modules-stock/goodreads/strings.json
Normal file
23
modules-stock/goodreads/strings.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"gr_book": {
|
||||||
|
"en": "[{title} by {author} - {rating}] - {link}"
|
||||||
|
},
|
||||||
|
"gr_summary": {
|
||||||
|
"en": "[{title}] - {summary} - {link}"
|
||||||
|
},
|
||||||
|
"gr_author": {
|
||||||
|
"en": "[{author}] - https://www.goodreads.com/author/show/{id}"
|
||||||
|
},
|
||||||
|
"gr_nobook": {
|
||||||
|
"en": "No book by that name was found."
|
||||||
|
},
|
||||||
|
"gr_noauthor": {
|
||||||
|
"en": "No author by that name was found."
|
||||||
|
},
|
||||||
|
"gr_not_reading": {
|
||||||
|
"en": "{user} is not currently reading any books."
|
||||||
|
},
|
||||||
|
"gr_is_reading": {
|
||||||
|
"en": "{user} is currently reading the following {count} books:"
|
||||||
|
}
|
||||||
|
}
|
6
modules-stock/goodreads/usage.json
Normal file
6
modules-stock/goodreads/usage.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"~book": "~book [bookname] - returns the title, author, rating, and GoodReads link",
|
||||||
|
"~booksummary": "~booksummary [bookname] - returns the summary for the requested book",
|
||||||
|
"~author": "~author [authorname] - returns the GoodReads link for the requested author",
|
||||||
|
"~reading": "~reading - displays up to 5 of the books you are currently reading"
|
||||||
|
}
|
26
modules-stock/ignore/README.md
Normal file
26
modules-stock/ignore/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
## Ignore
|
||||||
|
|
||||||
|
Ignore modules.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
Commands with which users can choose to ignore listeners and commands from
|
||||||
|
certain modules persistently, by storing their choices in the database. This is
|
||||||
|
an interface for the JSBot ignoreTag functionality which actually implements
|
||||||
|
the ignoration.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
All modules may return with them an 'ignorable' property, which defines whether
|
||||||
|
or not their functionality may be ignored by users.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~ignore [module]
|
||||||
|
Ignore a given module. If the user does not specify a module, or provides an
|
||||||
|
invalid one a list of modules which are available to ignore will be given.
|
||||||
|
|
||||||
|
#### ~unignore [module]
|
||||||
|
Unignore a previously ignored module. If the user does not specify a module, or
|
||||||
|
provides an invalid choice a list of modules which are currently ignored will be
|
||||||
|
given.
|
49
modules-stock/ignore/api.js
Normal file
49
modules-stock/ignore/api.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var api = function(dbot) {
|
||||||
|
return {
|
||||||
|
// Is user ignoring command/module?
|
||||||
|
'isUserIgnoring': function(user, item, callback) {
|
||||||
|
this.internalAPI.isUserImpeded(user, item, 'ignores', callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Is user banned from command/module?
|
||||||
|
'isUserBanned': function(user, item, callback) {
|
||||||
|
this.internalAPI.isUserImpeded(user, item, 'bans', callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Is channel ignoring module?
|
||||||
|
// TODO: Command support
|
||||||
|
'isChannelIgnoring': function(channelName, item, callback) {
|
||||||
|
var isIgnoring = false,
|
||||||
|
channel = false;
|
||||||
|
|
||||||
|
this.db.search('channel_ignores', {
|
||||||
|
'server': server,
|
||||||
|
'name': channel
|
||||||
|
}, function(result) {
|
||||||
|
channel = result;
|
||||||
|
}, function(err) {
|
||||||
|
if(!err && channel && _.include(channel.ignores, item)) {
|
||||||
|
isIgnoring = true;
|
||||||
|
}
|
||||||
|
callback(isIgnoring);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Resolve a nick and return their user and ignores object
|
||||||
|
'getUserIgnores': function(user, callback) {
|
||||||
|
this.db.read('ignores', user.id, function(err, ignores) {
|
||||||
|
if(!err && ignores) {
|
||||||
|
callback(false, ignores);
|
||||||
|
} else {
|
||||||
|
callback(true, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return api(dbot);
|
||||||
|
};
|
6
modules-stock/ignore/config.json
Normal file
6
modules-stock/ignore/config.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ignorable": false,
|
||||||
|
"dependencies": [ "users" ],
|
||||||
|
"dbKeys": [ "ignores", "bans" ],
|
||||||
|
"dbType": "redis"
|
||||||
|
}
|
304
modules-stock/ignore/ignore.js
Normal file
304
modules-stock/ignore/ignore.js
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Ignore
|
||||||
|
* Description: Handles commands in which users can choose to ignore listeners
|
||||||
|
* and commands from certain modules. It also populates the JSBot instance with
|
||||||
|
* this information, since that actually performs the ignorance. Also provides
|
||||||
|
* commands for moderators to choose the bot to ignore certain channels.
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
databank = require('databank'),
|
||||||
|
uuid = require('node-uuid'),
|
||||||
|
NoSuchThingError = databank.NoSuchThingError;
|
||||||
|
|
||||||
|
var ignore = function(dbot) {
|
||||||
|
this.internalAPI = {
|
||||||
|
'isUserImpeded': function(user, item, by, callback) {
|
||||||
|
this.api.getUserIgnores(user, function(err, ignores) {
|
||||||
|
var isImpeded = false;
|
||||||
|
if(!err && ignores) {
|
||||||
|
if(_.has(dbot.commands, item) && !_.include(ignores[by], item)) {
|
||||||
|
item = dbot.commands[item].module;
|
||||||
|
}
|
||||||
|
if(_.include(ignores[by], item) || _.include(ignores[by], '*')) {
|
||||||
|
isImpeded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(isImpeded);
|
||||||
|
});
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
var commands = {
|
||||||
|
'~ignore': function(event) {
|
||||||
|
var module = event.params[1];
|
||||||
|
var ignorableModules = _.chain(dbot.modules)
|
||||||
|
.filter(function(module, name) {
|
||||||
|
return dbot.config.modules[module].ignorable === true;
|
||||||
|
})
|
||||||
|
.pluck('name')
|
||||||
|
.value();
|
||||||
|
|
||||||
|
if(_.isUndefined(module)) {
|
||||||
|
event.reply(dbot.t('ignore_usage', {
|
||||||
|
'user': event.user,
|
||||||
|
'modules': ignorableModules.join(', ')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(module == '*' || _.include(ignorableModules, module)) {
|
||||||
|
this.api.getUserIgnores(event.rUser, function(err, ignores) {
|
||||||
|
if(!ignores) {
|
||||||
|
ignores = {
|
||||||
|
'id': event.rUser.id,
|
||||||
|
'ignores': [],
|
||||||
|
'bans': []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_.include(ignores.ignores, module)) {
|
||||||
|
ignores.ignores.push(module);
|
||||||
|
this.db.save('ignores', event.rUser.id, ignores, function(err) {
|
||||||
|
if(!err) {
|
||||||
|
dbot.instance.ignoreTag(event.user, module);
|
||||||
|
event.reply(dbot.t('ignored', {
|
||||||
|
'user': event.user,
|
||||||
|
'module': module
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('already_ignoring', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('invalid_ignore', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~unignore': function(event) {
|
||||||
|
var module = event.params[1];
|
||||||
|
|
||||||
|
this.api.getUserIgnores(event.rUser, function(err, ignores) {
|
||||||
|
if(err || !ignores || _.isUndefined(module)) {
|
||||||
|
if(ignores) {
|
||||||
|
event.reply(dbot.t('unignore_usage', {
|
||||||
|
'user': event.user,
|
||||||
|
'modules': ignores.ignores.join(', ')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('empty_unignore_usage', {
|
||||||
|
'user': event.user
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(_.include(ignores.ignores, module)) {
|
||||||
|
ignores.ignores = _.without(ignores.ignores, module);
|
||||||
|
this.db.save('ignores', event.rUser.id, ignores, function(err) {
|
||||||
|
if(!err) {
|
||||||
|
dbot.instance.removeIgnore(event.user, module)
|
||||||
|
event.reply(dbot.t('unignored', {
|
||||||
|
'user': event.user,
|
||||||
|
'module': module
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('invalid_unignore', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~ban': function(event) {
|
||||||
|
var nick = event.input[1],
|
||||||
|
item = event.input[2];
|
||||||
|
|
||||||
|
if(item == '*' || _.include(dbot.config.moduleNames, item) || _.include(dbot.commands, item)) {
|
||||||
|
dbot.api.users.resolveUser(event.server, nick, function(err, user) {
|
||||||
|
this.api.getUserIgnores(user, function(err, ignores) {
|
||||||
|
if(!ignores) {
|
||||||
|
ignores = {
|
||||||
|
'id': user.id,
|
||||||
|
'ignores': [],
|
||||||
|
'bans': []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_.include(ignores.bans, item)) {
|
||||||
|
ignores.bans.push(item);
|
||||||
|
this.db.save('ignores', user.id, ignores, function(err) {
|
||||||
|
if(!err) {
|
||||||
|
event.reply(dbot.t('banned_success', {
|
||||||
|
'user': event.user,
|
||||||
|
'banned': nick,
|
||||||
|
'module': item
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('already_banned', {
|
||||||
|
'user': event.user,
|
||||||
|
'banned': nick
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('invalid_ban', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~unban': function(event) {
|
||||||
|
var nick = event.input[1],
|
||||||
|
item = event.input[2];
|
||||||
|
|
||||||
|
dbot.api.users.resolveUser(event.server, nick, function(err, user) {
|
||||||
|
this.api.getUserIgnores(user, function(err, ignores) {
|
||||||
|
if(err || !ignores) {
|
||||||
|
event.reply(dbot.t('invalid_unban', {
|
||||||
|
'user': event.user,
|
||||||
|
'banned': nick
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(_.include(ignores.bans, item)) {
|
||||||
|
ignores.bans = _.without(ignores.bans, item);
|
||||||
|
this.db.save('ignores', user.id, ignores, function(err) {
|
||||||
|
event.reply(dbot.t('unbanned_success', {
|
||||||
|
'user': event.user,
|
||||||
|
'banned': nick,
|
||||||
|
'module': item
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('invalid_unban', {
|
||||||
|
'user': event.user,
|
||||||
|
'banned': nick
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~ignorechannel': function(event) {
|
||||||
|
var channelName = event.input[1],
|
||||||
|
module = event.input[2];
|
||||||
|
|
||||||
|
// Ignoring the value of 'ignorable' at the moment
|
||||||
|
if(module == '*' || _.include(dbot.config.moduleNames, module)) {
|
||||||
|
var channel = false;
|
||||||
|
|
||||||
|
this.db.search('channel_ignores', {
|
||||||
|
'server': event.server,
|
||||||
|
'name': channelName
|
||||||
|
}, function(result) {
|
||||||
|
channel = result;
|
||||||
|
}, function(err) {
|
||||||
|
if(!channel) {
|
||||||
|
var id = uuid.v4();
|
||||||
|
channel = {
|
||||||
|
'id': id,
|
||||||
|
'server': event.server,
|
||||||
|
'name': channelName,
|
||||||
|
'ignores': []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_.include(channel.ignores, module)) {
|
||||||
|
channel.ignores.push(module);
|
||||||
|
this.db.save('channel_ignores', channel.id, channel, function(err) {
|
||||||
|
dbot.instance.ignoreTag(channel.name, module);
|
||||||
|
event.reply(dbot.t('ignoring_channel', {
|
||||||
|
'module': module,
|
||||||
|
'channel': channelName
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('already_ignoring_channel', {
|
||||||
|
'module': module,
|
||||||
|
'channel': channelName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('module_not_exist', { 'module': module }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~unignorechannel': function(event) {
|
||||||
|
var channelName = event.input[1],
|
||||||
|
module = event.input[2],
|
||||||
|
channel = false;
|
||||||
|
|
||||||
|
this.db.search('channel_ignores', {
|
||||||
|
'server': event.server,
|
||||||
|
'name': channelName
|
||||||
|
}, function(result) {
|
||||||
|
channel = result;
|
||||||
|
}, function(err) {
|
||||||
|
if(channel && _.include(channel.ignores, module)) {
|
||||||
|
channel.ignores = _.without(channel.ignores, module);
|
||||||
|
this.db.save('channel_ignores', channel.id, channel, function(err) {
|
||||||
|
dbot.instance.removeIgnore(channel.name, module);
|
||||||
|
event.reply(dbot.t('unignoring_channel', {
|
||||||
|
'module': module,
|
||||||
|
'channel': channelName
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('not_ignoring_channel', {
|
||||||
|
'module': module,
|
||||||
|
'channel': channelName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
commands['~ban'].regex = [/^ban ([^ ]+) ([^ ]+)$/, 3];
|
||||||
|
commands['~unban'].regex = [/^unban ([^ ]+) ([^ ]+)$/, 3];
|
||||||
|
commands['~ignorechannel'].regex = [/^ignorechannel ([^ ]+) ([^ ]+)$/, 3];
|
||||||
|
commands['~unignorechannel'].regex = [/^unignorechannel ([^ ]+) ([^ ]+)$/, 3];
|
||||||
|
|
||||||
|
commands['~ban'].access = 'power_user';
|
||||||
|
commands['~unban'].access = 'power_user';
|
||||||
|
commands['~ignorechannel'].access = 'moderator';
|
||||||
|
commands['~unignorechannel'].access = 'moderator';
|
||||||
|
|
||||||
|
this.commands = commands;
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
dbot.instance.clearIgnores();
|
||||||
|
|
||||||
|
this.db.scan('ignores', function(ignores) {
|
||||||
|
dbot.api.users.getUser(ignores.id, function(err, user) {
|
||||||
|
if(user) {
|
||||||
|
_.each(ignores.ignores, function(module) {
|
||||||
|
dbot.instance.ignoreTag(user.currentNick, module);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, function(err) { });
|
||||||
|
|
||||||
|
this.db.scan('channel_ignores', function(channel) {
|
||||||
|
_.each(channel.ignores, function(module) {
|
||||||
|
dbot.instance.ignoreTag(channel.name, module);
|
||||||
|
});
|
||||||
|
}, function(err) { });
|
||||||
|
|
||||||
|
dbot.api.event.addHook('new_current_nick', function(user, oldNick) {
|
||||||
|
this.api.getUserIgnores(user, function(err, ignores) {
|
||||||
|
if(ignores) {
|
||||||
|
_.each(ignores.ignores, function(module) {
|
||||||
|
dbot.instance.removeIgnore(oldNick, module);
|
||||||
|
dbot.instance.ignoreTag(user.currentNick, module);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new ignore(dbot);
|
||||||
|
};
|
183
modules-stock/ignore/strings.json
Normal file
183
modules-stock/ignore/strings.json
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
{
|
||||||
|
"ignore_usage": {
|
||||||
|
"en": "{user}: Usage: ~ignore [module]. Modules you can ignore are: {modules}.",
|
||||||
|
"es": "{user}: Modo de empleo: ~ignore [módulo]. Módulos que tú puedes ignorar son: {modules}.",
|
||||||
|
"na'vi": "{user}: Sar: ~ignore ['u]. U, nga ke tìng mikyun: {modules}.",
|
||||||
|
"cy": "{user}: Defnydd: ~ignore [modiwl]. Modiwlau a allech anwybyddu yw: {modules}.",
|
||||||
|
"nl": "{user}: Gebruik: ~ignore [module]. Modules die negeert kunnen worden zijn: {modules}.",
|
||||||
|
"de": "{user}: Benutzung: ~ignore [module]. Module, die ausgeschaltet werden können: {modules}.",
|
||||||
|
"fr": "{user}: Utilisation: ~ignore [module]. Les modules que vous pouvez ignorer sont: {modules}.",
|
||||||
|
"it": "{user}: Uso: ~ignore [module]. I moduli che puoi ignorare sono: {modules}."
|
||||||
|
},
|
||||||
|
"already_ignoring": {
|
||||||
|
"en": "{user}: You're already ignoring that module.",
|
||||||
|
"es": "{user}: Ya ignoras este módulo.",
|
||||||
|
"na'vi": "{user}: 'uri nga ke tìng mikyun srekrr.",
|
||||||
|
"cy": "{user}: Mi rwyt ti'n anwybyddu'r modiwl yna'n barod.",
|
||||||
|
"nl": "{user}: Je negeert deze module al.",
|
||||||
|
"de": "{user}: Dieses Modul ist bereits ausgeschaltet.",
|
||||||
|
"fr": "{user}: Vous ignorez déjà ce module.",
|
||||||
|
"it": "{user}: Stai già ignorando questo modulo"
|
||||||
|
},
|
||||||
|
"ignored": {
|
||||||
|
"en": "{user}: Now ignoring {module}.",
|
||||||
|
"es": "{user}: Estás ignorando {module}.",
|
||||||
|
"na'vi": "{user}: Nga ke terìng mikyun {module}ne set.",
|
||||||
|
"cy": "{user}: Nawr yn anwybyddu {module}",
|
||||||
|
"nl": "{user}: {module} wordt nu genegeerd.",
|
||||||
|
"de": "{user}: {module} wird nun ausgeschaltet.",
|
||||||
|
"fr": "{user}: {module} désormais ignoré.",
|
||||||
|
"it": "{user}: {module} sarà adesso ignorato"
|
||||||
|
},
|
||||||
|
"invalid_ignore": {
|
||||||
|
"en": "{user}: That isn't a valid module name.",
|
||||||
|
"es": "{user}: Ese no es un nombre de un módulo valido.",
|
||||||
|
"na'vi": "{user}: Tsatstxo eyawr ke lu.",
|
||||||
|
"cy": "{user}: Nid oedd hwna'n modiwl dilys",
|
||||||
|
"nl": "{user}: Dat is geen geldige modulenaam.",
|
||||||
|
"de": "{user}: Dies ist kein Name eines Moduls.",
|
||||||
|
"fr": "{user}: Ceci ne correspond pas à un nom de module valide.",
|
||||||
|
"it": "{user}: Questo non è un nome di modulo valido"
|
||||||
|
},
|
||||||
|
"unignore_usage": {
|
||||||
|
"en": "{user}: Usage: ~unignore [module]. Modules you are currently ignoring: {modules}.",
|
||||||
|
"es": "{user}: Modo de empleo: ~unignore [módulo]. Módulos que ignoras ahora mismo: {modules}.",
|
||||||
|
"na'vi": "{user}: Sar: ~unignore ['u]. Uri, nga ke terìng mikyun: {modules}.",
|
||||||
|
"cy": "{user}: Defnydd: ~unignore [modiwl]. Modiwlau rydech yn anwybyddu ar hyn o bryd: {modules}",
|
||||||
|
"nl": "{user}: Gebruik: ~unignore [module]. Modules die momenteel worden genegeerd: {modules}.",
|
||||||
|
"de": "{user}: Benutzung: ~unignore [module]. Module, die im Moment ausgeschaltet sind: {modules}.",
|
||||||
|
"fr": "{user}: Utilisation: ~unignore [module]. Modules que vous ignorez actuellement: {modules}.",
|
||||||
|
"it": "{user}: Uso: ~unignore [module]. Module che ignori attualmente: {modules}."
|
||||||
|
},
|
||||||
|
"empty_unignore_usage": {
|
||||||
|
"en": "{user}: Usage: ~unignore [module].",
|
||||||
|
"es": "{user}: Modo de empleo: ~unignore [módulo].",
|
||||||
|
"na'vi": "{user}: Sar: ~unignore ['u].",
|
||||||
|
"cy": "{user}: Defnydd: ~unignore [modiwl].",
|
||||||
|
"nl": "{user}: Gebruik: ~unignore [module].",
|
||||||
|
"de": "{user}: Benutzung: ~unignore [module].",
|
||||||
|
"fr": "{user}: Utilisation: ~unignore [module].",
|
||||||
|
"it": "{user}: Uso: ~unignore [module]."
|
||||||
|
},
|
||||||
|
"invalid_unignore": {
|
||||||
|
"en": "{user}: You're not ignoring that module or it doesn't exist.",
|
||||||
|
"es": "{user}: No ignoras este módulo o no existe.",
|
||||||
|
"na'vi":"{user}: Nga terìng mikyun fu fì'ul fìtsengit ke tok.",
|
||||||
|
"cy": "{user}: Nid wyt ti'n anwybyddu'r modiwl yna neu nid yw e'n bodoli",
|
||||||
|
"nl": "{user}: Deze module bestaat niet of wordt niet genegeerd.",
|
||||||
|
"de": "{user}: Dieses Modul ist entweder ausgeschaltet oder existiert nicht.",
|
||||||
|
"fr": "{user}: Vous n'ignorez pas ce module ou il n'existe pas.",
|
||||||
|
"it": "{user}: Non stai ignorando questo modulo o non esiste."
|
||||||
|
},
|
||||||
|
"unignored": {
|
||||||
|
"en": "{user}: No longer ignoring {module}.",
|
||||||
|
"es": "{user}: Ya no ignoras {module}.",
|
||||||
|
"na'vi": "{user}: Nga terìng mikyun {module}ne set",
|
||||||
|
"cy": "{user}: Ddim yn anwybyddu {module} bellach",
|
||||||
|
"nl": "{user}: {module} wordt niet langer genegeerd.",
|
||||||
|
"de": "{user}: {module} ist nicht länger ausgeschaltet.",
|
||||||
|
"fr": "{user}: {module} n'est plus ignoré à présent.",
|
||||||
|
"it": "{user}: {module} non viene attualmente più ignorato"
|
||||||
|
},
|
||||||
|
"ban_usage": {
|
||||||
|
"en": "{user}: Usage: ~ban [user] [module/command]. Use * for all modules and commands.",
|
||||||
|
"cy": "{user}: Defnydd: ~ban [defnyddiwr] [modiwl/gorchymyn]. Defnyddio * am pob modiwlau a gorchmynion.",
|
||||||
|
"nl": "{user}: Gebruik: ~ban [gebruiker] [module/commando]. Gebruik * voor alle modules en alle commandos.",
|
||||||
|
"de": "{user}: Benutzung ~ban [Benutzer] [module/Befehl]. Benutze * für alle Module und Befehle.",
|
||||||
|
"fr": "{user}: Utilisation: ~ban [user] [module/command]. Utilisez * pour tous les modules et commandes.",
|
||||||
|
"it": "{user}: Uso: ~ban [user] [module/command]. Utilizza * per tutti i moduli e commandi."
|
||||||
|
},
|
||||||
|
"already_banned": {
|
||||||
|
"en": "{user}: {banned} is already banned from that module.",
|
||||||
|
"cy": "{user}: {banned} eisoes wedi ei wahardd o'r modiwl.",
|
||||||
|
"nl": "{user}: {banned} is al geband van deze module.",
|
||||||
|
"de": "{user}: {banned} ist bereits von diesem Modul gebannt.",
|
||||||
|
"fr": "{user}: {banned} est déjà interdit d'utiliser ce module.",
|
||||||
|
"it": "{user}: {banned} è già bandito da usare questo modulo."
|
||||||
|
},
|
||||||
|
"banned_success": {
|
||||||
|
"en": "{user}: {banned} is now banned from {module}.",
|
||||||
|
"cy": "{user}: {banned} ei wahardd yn awr am {module}.",
|
||||||
|
"nl": "{user}: {banned} mag {module} nu niet meer gebruiken.",
|
||||||
|
"de": "{user}: {banned} ist nun von {module} gebannt.",
|
||||||
|
"fr": "{user}: {banned} est maintenant interdit d'utiliser {module}.",
|
||||||
|
"it": "{user}: {banned} è stato adesso bandito da usare {module}."
|
||||||
|
},
|
||||||
|
"invalid_ban": {
|
||||||
|
"en": "{user}: That isn't a valid module name.",
|
||||||
|
"cy": "{user}: Nid oedd hwna'n modiwl dilys",
|
||||||
|
"nl": "{user}: Dat is geen geldige modulenaam.",
|
||||||
|
"de": "{user}: Dies ist kein Name eines Moduls.",
|
||||||
|
"fr": "{user}: Ceci n'est pas un nom de module valide.",
|
||||||
|
"it": "{user}: Queso non è il nome di un modulo valido."
|
||||||
|
},
|
||||||
|
"unban_usage": {
|
||||||
|
"en": "{user}: Usage: ~unban [user] [module].",
|
||||||
|
"cy": "{user}: Defnydd: ~unban [defnyddiwr] [modiwl].",
|
||||||
|
"nl": "{user}: Gebruik: ~unban [gebruiker] [module].",
|
||||||
|
"de": "{user}: Benutzung: ~unban [Benutzer] [module].",
|
||||||
|
"fr": "{user}: Utilisation: ~unban [user] [module].",
|
||||||
|
"it": "{user}: Uso: ~unban [user] [module]."
|
||||||
|
},
|
||||||
|
"invalid_unban": {
|
||||||
|
"en": "{user}: {banned} is not banned from that module or it doesn't exist.",
|
||||||
|
"cy": "{user}: Nid oedd {banned} wedi ei wahardd o'r modiwl, neu nid yw'n bodoli.",
|
||||||
|
"nl": "{user}: {banned} is niet geband van die module of de module bestaat niet.",
|
||||||
|
"de": "{user}: {banned} ist von diesem Modul nicht gebannt, oder es existiert nicht.",
|
||||||
|
"fr": "{user}: {banned} n'est pas interdit d'utiliser ce module, ou il n'existe pas.",
|
||||||
|
"it": "{user}: {banned} non è stato bandito da questo modulo o non esiste."
|
||||||
|
},
|
||||||
|
"unbanned_success": {
|
||||||
|
"en": "{user}: {banned} is no longer banned from {module}.",
|
||||||
|
"cy": "{user}: Nid yw {banned} yn cael ei wahardd mwyach.",
|
||||||
|
"nl": "{user}: {banned} mag {module} weer gebruiken.",
|
||||||
|
"de": "{user}: {banned} wurde von {module} entbannt.",
|
||||||
|
"fr": "{user}: {banned} n'est plus interdit d'utiliser {module}.",
|
||||||
|
"it": "{user}: {banned} non è più bandito dall' utilizzare {module}."
|
||||||
|
},
|
||||||
|
"ignoring_channel": {
|
||||||
|
"en": "Now ignoring {module} in {channel}.",
|
||||||
|
"na'vi": "Oe ke stayawm {module}ur mì {channel}",
|
||||||
|
"cy": "Bellach yn anwybyddu {module} yn {channel}.",
|
||||||
|
"nl": "{module} wordt nu genegeerd in {channel}.",
|
||||||
|
"de": "{module} in {channel} ist nun ausgeschaltet.",
|
||||||
|
"fr": "{module} dans {channel} maintenant ignoré.",
|
||||||
|
"it": "{module} in {channel} viene adesso ignorato."
|
||||||
|
},
|
||||||
|
"already_ignoring_channel": {
|
||||||
|
"en": "Already ignoring {module} in {channel}.",
|
||||||
|
"na'vi": "Oe ke stayawm {module}ur mì {channel} li",
|
||||||
|
"cy": "Eisoes anwybyddu {module} yn {channel}",
|
||||||
|
"nl": "{module} wordt al genegeerd in {channel}.",
|
||||||
|
"de": "{module} in {channel} ist bereits ausgeschaltet.",
|
||||||
|
"fr": "{module} dans {channel} déjà ignoré.",
|
||||||
|
"it": "{module} in {channel} già ignorato."
|
||||||
|
},
|
||||||
|
"module_not_exist": {
|
||||||
|
"en": "{module} isn't loaded or doesn't exist.",
|
||||||
|
"na'vi": "Oel ke omum teri {module}it fu {module} ke fkeytok",
|
||||||
|
"cy": "Dydy {module} ddim yn lwythodd, neu ddim yn bodoli.",
|
||||||
|
"nl": "{module} is niet geladen of bestaat niet.",
|
||||||
|
"de": "{module} ist nicht geladen oder existiert nicht.",
|
||||||
|
"fr": "{module} n'est pas chargé ou n'existe pas.",
|
||||||
|
"it": "{module} non caricato o non esiste"
|
||||||
|
},
|
||||||
|
"unignoring_channel": {
|
||||||
|
"en": "No longer ignoring {module} in {channel}.",
|
||||||
|
"na'vi": "Oel stayawm {module}ur mì {channel} set.",
|
||||||
|
"cy": "Nid anwybyddu {module} yn {channel} mwyach.",
|
||||||
|
"nl": "{module} wordt niet meer genegeerd in {channel}.",
|
||||||
|
"de": "{module} in {channel} ist nicht länger ausgeschaltet.",
|
||||||
|
"fr": "{module} dans {channel} n'est plus ignoré à présent.",
|
||||||
|
"it": "{module} in {channel} non viene più ignorato."
|
||||||
|
},
|
||||||
|
"not_ignoring_channel": {
|
||||||
|
"en": "{module} wasn't being ignored in {channel}.",
|
||||||
|
"na'vi": "Oel stayawm {module}ur mì {channel} li.",
|
||||||
|
"cy": "Nid yw {module} yn cael ei anwybyddu yn {channel}.",
|
||||||
|
"nl": "{module} werd niet genegeerd in {channel}.",
|
||||||
|
"de": "{module} war in {channel} nicht ausgeschaltet.",
|
||||||
|
"fr": "{module} n'était pas ignoré dans {channel}.",
|
||||||
|
"it": "{module} non veniva ignorato in {channel}"
|
||||||
|
}
|
||||||
|
}
|
65
modules-stock/imgur/README.md
Normal file
65
modules-stock/imgur/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
## 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.
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
#### imagelength: 5
|
||||||
|
Length of slugs generated by the random imgur functionality.
|
||||||
|
|
||||||
|
#### nsfwwarn: true
|
||||||
|
Warn that images generated by the ~ri command may be NSFW.
|
||||||
|
|
||||||
|
#### apikey
|
||||||
|
Key to use with the imgur API.
|
||||||
|
|
||||||
|
#### highscore: ricount
|
||||||
|
Quote category to use for a 'highscore;' used to run games with the web
|
||||||
|
/random page, by storing a highscore based on some arbitrary rule in the
|
||||||
|
chosen quote category (say, how far can you get before seeing a turtle). Then,
|
||||||
|
on the imgur random page you can press 'c' to see a countdown towards the last
|
||||||
|
stored value in the highscore quote category. If you beat the highscore, simply
|
||||||
|
add the winning score to the quote category.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### Web
|
||||||
|
|
||||||
|
#### /imgur/random
|
||||||
|
|
||||||
|
A web page which loads a random image from imgur. You can press the space bar to
|
||||||
|
load a new image, and information about the images are shown on the top-left of
|
||||||
|
the page. You can press 'c' to view a highscore count (as documented above).
|
||||||
|
|
||||||
|
#### /imgur/stats
|
||||||
|
|
||||||
|
Show statistics on the total use of the imgur module.
|
||||||
|
|
||||||
|
### Hooks
|
||||||
|
|
||||||
|
#### link
|
||||||
|
Posts information about an imgur link when one is linked in the channel.
|
14
modules-stock/imgur/config.json
Normal file
14
modules-stock/imgur/config.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"dbKeys": [ "imgur" ],
|
||||||
|
"dependencies": [ "web", "api", "link" ],
|
||||||
|
"imagelength": 5,
|
||||||
|
"ricachelength": 7,
|
||||||
|
"nsfwwarn": true,
|
||||||
|
"apikey": "86fd3a8da348b65",
|
||||||
|
"highscore": "ricount",
|
||||||
|
"autoadd": {
|
||||||
|
"e49e686582ce3f60cb51d00c10924861": "facebookman",
|
||||||
|
"b11b634c74562bcd4e5d17b0d90987be": "raffleguy"
|
||||||
|
},
|
||||||
|
"outputPrefix": "\u00038imgur\u000f"
|
||||||
|
}
|
330
modules-stock/imgur/imgur.js
Normal file
330
modules-stock/imgur/imgur.js
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: imgur
|
||||||
|
* Description: Various imgur functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
request = require('request'),
|
||||||
|
async = require('async'),
|
||||||
|
crypto = require('crypto'),
|
||||||
|
humanise = require('humanize');
|
||||||
|
|
||||||
|
var imgur = function(dbot) {
|
||||||
|
this.ApiRoot = 'https://api.imgur.com/3/';
|
||||||
|
this.ExcludeRes = [
|
||||||
|
{ 'w': 800, 'h': 600 },
|
||||||
|
{ 'w': 1024, 'h': 768 },
|
||||||
|
{ 'w': 1280, 'h': 768 },
|
||||||
|
{ 'w': 1280, 'h': 960 },
|
||||||
|
{ 'w': 1366, 'h': 768 },
|
||||||
|
{ 'w': 1600, 'h': 900 },
|
||||||
|
{ 'w': 1680, 'h': 1050 },
|
||||||
|
{ 'w': 1920, 'h': 1080 },
|
||||||
|
{ 'w': 1024, 'h': 640 }
|
||||||
|
];
|
||||||
|
this.riCache = [];
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
'infoString': function(imgData) {
|
||||||
|
info = '';
|
||||||
|
if(!_.isUndefined(imgData) && _.has(imgData, 'data') && !_.isUndefined(imgData.data.type)) {
|
||||||
|
imgData = imgData.data;
|
||||||
|
if(imgData.title) {
|
||||||
|
info += imgData.title + ' - ';
|
||||||
|
}
|
||||||
|
if(imgData.type) {
|
||||||
|
if(imgData.animated) {
|
||||||
|
info += 'an animated ' + imgData.type.split('/')[1] + ' with ';
|
||||||
|
} else {
|
||||||
|
info += 'a ' + imgData.type.split('/')[1] + ' with ';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info += 'an image with ';
|
||||||
|
}
|
||||||
|
info += imgData.views + ' views (';
|
||||||
|
info += imgData.width + 'x' + imgData.height + ')';
|
||||||
|
|
||||||
|
info += ' ('+humanise.filesize(imgData.size)+')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
'albumInfoString': function(albumData) {
|
||||||
|
var info = '';
|
||||||
|
if(!_.isUndefined(albumData) && _.has(albumData, 'data') && !_.isUndefined(albumData.data.id)) {
|
||||||
|
albumData = albumData.data;
|
||||||
|
if(albumData.title) {
|
||||||
|
info += albumData.title + ' - ';
|
||||||
|
}
|
||||||
|
if(albumData.description) {
|
||||||
|
info += albumData.description.split('\n')[0] + ' is ';
|
||||||
|
}
|
||||||
|
info += 'an album with ' + albumData.images_count + ' images ';
|
||||||
|
info += 'and ' + albumData.views + ' views';
|
||||||
|
if(albumData.nsfw) {
|
||||||
|
info += ' - NSFW';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
'galleryInfoString': function(galData) {
|
||||||
|
var info = '';
|
||||||
|
if(!_.isUndefined(galData) && _.has(galData, 'data') && !_.isUndefined(galData.data.is_album)) {
|
||||||
|
if(galData.data.is_album === true) {
|
||||||
|
info = this.internalAPI.albumInfoString(galData);
|
||||||
|
} else {
|
||||||
|
info = this.internalAPI.infoString(galData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'getRandomImage': function(callback) {
|
||||||
|
var random = function(len) {
|
||||||
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||||
|
return len ? chars.charAt(~~(Math.random()*chars.length)) + random(len-1) : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
var ext = [ 'gif', 'png', 'jpg' ];
|
||||||
|
var testSlug = random(5);
|
||||||
|
var testUrl = 'http://i.imgur.com/' +
|
||||||
|
testSlug +
|
||||||
|
'.' + ext[_.random(0, ext.length - 1)],
|
||||||
|
fbman = false;
|
||||||
|
dbot.db.imgur.totalHttpRequests += 1;
|
||||||
|
|
||||||
|
request(testUrl, function(error, response, body) {
|
||||||
|
// 492 is body.length of a removed image
|
||||||
|
if(!error && response.statusCode == 200 && body.length != 492) {
|
||||||
|
dbot.db.imgur.totalImages += 1;
|
||||||
|
var hash = crypto.createHash('md5').update(body).digest("hex");
|
||||||
|
if(_.has(dbot.modules, 'quotes')){
|
||||||
|
// autoadd: {"abcdef": "facebookman"}
|
||||||
|
if(_.has(dbot.config.modules.imgur.autoadd,hash)){
|
||||||
|
fbman = true;
|
||||||
|
var category = this.config.autoadd[hash];
|
||||||
|
if (_.contains(category, testUrl)){
|
||||||
|
// there's probably less than 62^5 chance of this happening
|
||||||
|
} else {
|
||||||
|
dbot.api.quotes.addQuote(category, testUrl,
|
||||||
|
dbot.config.name, function() { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(testUrl, testSlug, hash, fbman);
|
||||||
|
} else {
|
||||||
|
this.api.getRandomImage(callback);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'getGoodRandomImage': function(callback) {
|
||||||
|
this.api.getRandomImage(function(url, slug, hash, fbman) {
|
||||||
|
this.api.getImageInfo(slug, function(imgData) {
|
||||||
|
if(!_.isUndefined(imgData) &&
|
||||||
|
imgData.data &&
|
||||||
|
imgData.data.height > 500 && imgData.data.width > 500 &&
|
||||||
|
!_.any(this.ExcludeRes, function(res) {
|
||||||
|
return imgData.data.height == res.h && imgData.data.width == res.w;
|
||||||
|
})) {
|
||||||
|
callback(url, imgData);
|
||||||
|
} else if(fbman === true) {
|
||||||
|
callback(url, imgData);
|
||||||
|
} else {
|
||||||
|
this.api.getGoodRandomImage(callback);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'getImageInfoString': function(slug, callback) {
|
||||||
|
this.api.getImageInfo(slug, function(imgData) {
|
||||||
|
callback(this.internalAPI.infoString(imgData));
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'getImageInfo': function(slug, callback) {
|
||||||
|
request.get({
|
||||||
|
'url': 'https://api.imgur.com/3/image/' + slug + '.json',
|
||||||
|
'json': true,
|
||||||
|
'headers': {
|
||||||
|
'Authorization': 'Client-ID ' + this.config.apikey
|
||||||
|
}
|
||||||
|
}, function(err, response, body) {
|
||||||
|
dbot.db.imgur.totalApiRequests += 1;
|
||||||
|
callback(body);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'getAlbumInfo': function(slug, callback) {
|
||||||
|
request.get({
|
||||||
|
'url': 'https://api.imgur.com/3/album/' + slug + '.json',
|
||||||
|
'json': true,
|
||||||
|
'headers': {
|
||||||
|
'Authorization': 'Client-ID ' + this.config.apikey
|
||||||
|
}
|
||||||
|
}, function(err, response, body) {
|
||||||
|
this.db.totalApiRequests += 1;
|
||||||
|
callback(body);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'getGalleryInfo': function(slug, callback) {
|
||||||
|
request.get({
|
||||||
|
'url': 'https://api.imgur.com/3/gallery/' + slug + '.json',
|
||||||
|
'json': true,
|
||||||
|
'headers': {
|
||||||
|
'Authorization': 'Client-ID ' + this.config.apikey
|
||||||
|
}
|
||||||
|
}, function(err, response, body) {
|
||||||
|
this.db.totalApiRequests += 1;
|
||||||
|
callback(body);
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.api['getRandomImage'].external = true;
|
||||||
|
this.api['getRandomImage'].extMap = [ 'callback' ];
|
||||||
|
this.api['getImageInfoString'].external = true;
|
||||||
|
this.api['getImageInfoString'].extMap = [ 'slug', 'callback' ];
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~ri': function(event) {
|
||||||
|
var local = event.user;
|
||||||
|
if(event.params[1]) {
|
||||||
|
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
var postImage = function(link, imgData) {
|
||||||
|
var info = this.internalAPI.infoString(imgData);
|
||||||
|
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + link + ' [' + info + ']');
|
||||||
|
}.bind(this);
|
||||||
|
var newCacheImage = function(link, imgData) {
|
||||||
|
this.riCache.push([link, imgData]);
|
||||||
|
}.bind(this);
|
||||||
|
var callback = postImage;
|
||||||
|
|
||||||
|
if(this.riCache.length > 0) {
|
||||||
|
var image = this.riCache.pop();
|
||||||
|
postImage(image[0], image[1]);
|
||||||
|
callback = newCacheImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.getGoodRandomImage(callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Legacy RI
|
||||||
|
'~lri': function(event) {
|
||||||
|
var local = event.user;
|
||||||
|
if(event.params[1]) {
|
||||||
|
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
|
||||||
|
}
|
||||||
|
this.api.getRandomImage(function(link, slug) {
|
||||||
|
this.api.getImageInfo(slug, function(imgData) {
|
||||||
|
var info = this.internalAPI.infoString(imgData);
|
||||||
|
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + link + ' [' + info + ']');
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Super RI
|
||||||
|
'~sri': function(event) {
|
||||||
|
var local = event.user;
|
||||||
|
if(event.params[1]) {
|
||||||
|
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
|
||||||
|
}
|
||||||
|
request.get({
|
||||||
|
'url': this.ApiRoot + 'gallery/random/random/',
|
||||||
|
'json': true,
|
||||||
|
'headers': {
|
||||||
|
'Authorization': 'Client-ID ' + this.config.apikey
|
||||||
|
}
|
||||||
|
}, function(err, response, body) {
|
||||||
|
if(!_.isUndefined(body) && body.data && body.data[0] != undefined) {
|
||||||
|
var num = _.random(0, body.data.length - 1);
|
||||||
|
this.api.getGalleryInfo(body.data[num].id, function(gal) {
|
||||||
|
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + gal.data.link + ' [' +
|
||||||
|
this.internalAPI.galleryInfoString(gal) + ']');
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~imgur': function(event) {
|
||||||
|
var term = event.input[1];
|
||||||
|
request.get({
|
||||||
|
'url': this.ApiRoot + 'gallery/search/',
|
||||||
|
'json': true,
|
||||||
|
'headers': {
|
||||||
|
'Authorization': 'Client-ID ' + this.config.apikey
|
||||||
|
},
|
||||||
|
'qs': {
|
||||||
|
'q': term
|
||||||
|
}
|
||||||
|
}, function(err, response, body) {
|
||||||
|
if(!_.isUndefined(body) && body.data && body.data[0] != undefined) {
|
||||||
|
var num = _.random(0, body.data.length - 1);
|
||||||
|
this.api.getGalleryInfo(body.data[num].id, function(gal) {
|
||||||
|
event.reply(dbot.t('imgurinfo', {
|
||||||
|
'info': this.internalAPI.galleryInfoString(gal)
|
||||||
|
}) + ' - ' + gal.data.link);
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('imgur_noresults'));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.commands['~imgur'].regex = [/^imgur ([\d\w\s-]*)/, 2];
|
||||||
|
|
||||||
|
this.onLoad = function() {
|
||||||
|
var imgurHandler = function(matches, name, callback) {
|
||||||
|
if(matches[1]) {
|
||||||
|
var dataCallback = function(data) {
|
||||||
|
var info;
|
||||||
|
if(name == 'imgurimage') {
|
||||||
|
info = this.internalAPI.infoString(data);
|
||||||
|
} else if(name == 'imguralbum') {
|
||||||
|
info = this.internalAPI.albumInfoString(data);
|
||||||
|
} else if(name == 'imgurgallery') {
|
||||||
|
info = this.internalAPI.galleryInfoString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info) callback(dbot.t('imgurinfo', { 'info': info }));
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
if(name == 'imgurimage') {
|
||||||
|
this.api.getImageInfo(matches[1], dataCallback);
|
||||||
|
} else if(name == 'imguralbum') {
|
||||||
|
this.api.getAlbumInfo(matches[1], dataCallback);
|
||||||
|
} else if(name == 'imgurgallery') {
|
||||||
|
this.api.getGalleryInfo(matches[1], dataCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
dbot.api.link.addHandler('imguralbum', /https?:\/\/imgur\.com\/a\/([a-zA-Z0-9]+)/, imgurHandler);
|
||||||
|
dbot.api.link.addHandler('imgurgallery', /https?:\/\/imgur\.com\/gallery\/([a-zA-Z0-9]+)/, imgurHandler);
|
||||||
|
dbot.api.link.addHandler('imgurimage', /https?:\/\/i\.imgur\.com\/([a-zA-Z0-9]+)\.([jpg|png|gif])/, imgurHandler);
|
||||||
|
dbot.api.link.addHandler('imgurimage', /https?:\/\/imgur\.com\/([a-zA-Z0-9]+)/, imgurHandler);
|
||||||
|
|
||||||
|
async.times(this.config.ricachelength, function(n, next) {
|
||||||
|
this.api.getGoodRandomImage(function(link, imgData) {
|
||||||
|
this.riCache.push([ link, imgData ]);
|
||||||
|
next();
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this), function() {});
|
||||||
|
|
||||||
|
if(!_.has(dbot.db.imgur, 'totalHttpRequests')) dbot.db.imgur.totalHttpRequests = 0;
|
||||||
|
if(!_.has(dbot.db.imgur, 'totalApiRequests')) dbot.db.imgur.totalApiRequests = 0;
|
||||||
|
if(!_.has(dbot.db.imgur, 'totalImages')) dbot.db.imgur.totalImages = 0;
|
||||||
|
this.db = dbot.db.imgur;
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new imgur(dbot);
|
||||||
|
}
|
23
modules-stock/imgur/pages.js
Normal file
23
modules-stock/imgur/pages.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var pages = function(dbot) {
|
||||||
|
return {
|
||||||
|
'/imgur/random': function(req, res) {
|
||||||
|
var highScore = 0;
|
||||||
|
res.render('imgurr', { "highscore" : highScore });
|
||||||
|
},
|
||||||
|
|
||||||
|
'/imgur/stats': function(req, res) {
|
||||||
|
res.render('imgurstats', {
|
||||||
|
'name': dbot.config.name,
|
||||||
|
'totalHttpRequests': this.db.totalHttpRequests,
|
||||||
|
'totalApiRequests': this.db.totalApiRequests,
|
||||||
|
'totalImages': this.db.totalImages
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return pages(dbot);
|
||||||
|
};
|
22
modules-stock/imgur/strings.json
Normal file
22
modules-stock/imgur/strings.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"nsfw": {
|
||||||
|
"en": "might be NSFW",
|
||||||
|
"na'vi": "kxawm ke wivìntxu evengur",
|
||||||
|
"cy": "Gallai fod yn anniogel ar gwaith",
|
||||||
|
"nl": "bevat mogelijk gevoelige beelden",
|
||||||
|
"de": "Könnte 18+ Material enthalten",
|
||||||
|
"fr": "peut être risqué pour le travail (NSFW)",
|
||||||
|
"it": "può essere rischioso al lavoro (NSFW)"
|
||||||
|
},
|
||||||
|
"imgurinfo": {
|
||||||
|
"en": "[{info}]",
|
||||||
|
"de": "[{info}]",
|
||||||
|
"fr": "[{info}]",
|
||||||
|
"it": "[{info}]"
|
||||||
|
},
|
||||||
|
"imgur_noresults": {
|
||||||
|
"en": "No results found.",
|
||||||
|
"de": "Kein Suchergebnis.",
|
||||||
|
"it": "Nessun risultato"
|
||||||
|
}
|
||||||
|
}
|
40
modules-stock/js/README.md
Normal file
40
modules-stock/js/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
## JS
|
||||||
|
|
||||||
|
Run JavaScript.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module provides two commands which allow the execution of Javascript code
|
||||||
|
from the bot.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~js [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.
|
||||||
|
|
||||||
|
#### ~ajs [code]
|
||||||
|
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 your
|
||||||
|
DBot instance, as there's nothing to stop them wiping the database or probably
|
||||||
|
even your hard drive - 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 it for debugging, or even adding new commands while DBot is
|
||||||
|
running.
|
9
modules-stock/js/config.json
Normal file
9
modules-stock/js/config.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"~js": {
|
||||||
|
"disabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": [ "command" ],
|
||||||
|
"ignorable": true
|
||||||
|
}
|
51
modules-stock/js/js.js
Normal file
51
modules-stock/js/js.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: JS
|
||||||
|
* Description: Allows users to run sandboxed JS code, printing the result in
|
||||||
|
* the channel. Also allows admins to run un-sandboxed Javascript code with
|
||||||
|
* access to the DepressionBot instance memory.
|
||||||
|
*/
|
||||||
|
var vm = require('vm');
|
||||||
|
var sbox = require('sandbox');
|
||||||
|
|
||||||
|
var js = function(dbot) {
|
||||||
|
var commands = {
|
||||||
|
// Run JS code sandboxed, return result to channel.
|
||||||
|
'~js': function(event) {
|
||||||
|
try {
|
||||||
|
var s = new sbox();
|
||||||
|
s.run(event.input[1], function(output) {
|
||||||
|
event.reply(output.result);
|
||||||
|
}.bind(this));
|
||||||
|
} catch(err) {}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Run JS code un-sandboxed, with access to DBot memory (admin-only).
|
||||||
|
'~ajs': function(event) {
|
||||||
|
var callback = function() {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
for(var i=0;i<args.length;i++) {
|
||||||
|
var arg = args[i];
|
||||||
|
if(_.isObject(arg) && !_.isArray(arg)) {
|
||||||
|
arg = '[object Object]: ' + _.keys(arg).join(', ');
|
||||||
|
}
|
||||||
|
event.reply('Callback[' + i + ']: ' + arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var ret = eval(event.input[1]);
|
||||||
|
if(ret !== undefined) {
|
||||||
|
event.reply(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
commands['~js'].regex = [/^js (.*)/, 2];
|
||||||
|
commands['~ajs'].regex = [/^ajs (.*)/, 2];
|
||||||
|
commands['~ajs'].access = 'admin';
|
||||||
|
|
||||||
|
this.name = 'js';
|
||||||
|
this.ignorable = true;
|
||||||
|
this.commands = commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new js(dbot);
|
||||||
|
};
|
4
modules-stock/js/usage.json
Normal file
4
modules-stock/js/usage.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"~js": "~js [command]",
|
||||||
|
"~ajs": "~ajs [command]"
|
||||||
|
}
|
5
modules-stock/karma/config.json
Normal file
5
modules-stock/karma/config.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"announce": [ { "server": "tripsit", "name": "#dbot" } ],
|
||||||
|
"dbKeys": [ "karma" ],
|
||||||
|
"dbType": "redis"
|
||||||
|
}
|
201
modules-stock/karma/karma.js
Normal file
201
modules-stock/karma/karma.js
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Karma
|
||||||
|
* Description: Thanking, with Karma!
|
||||||
|
*/
|
||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var karma = function(dbot) {
|
||||||
|
this.lastKarma = {};
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
'getKarma': function(item, callback) {
|
||||||
|
this.db.read('karma', item.toLowerCase(), callback);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
'setKarma': function(item, value, callback) {
|
||||||
|
this.db.save('karma', item.toLowerCase(), {
|
||||||
|
'item': item.toLowerCase(),
|
||||||
|
'karma': value
|
||||||
|
}, callback);
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'karma': function(event) {
|
||||||
|
var item = event.params[1] || event.user;
|
||||||
|
this.internalAPI.getKarma(item, function(err, karma) {
|
||||||
|
if(!err && karma) {
|
||||||
|
karma = karma.karma;
|
||||||
|
} else {
|
||||||
|
karma = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(dbot.t('karma', {
|
||||||
|
'item': item,
|
||||||
|
'karma': karma
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'setkarma': function(event) {
|
||||||
|
var item = event.params[1],
|
||||||
|
value = parseInt(event.params[2]);
|
||||||
|
|
||||||
|
this.internalAPI.setKarma(item, value, function(err, karma) {
|
||||||
|
event.reply(dbot.t('newkarma', {
|
||||||
|
'item': item,
|
||||||
|
'value': value
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'unkarmaiest': function(event) {
|
||||||
|
var karmas = {};
|
||||||
|
this.db.scan('karma', function(karma) {
|
||||||
|
if(karma && !_.isUndefined(karma.item)) {
|
||||||
|
karmas[karma.item] = karma.karma;
|
||||||
|
}
|
||||||
|
}.bind(this), function(err) {
|
||||||
|
var qSizes = _.chain(karmas)
|
||||||
|
.pairs()
|
||||||
|
.sortBy(function(category) { return category[1]; })
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var qString = 'Unkarmaiest: ';
|
||||||
|
for(var i=0;i<qSizes.length;i++) {
|
||||||
|
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(qString.slice(0, -2));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'karmaiest': function(event) {
|
||||||
|
var karmas = {};
|
||||||
|
this.db.scan('karma', function(karma) {
|
||||||
|
if(karma && !_.isUndefined(karma.item)) {
|
||||||
|
karmas[karma.item] = karma.karma;
|
||||||
|
}
|
||||||
|
}.bind(this), function(err) {
|
||||||
|
var qSizes = _.chain(karmas)
|
||||||
|
.pairs()
|
||||||
|
.sortBy(function(category) { return category[1]; })
|
||||||
|
.reverse()
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var qString = 'Karmaiest: ';
|
||||||
|
for(var i=0;i<qSizes.length;i++) {
|
||||||
|
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(qString.slice(0, -2));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'wattest': function(event) {
|
||||||
|
var karmas = {};
|
||||||
|
this.db.scan('karma', function(karma) {
|
||||||
|
if(karma && !_.isUndefined(karma.item)) {
|
||||||
|
if(karma.item.match(/_wat$/)) {
|
||||||
|
karmas[karma.item] = karma.karma;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this), function(err) {
|
||||||
|
var qSizes = _.chain(karmas)
|
||||||
|
.pairs()
|
||||||
|
.sortBy(function(category) { return category[1]; })
|
||||||
|
.reverse()
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var qString = 'Karmaiest: ';
|
||||||
|
for(var i=0;i<qSizes.length;i++) {
|
||||||
|
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(qString.slice(0, -2));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
this.commands.setkarma.access = 'admin';
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
dbot.api.ignore.isUserBanned(event.rUser, 'karma', function(isBanned) {
|
||||||
|
if(!isBanned) {
|
||||||
|
var match = event.message.match(/^(.+)(\+\+|\-\-)$/);
|
||||||
|
if(event.user !== dbot.config.name && match && match[1].length < 25) {
|
||||||
|
match[1] = match[1].replace(/(\+|\-)/g,'').replace(/:/g,'').trim();
|
||||||
|
|
||||||
|
var timeout = 5000;
|
||||||
|
/* if(event.channel.name == '#stims' || event.channel.name == '##meth' || event.channel.name == '##sweden') {
|
||||||
|
timeout = 20000;
|
||||||
|
}*/
|
||||||
|
if(_.has(this.lastKarma, event.rUser.id) && this.lastKarma[event.rUser.id]+ timeout > Date.now()) {
|
||||||
|
return event.reply('Try again in a few seconds : - )');
|
||||||
|
} else if(event.rUser.currentNick.toLowerCase() === match[1].toLowerCase() || event.rUser.primaryNick.toLowerCase() === match[1].toLowerCase()) {
|
||||||
|
return event.reply('Stop playing with yourself : - )');
|
||||||
|
} else if(event.channel == event.user) {
|
||||||
|
return event.reply('Don\'t be a Secretive Sally : - )');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.channel.name == '##wat') {
|
||||||
|
match[1] = match[1].replace(/_wat$/, '');
|
||||||
|
match[1] += '_wat';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalAPI.getKarma(match[1], function(err, karma) {
|
||||||
|
if(!karma) {
|
||||||
|
karma = 0;
|
||||||
|
} else {
|
||||||
|
karma = karma.karma;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[2] === '--') {
|
||||||
|
if(match[1].toLowerCase() =='weed') {
|
||||||
|
karma -= 2;
|
||||||
|
} else {
|
||||||
|
karma -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(match[1].toLowerCase() == 'weed') {
|
||||||
|
karma += 2;
|
||||||
|
} else {
|
||||||
|
karma += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalAPI.setKarma(match[1], karma, function(err, karma) {
|
||||||
|
this.lastKarma[event.rUser.id] = Date.now();
|
||||||
|
var pre;
|
||||||
|
if(karma.karma > 0) {
|
||||||
|
pre = '[\u00039karma\u000f]';
|
||||||
|
karma.karma = '\u00039 '+karma.karma+'\u000f';
|
||||||
|
} else if(karma.karma < 0) {
|
||||||
|
pre = '[\u00034karma\u000f]';
|
||||||
|
karma.karma = '\u00034 '+karma.karma+'\u000f';
|
||||||
|
} else {
|
||||||
|
pre = '[\u00036karma\u000f]';
|
||||||
|
karma.karma = '\u00036 '+karma.karma+'\u000f';
|
||||||
|
}
|
||||||
|
event.reply(pre + ' ' + dbot.t('newkarma', {
|
||||||
|
'item': match[1],
|
||||||
|
'value': karma.karma
|
||||||
|
}));
|
||||||
|
if(_.has(dbot.modules, 'log')) {
|
||||||
|
dbot.api.log.logWithChannel(event.server, event.channel, event.rUser.primaryNick, event.message);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this);
|
||||||
|
this.on = 'PRIVMSG';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new karma(dbot);
|
||||||
|
};
|
8
modules-stock/karma/strings.json
Normal file
8
modules-stock/karma/strings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"karma": {
|
||||||
|
"en": "[karma] {item} has {karma} karma"
|
||||||
|
},
|
||||||
|
"newkarma": {
|
||||||
|
"en": "{item} now has{value} karma"
|
||||||
|
}
|
||||||
|
}
|
29
modules-stock/kick/README.md
Normal file
29
modules-stock/kick/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
## Kick
|
||||||
|
|
||||||
|
Kicking and kicking-related accessories.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
This module counts the number of times people are kicked from and kick people
|
||||||
|
from channels, and provides commands for viewing this data. It also has the bot
|
||||||
|
attempt to rejoin the channel if it is kicked.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
#### ~kickcount [username]
|
||||||
|
Show the number of times a given user has been kicked and has kicked other
|
||||||
|
people.
|
||||||
|
|
||||||
|
#### ~kickstats
|
||||||
|
Show a list of top kickers and kickees.
|
||||||
|
|
||||||
|
#### ~ckick [#channel] [username] [reason]
|
||||||
|
Kick a user from a channel.
|
||||||
|
|
||||||
|
#### ~cban [#channel] [username] [reason]
|
||||||
|
Ban a user from a channel.
|
||||||
|
|
||||||
|
#### ~nban {optional: Duration in Hours} [username] [reason]
|
||||||
|
Ban a user from the network.
|
||||||
|
|
||||||
|
#### ~nunban [username] [reason]
|
||||||
|
Unban a user from the network.
|
512
modules-stock/kick/commands.js
Normal file
512
modules-stock/kick/commands.js
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
var _ = require('underscore')._,
|
||||||
|
uuid = require('node-uuid');
|
||||||
|
|
||||||
|
var commands = function (dbot) {
|
||||||
|
var commands = {
|
||||||
|
/*** Kick Management ***/
|
||||||
|
'~quiet': function (event) {
|
||||||
|
var server = event.server,
|
||||||
|
quieter = event.rUser,
|
||||||
|
duration = event.input[1],
|
||||||
|
channel = (event.input[2] || event.channel.name).trim(),
|
||||||
|
quietee = event.input[3].trim(),
|
||||||
|
reason = event.input[4] || "N/A";
|
||||||
|
|
||||||
|
this.api.quietUser(server, quieter, duration, channel, quietee, reason, function (response) {
|
||||||
|
event.reply(response);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~timeout': function (event) {
|
||||||
|
var server = event.server,
|
||||||
|
quieter = event.rUser,
|
||||||
|
duration = this.config.timeoutTime,
|
||||||
|
channel = event.channel.name,
|
||||||
|
quietee = event.input[1],
|
||||||
|
reason = event.input[2] || "N/A";
|
||||||
|
|
||||||
|
reason += ' #timeout';
|
||||||
|
|
||||||
|
dbot.api.users.resolveUser(server, quietee, function (err, user) {
|
||||||
|
if (!err && user) {
|
||||||
|
if (!_.has(this.recentTimeouts, user.id)) {
|
||||||
|
this.recentTimeouts[user.id] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recentTimeouts[user.id] += 1;
|
||||||
|
setTimeout(function () {
|
||||||
|
this.recentTimeouts[user.id] -= 1;
|
||||||
|
if (this.recentTimeouts[user.id] == 0) {
|
||||||
|
delete this.recentTimeouts[user.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this), 3600000);
|
||||||
|
|
||||||
|
if (this.recentTimeouts[user.id] == 3) {
|
||||||
|
duration = null;
|
||||||
|
reason += ' #permatimeout';
|
||||||
|
dbot.say(event.server, dbot.config.servers[event.server].admin_channel, quietee + ' has been given three timeouts in the last hour, and so has been quieted indefinitely in ' + channel + '. Please review.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.quietUser(server, quieter, duration, channel, quietee, reason, function (response) {
|
||||||
|
event.reply(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~unquiet': function (event) {
|
||||||
|
var server = event.server,
|
||||||
|
quieter = event.user,
|
||||||
|
channel = (event.input[1] || event.channel.name).trim(),
|
||||||
|
quietee = event.input[2].trim();
|
||||||
|
|
||||||
|
if (_.has(this.hosts[server], quietee)) {
|
||||||
|
if (_.include(this.config.quietBans, channel)) {
|
||||||
|
this.api.unban(server, this.hosts[server][quietee], channel);
|
||||||
|
} else {
|
||||||
|
this.api.unquiet(server, this.hosts[server][quietee], channel);
|
||||||
|
}
|
||||||
|
event.reply(dbot.t('unquieted', {
|
||||||
|
'quietee': quietee
|
||||||
|
}));
|
||||||
|
dbot.api.report.notify('unquiet', server, event.rUser, channel,
|
||||||
|
dbot.t('unquiet_notify', {
|
||||||
|
'unquieter': quieter,
|
||||||
|
'quietee': quietee
|
||||||
|
}), false, quietee);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~ckick': function (event) {
|
||||||
|
var server = event.server,
|
||||||
|
kicker = event.user,
|
||||||
|
kickee = event.input[2],
|
||||||
|
channel = event.input[1],
|
||||||
|
reason = event.input[3];
|
||||||
|
|
||||||
|
if (_.isUndefined(channel)) {
|
||||||
|
channel = event.channel.name;
|
||||||
|
}
|
||||||
|
channel = channel.trim();
|
||||||
|
|
||||||
|
this.api.kick(server, kickee, channel, reason + ' (requested by ' + kicker + ')');
|
||||||
|
|
||||||
|
dbot.api.report.notify('kick', server, event.rUser, channel, dbot.t('ckicked', {
|
||||||
|
'kicker': kicker,
|
||||||
|
'kickee': kickee,
|
||||||
|
'reason': reason
|
||||||
|
}), false, kickee);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Kick and ban from all channels on the network.
|
||||||
|
'~nban': function (event) {
|
||||||
|
if (!event.input)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var server = event.server,
|
||||||
|
banner = event.user,
|
||||||
|
timeout = event.input[1],
|
||||||
|
banee = event.input[2],
|
||||||
|
reason = event.input[3],
|
||||||
|
adminChannel = dbot.config.servers[server].admin_channel,
|
||||||
|
channels = _.keys(dbot.instance.connections[server].channels),
|
||||||
|
network = event.server;
|
||||||
|
|
||||||
|
if (this.config.network_name[event.server]) {
|
||||||
|
network = this.config.network_name[event.server];
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.api.nickserv.getUserHost(event.server, banee, function (host) {
|
||||||
|
// Add host record entry
|
||||||
|
if (host) {
|
||||||
|
var didKill = false;
|
||||||
|
|
||||||
|
if ((reason.match('#line') || reason.match('#specialk') || reason.match('#kline')) && _.include(dbot.access.moderator(), event.rUser.primaryNick)) {
|
||||||
|
didKill = true;
|
||||||
|
var t = ' !P ';
|
||||||
|
if (timeout) {
|
||||||
|
t = ' !T ' + timeout + ' ';
|
||||||
|
}
|
||||||
|
dbot.say(event.server, 'operserv', 'akill add ' + banee + t + banee + ' banned by ' + banner + ': ' + reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not ban if user was killed - redundant
|
||||||
|
if(!didKill) {
|
||||||
|
// Ban from current channel first
|
||||||
|
this.api.ban(server, host, event.channel);
|
||||||
|
this.api.kick(server, banee, event.channel, reason +
|
||||||
|
' (network-wide ban)');
|
||||||
|
channels = _.without(channels, event.channel);
|
||||||
|
if (!_.isUndefined(adminChannel)) {
|
||||||
|
channels = _.without(channels, adminChannel);
|
||||||
|
} else {
|
||||||
|
adminChannel = event.channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ban the user from all channels
|
||||||
|
var i = 0;
|
||||||
|
var banChannel = function (channels) {
|
||||||
|
if (i >= channels.length)
|
||||||
|
return;
|
||||||
|
var channel = channels[i];
|
||||||
|
this.api.ban(server, host, channel);
|
||||||
|
this.api.kick(server, banee, channel, reason +
|
||||||
|
' (network-wide ban)');
|
||||||
|
i++;
|
||||||
|
banChannel(channels);
|
||||||
|
}
|
||||||
|
.bind(this);
|
||||||
|
banChannel(channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hosts[event.server][banee] = host;
|
||||||
|
|
||||||
|
// Create notify string
|
||||||
|
if (!_.isUndefined(timeout)) {
|
||||||
|
timeout = timeout.trim();
|
||||||
|
|
||||||
|
var msTimeout = new Date(new Date().getTime() + (parseFloat(timeout) * 3600000));
|
||||||
|
if (_.has(dbot.modules, 'remind')) {
|
||||||
|
msTimeout = dbot.api.remind.parseTime(timeout);
|
||||||
|
if (!msTimeout) {
|
||||||
|
return event.reply('Invalid time. Remember you must give e.g. 5m now.');
|
||||||
|
}
|
||||||
|
timeout = timeout.replace(/([\d]+)d/, '$1 days').replace(/([\d]+)h/, '$1 hours ').replace(/([\d]+)m/, '$1 minutes ').replace(/([\d]+)s/, '$1 seconds').trim();
|
||||||
|
} else {
|
||||||
|
timeout += ' hours';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not schedule unbans if the user was killed as no ban was put in place
|
||||||
|
if(!didKill) {
|
||||||
|
if (!_.has(this.tempBans, event.server))
|
||||||
|
this.tempBans[event.server] = {};
|
||||||
|
this.tempBans[event.server][banee] = msTimeout;
|
||||||
|
this.internalAPI.addTempBan(event.server, banee, msTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifyString = dbot.t('tbanned', {
|
||||||
|
'network': network,
|
||||||
|
'banner': banner,
|
||||||
|
'banee': banee,
|
||||||
|
'hours': timeout,
|
||||||
|
'host': host,
|
||||||
|
'reason': reason
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var notifyString = dbot.t('nbanned', {
|
||||||
|
'network': network,
|
||||||
|
'banner': banner,
|
||||||
|
'banee': banee,
|
||||||
|
'host': host,
|
||||||
|
'reason': reason
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add db entry documenting ban
|
||||||
|
if (this.config.document_bans) {
|
||||||
|
var id = uuid.v4();
|
||||||
|
var banRecord = {
|
||||||
|
'id': id,
|
||||||
|
'time': new Date().getTime(),
|
||||||
|
'server': server,
|
||||||
|
'banee': banee,
|
||||||
|
'banner': banner,
|
||||||
|
'host': host,
|
||||||
|
'reason': reason
|
||||||
|
};
|
||||||
|
this.db.save('nbans', id, banRecord, function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify moderators, banee
|
||||||
|
if (!_.isUndefined(adminChannel)) {
|
||||||
|
channels = _.without(channels, adminChannel);
|
||||||
|
} else {
|
||||||
|
adminChannel = event.channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.api.report.notify('ban', server, event.rUser, adminChannel, notifyString, false, banee);
|
||||||
|
dbot.say(event.server, adminChannel, notifyString);
|
||||||
|
|
||||||
|
if (!_.isUndefined(timeout)) {
|
||||||
|
dbot.say(event.server, banee, dbot.t('tbanned_notify', {
|
||||||
|
'network': network,
|
||||||
|
'banner': banner,
|
||||||
|
'reason': reason,
|
||||||
|
'hours': timeout,
|
||||||
|
'admin_channel': adminChannel
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dbot.say(event.server, banee, dbot.t('nbanned_notify', {
|
||||||
|
'network': network,
|
||||||
|
'banner': banner,
|
||||||
|
'reason': reason,
|
||||||
|
'hours': timeout,
|
||||||
|
'admin_channel': adminChannel
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// err
|
||||||
|
dbot.say(event.server, 'NickServ', 'FREEZE ' + banee + ' ON ' + reason);
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('no_user', {
|
||||||
|
'user': banee
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~nunban': function (event) {
|
||||||
|
var unbanee = event.params[1],
|
||||||
|
host = event.params[2] || undefined,
|
||||||
|
unbanner = event.rUser;
|
||||||
|
|
||||||
|
this.api.networkUnban(event.server, unbanee, unbanner, host, function (err) {
|
||||||
|
if (err) {
|
||||||
|
event.reply(dbot.t('nunban_error', {
|
||||||
|
'unbanee': unbanee
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*** Kick Stats ***/
|
||||||
|
|
||||||
|
// Give the number of times a given user has been kicked and has kicked
|
||||||
|
// other people.
|
||||||
|
'~kickcount': function (event) {
|
||||||
|
var username = event.params[1];
|
||||||
|
|
||||||
|
if (!_.has(dbot.db.kicks, username)) {
|
||||||
|
var kicks = '0';
|
||||||
|
} else {
|
||||||
|
var kicks = dbot.db.kicks[username];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.has(dbot.db.kickers, username)) {
|
||||||
|
var kicked = '0';
|
||||||
|
} else {
|
||||||
|
var kicked = dbot.db.kickers[username];
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(dbot.t('user_kicks', {
|
||||||
|
'user': username,
|
||||||
|
'kicks': kicks,
|
||||||
|
'kicked': kicked
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Output a list of the people who have been kicked the most and those
|
||||||
|
// who have kicked other people the most.
|
||||||
|
'~kickstats': function (event) {
|
||||||
|
var orderedKickLeague = function (list, topWhat) {
|
||||||
|
var kickArr = _.chain(list)
|
||||||
|
.pairs()
|
||||||
|
.sortBy(function (kick) {
|
||||||
|
return kick[1]
|
||||||
|
})
|
||||||
|
.reverse()
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var kickString = "Top " + topWhat + ": ";
|
||||||
|
for (var i = 0; i < kickArr.length; i++) {
|
||||||
|
kickString += kickArr[i][0] + " (" + kickArr[i][1] + "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return kickString.slice(0, -2);
|
||||||
|
};
|
||||||
|
|
||||||
|
event.reply(orderedKickLeague(dbot.db.kicks, 'Kicked'));
|
||||||
|
event.reply(orderedKickLeague(dbot.db.kickers, 'Kickers'));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~votequiet': function (event) {
|
||||||
|
var target = event.input[1],
|
||||||
|
reason = event.input[2];
|
||||||
|
|
||||||
|
if (_.has(event.channel.nicks, target)) {
|
||||||
|
dbot.api.users.resolveUser(event.server, target, function (err, user) {
|
||||||
|
if (!err && user) {
|
||||||
|
if (_.include(dbot.access.power_user(), user.primaryNick) || target == dbot.config.name) {
|
||||||
|
return event.reply('User is immune to votequiet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.has(this.voteQuiets, user.id)) {
|
||||||
|
this.voteQuiets[user.id] = {
|
||||||
|
'user': user.id,
|
||||||
|
'reason': reason,
|
||||||
|
'channel': event.channel,
|
||||||
|
'yes': [event.rUser.primaryNick],
|
||||||
|
'no': []
|
||||||
|
};
|
||||||
|
event.reply(event.user + ' has started a vote to quiet ' + target + ' for "' + reason + '." Type either "~voteyes ' + target + '" or "~voteno ' + target + '" in the next 90 seconds.');
|
||||||
|
|
||||||
|
this.voteQuiets[user.id].timer = setTimeout(function () {
|
||||||
|
var vq = this.voteQuiets[user.id];
|
||||||
|
vq.spent = true;
|
||||||
|
if (vq.yes.length >= 3 && vq.no.length < 2) {
|
||||||
|
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
|
||||||
|
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, reason + '[votequiet]', function (response) {
|
||||||
|
clearTimeout(vq.timer);
|
||||||
|
event.reply(response);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply('Attempt to quiet ' + target + ' failed. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
}
|
||||||
|
|
||||||
|
var nString = 'A votequiet was attempted on ' + target + ' in ' + event.channel + '. It was initiated by ' + event.rUser.primaryNick + '. ' +
|
||||||
|
vq.yes.join(', ') + ' voted yes (' + vq.yes.length + '). ';
|
||||||
|
if (vq.no.length > 0) {
|
||||||
|
nString += vq.no.join(', ') + ' voted no (' + vq.no.length + ').'
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.api.report.notify('votequiet', event.server, event.rUser, event.channel, nString, false, target);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
delete this.voteQuiets[user.id];
|
||||||
|
}
|
||||||
|
.bind(this), 600000);
|
||||||
|
}
|
||||||
|
.bind(this), 90000);
|
||||||
|
} else {
|
||||||
|
if (this.voteQuiets[user.id].spent) {
|
||||||
|
event.reply('A votequiet attempt has already been made on this user in the last 10 minutes.');
|
||||||
|
} else {
|
||||||
|
var vq = this.voteQuiets[user.id]
|
||||||
|
if (!_.include(vq.yes, event.rUser.primaryNick)) {
|
||||||
|
vq.yes.push(event.rUser.primaryNick);
|
||||||
|
|
||||||
|
event.reply('There is already a votequiet attempt active for this user, adding yes vote to existing poll.');
|
||||||
|
event.reply('Voted yes on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
|
||||||
|
if (vq.yes.length == 4) {
|
||||||
|
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, reason + '[votequiet]', function (response) {
|
||||||
|
clearTimeout(vq.timer);
|
||||||
|
vq.spent = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
delete this.voteQuiets[user.id];
|
||||||
|
}
|
||||||
|
.bind(this), 600000);
|
||||||
|
event.reply(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('There is already a votequiet attempt active for this user, and you already voted yes!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('Target does not seem to be in the channel.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply('Target does not seem to be in the channel.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'~voteyes': function (event) {
|
||||||
|
var target = event.params[1];
|
||||||
|
|
||||||
|
dbot.api.users.resolveUser(event.server, target, function (err, user) {
|
||||||
|
if (!err && user) {
|
||||||
|
if (user.id == event.rUser.id) {
|
||||||
|
return event.reply('You cannot vote on your own silencing. Be good.');
|
||||||
|
}
|
||||||
|
if (_.has(this.voteQuiets, user.id) && !this.voteQuiets[user.id].spent) {
|
||||||
|
var vq = this.voteQuiets[user.id];
|
||||||
|
if (event.channel != vq.channel) {
|
||||||
|
return event.reply('Vote must be in ' + vq.channel);
|
||||||
|
}
|
||||||
|
if (!_.include(vq.yes, event.rUser.primaryNick) && !_.include(vq.no, event.rUser.primaryNick)) {
|
||||||
|
vq.yes.push(event.rUser.primaryNick);
|
||||||
|
event.reply('Voted yes on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
|
||||||
|
if (vq.yes.length == 4) {
|
||||||
|
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, vq.reason + '[votequiet]', function (response) {
|
||||||
|
clearTimeout(vq.timer);
|
||||||
|
vq.spent = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
delete this.voteQuiets[user.id];
|
||||||
|
}
|
||||||
|
.bind(this), 600000);
|
||||||
|
event.reply(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('You have already voted.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('There is no active votequiet for this user. You can start one by typing "~votequiet ' + target + ' [reason].');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('No idea who that is m8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~voteno': function (event) {
|
||||||
|
var target = event.params[1];
|
||||||
|
|
||||||
|
dbot.api.users.resolveUser(event.server, target, function (err, user) {
|
||||||
|
if (!err && user) {
|
||||||
|
if (user.id == event.rUser.id) {
|
||||||
|
return event.reply('You cannot vote on your own silencing. Be good.');
|
||||||
|
}
|
||||||
|
if (_.has(this.voteQuiets, user.id) && !this.voteQuiets[user.id].spent) {
|
||||||
|
var vq = this.voteQuiets[user.id];
|
||||||
|
if (event.channel != vq.channel) {
|
||||||
|
return event.reply('Vote must be in ' + vq.channel);
|
||||||
|
}
|
||||||
|
if (!_.include(vq.yes, event.rUser.primaryNick) && !_.include(vq.no, event.rUser.primaryNick)) {
|
||||||
|
vq.no.push(event.rUser.primaryNick);
|
||||||
|
event.reply('Voted no on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
|
||||||
|
} else {
|
||||||
|
event.reply('You have already voted.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('There is no active votequiet for this user. You can start one by typing "~votequiet ' + target + ' [reason].');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply('No idea who that is m8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_.each(commands, function (command) {
|
||||||
|
command.access = 'moderator';
|
||||||
|
});
|
||||||
|
|
||||||
|
commands['~kickcount'].access = 'regular';
|
||||||
|
commands['~kickstats'].access = 'regular';
|
||||||
|
commands['~votequiet'].access = 'regular';
|
||||||
|
commands['~voteyes'].access = 'regular';
|
||||||
|
commands['~voteno'].access = 'regular';
|
||||||
|
commands['~quiet'].access = 'voice';
|
||||||
|
commands['~timeout'].access = 'voice';
|
||||||
|
commands['~unquiet'].access = 'voice';
|
||||||
|
commands['~nban'].access = 'power_user';
|
||||||
|
commands['~nunban'].access = 'power_user';
|
||||||
|
|
||||||
|
commands['~ckick'].regex = /^ckick (#[^ ]+ )?([^ ]+) ?(.*)?$/;
|
||||||
|
commands['~nban'].regex = /^nban (\d[\d\.dhmsy]+)? ?([^ ]+) (.+)$/;
|
||||||
|
commands['~quiet'].regex = /^quiet (\d[\d\.hmsy]+)? ?(#[^ ]+ )?([^ ]+) ?(.*)?$/;
|
||||||
|
commands['~timeout'].regex = /^timeout ([^ ]+) ?(.*)?$/;
|
||||||
|
commands['~unquiet'].regex = /^unquiet (#[^ ]+ )?([^ ]+) ?$/;
|
||||||
|
commands['~votequiet'].regex = [/^votequiet ([^ ]+) (.+)$/, 3];
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function (dbot) {
|
||||||
|
return commands(dbot);
|
||||||
|
};
|
18
modules-stock/kick/config.json
Normal file
18
modules-stock/kick/config.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"dbKeys": [ "kicks", "kickers", "hosts", "tempBans" ],
|
||||||
|
"dependencies": [ "command", "report", "users" ],
|
||||||
|
"ignorable": true,
|
||||||
|
"countSilently": true,
|
||||||
|
"quietBans": [
|
||||||
|
"#wherever"
|
||||||
|
],
|
||||||
|
"network_name": {
|
||||||
|
"aberwiki": "OAOSIDL"
|
||||||
|
},
|
||||||
|
"chanserv": "ChanServ",
|
||||||
|
"document_bans": false,
|
||||||
|
"dbType": "redis",
|
||||||
|
"requireWebLogin": true,
|
||||||
|
"webAccess": "power_user",
|
||||||
|
"timeoutTime": "10m"
|
||||||
|
}
|
270
modules-stock/kick/kick.js
Normal file
270
modules-stock/kick/kick.js
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var kick = function (dbot) {
|
||||||
|
if (!_.has(dbot.db, 'recentTimeouts')) {
|
||||||
|
dbot.db.recentTimeouts = {};
|
||||||
|
}
|
||||||
|
this.recentTimeouts = dbot.db.recentTimeouts;
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'ban': function (server, host, channel) {
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' +b *!*@' + host);
|
||||||
|
},
|
||||||
|
|
||||||
|
'quiet': function (server, host, channel) {
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' +q *!*@' + host);
|
||||||
|
},
|
||||||
|
|
||||||
|
'unquiet': function (server, host, channel) {
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' -q *!*@' + host);
|
||||||
|
},
|
||||||
|
|
||||||
|
'devoice': function (server, nick, channel) {
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' -v ' + nick);
|
||||||
|
},
|
||||||
|
|
||||||
|
'voice': function (server, nick, channel) {
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' +v ' + nick);
|
||||||
|
},
|
||||||
|
|
||||||
|
'kick': function (server, user, channel, msg) {
|
||||||
|
dbot.instance.connections[server].send('KICK ' + channel + ' ' + user + ' :' + msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
'kill': function (server, user, reason) {
|
||||||
|
dbot.instance.connections[server].send('kill ' + user + ' ' + reason);
|
||||||
|
},
|
||||||
|
|
||||||
|
'unban': function (server, host, channel) {
|
||||||
|
// TODO: Wrest control from chanserv
|
||||||
|
//dbot.say(server, this.config.chanserv, 'unban ' + channel + ' *!*@' + host);
|
||||||
|
dbot.instance.connections[server].send('MODE ' + channel + ' -b *!*@' + host);
|
||||||
|
},
|
||||||
|
|
||||||
|
'quietUser': function (server, quieter, duration, channel, quietee, reason, callback) {
|
||||||
|
dbot.api.nickserv.getUserHost(server, quietee, function (host) {
|
||||||
|
// Add host record entry
|
||||||
|
if (host) {
|
||||||
|
this.hosts[server][quietee] = host;
|
||||||
|
|
||||||
|
if (!_.isUndefined(duration) && !_.isNull(duration)) {
|
||||||
|
duration = duration.trim();
|
||||||
|
var msTimeout = new Date(new Date().getTime() + (parseFloat(duration) * 60000));
|
||||||
|
if (_.has(dbot.modules, 'remind')) {
|
||||||
|
msTimeout = dbot.api.remind.parseTime(duration);
|
||||||
|
if (!msTimeout) {
|
||||||
|
return callback('Invalid time. Remember you must give e.g. 5m now.');
|
||||||
|
}
|
||||||
|
duration = duration.replace(/([\d]+)d/, '$1 years').replace(/([\d]+)d/, '$1 days').replace(/([\d]+)h/, '$1 hours ').replace(/([\d]+)m/, '$1 minutes ').replace(/([\d]+)s/, '$1 seconds').trim();
|
||||||
|
} else {
|
||||||
|
duration += ' minutes';
|
||||||
|
}
|
||||||
|
|
||||||
|
var vStatus = dbot.instance.connections[server].channels[channel].nicks[quietee].voice;
|
||||||
|
dbot.api.timers.addTimeout(msTimeout, function () {
|
||||||
|
if (_.has(this.hosts[server], quietee)) {
|
||||||
|
if (_.include(this.config.quietBans, channel)) {
|
||||||
|
this.api.unban(server, this.hosts[server][quietee], channel);
|
||||||
|
this.api.voice(server, quietee, channel);
|
||||||
|
} else {
|
||||||
|
this.api.unquiet(server, this.hosts[server][quietee], channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbot.api.users.resolveUser(server, dbot.config.name, function (err, user) {
|
||||||
|
dbot.api.report.notify('unquiet', server, user, channel,
|
||||||
|
dbot.t('unquiet_notify', {
|
||||||
|
'unquieter': dbot.config.name,
|
||||||
|
'quietee': quietee
|
||||||
|
}), false, quietee);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
callback(dbot.t('tquieted', {
|
||||||
|
'quietee': quietee,
|
||||||
|
'minutes': duration
|
||||||
|
}));
|
||||||
|
dbot.api.report.notify('quiet', server, quieter.primaryNick, channel,
|
||||||
|
dbot.t('tquiet_notify', {
|
||||||
|
'minutes': duration,
|
||||||
|
'quieter': quieter.primaryNick,
|
||||||
|
'quietee': quietee,
|
||||||
|
'reason': reason
|
||||||
|
}), false, quietee);
|
||||||
|
} else {
|
||||||
|
callback(dbot.t('quieted', {
|
||||||
|
'quietee': quietee
|
||||||
|
}));
|
||||||
|
dbot.api.report.notify('quiet', server, quieter.primaryNick, channel,
|
||||||
|
dbot.t('quiet_notify', {
|
||||||
|
'quieter': quieter.primaryNick,
|
||||||
|
'quietee': quietee,
|
||||||
|
'reason': reason
|
||||||
|
}), false, quietee);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.devoice(server, quietee, channel);
|
||||||
|
|
||||||
|
if (_.include(this.config.quietBans, channel)) {
|
||||||
|
this.api.ban(server, this.hosts[server][quietee], channel);
|
||||||
|
} else {
|
||||||
|
this.api.quiet(server, host, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason.indexOf('#warn') !== -1) {
|
||||||
|
dbot.api.warning.warn(server, quieter, quietee,
|
||||||
|
'Quieted in ' + channel + ' for ' + reason, channel,
|
||||||
|
function () {});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('no_user', {
|
||||||
|
'user': quietee
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'networkUnban': function (server, unbanee, unbanner, manualHost, callback) {
|
||||||
|
var channels = dbot.config.servers[server].channels,
|
||||||
|
network = this.config.network_name[server] || server,
|
||||||
|
adminChannel = dbot.config.servers[server].admin_channel;
|
||||||
|
|
||||||
|
if (!_.isUndefined(manualHost)) {
|
||||||
|
this.hosts[server][unbanee] = manualHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.has(this.hosts, server) && _.has(this.hosts[server], unbanee) && _.isString(this.hosts[server][unbanee])) {
|
||||||
|
var host = this.hosts[server][unbanee];
|
||||||
|
|
||||||
|
// Notify Staff
|
||||||
|
if (_.isUndefined(adminChannel)) {
|
||||||
|
adminChannel = event.channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifyString = dbot.t('nunbanned', {
|
||||||
|
'network': network,
|
||||||
|
'unbanee': unbanee,
|
||||||
|
'host': host,
|
||||||
|
'unbanner': unbanner.currentNick
|
||||||
|
});
|
||||||
|
dbot.api.report.notify('unban', server, unbanner, adminChannel, notifyString, false, unbanee);
|
||||||
|
dbot.say(server, adminChannel, notifyString);
|
||||||
|
|
||||||
|
// Notify Unbanee
|
||||||
|
dbot.say(server, unbanee, dbot.t('nunban_notify', {
|
||||||
|
'network': network,
|
||||||
|
'unbanee': unbanee,
|
||||||
|
'unbanner': unbanner.currentNick
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Unban
|
||||||
|
var i = 0;
|
||||||
|
var unbanChannel = function (channels) {
|
||||||
|
if (i >= channels.length)
|
||||||
|
return;
|
||||||
|
var channel = channels[i];
|
||||||
|
this.api.unban(server, host, channel);
|
||||||
|
setTimeout(function () {
|
||||||
|
i++;
|
||||||
|
unbanChannel(channels);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
.bind(this);
|
||||||
|
unbanChannel(channels);
|
||||||
|
|
||||||
|
dbot.say(server, 'NickServ', 'FREEZE ' + unbanee + ' OFF');
|
||||||
|
callback(null); // Success
|
||||||
|
} else {
|
||||||
|
// Attempt to look up the host on-the-fly
|
||||||
|
dbot.api.nickserv.getUserHost(server, unbanee, unbanner, function (host) {
|
||||||
|
if (host) {
|
||||||
|
if (!_.has(this.hosts, server))
|
||||||
|
this.hosts[server] = {};
|
||||||
|
this.hosts[server][unbanee] = host;
|
||||||
|
this.api.networkUnban(server, unbanee, unbanner);
|
||||||
|
} else {
|
||||||
|
callback(true); // No host could be found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
'addTempBan': function (server, banee, timeout) {
|
||||||
|
dbot.api.users.resolveUser(server, dbot.config.name, function (err, bot) {
|
||||||
|
dbot.api.timers.addTimeout(timeout, function () {
|
||||||
|
this.api.networkUnban(server, banee, bot, undefined, function (err) {});
|
||||||
|
delete this.tempBans[server][banee];
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
}
|
||||||
|
.bind(this));
|
||||||
|
}
|
||||||
|
.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listener = function (event) {
|
||||||
|
if (event.kickee == dbot.config.name) {
|
||||||
|
dbot.instance.join(event, event.channel.name);
|
||||||
|
event.reply(dbot.t('kicked_dbot', {
|
||||||
|
'botname': dbot.config.name
|
||||||
|
}));
|
||||||
|
dbot.db.kicks[dbot.config.name] += 1;
|
||||||
|
} else {
|
||||||
|
if (!_.has(dbot.db.kicks, event.kickee)) {
|
||||||
|
dbot.db.kicks[event.kickee] = 1;
|
||||||
|
} else {
|
||||||
|
dbot.db.kicks[event.kickee] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.has(dbot.db.kickers, event.user)) {
|
||||||
|
dbot.db.kickers[event.user] = 1;
|
||||||
|
} else {
|
||||||
|
dbot.db.kickers[event.user] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.config.countSilently) {
|
||||||
|
event.reply(event.kickee + '-- (' + dbot.t('user_kicks', {
|
||||||
|
'user': event.kickee,
|
||||||
|
'kicks': dbot.db.kicks[event.kickee],
|
||||||
|
'kicked': dbot.db.kickers[event.kickee]
|
||||||
|
}) + ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this);
|
||||||
|
this.on = 'KICK';
|
||||||
|
|
||||||
|
this.onLoad = function () {
|
||||||
|
if (!_.has(dbot.db, 'hosts')) {
|
||||||
|
dbot.db.hosts = {};
|
||||||
|
_.each(dbot.config.servers, function (v, k) {
|
||||||
|
dbot.db.hosts[k] = {};
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
if (!_.has(dbot.db, 'tempBans'))
|
||||||
|
dbot.db.tempBans = {};
|
||||||
|
this.hosts = dbot.db.hosts;
|
||||||
|
this.tempBans = dbot.db.tempBans;
|
||||||
|
this.voteQuiets = {};
|
||||||
|
|
||||||
|
_.each(this.tempBans, function (bans, server) {
|
||||||
|
_.each(bans, function (timeout, nick) {
|
||||||
|
timeout = new Date(timeout);
|
||||||
|
this.internalAPI.addTempBan(server, nick, timeout);
|
||||||
|
}, this);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
if (_.has(dbot.modules, 'web')) {
|
||||||
|
dbot.api.web.addIndexLink('/bans', 'Ban List');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function (dbot) {
|
||||||
|
return new kick(dbot);
|
||||||
|
};
|
46
modules-stock/kick/pages.js
Normal file
46
modules-stock/kick/pages.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var pages = function (dbot) {
|
||||||
|
return {
|
||||||
|
'/bans': function (req, res) {
|
||||||
|
res.render('servers', {
|
||||||
|
'servers': _.keys(dbot.config.servers)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'/underbans': function (req, res) {
|
||||||
|
this.db.search('nbans', {
|
||||||
|
'server': server
|
||||||
|
}, function (ban) {
|
||||||
|
if (ban.reason.match('#underban')) {
|
||||||
|
bans.push(ban);
|
||||||
|
}
|
||||||
|
}, function () {
|
||||||
|
res.render('bans', {
|
||||||
|
'server': server,
|
||||||
|
'bans': bans
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'/bans/:server': function (req, res) {
|
||||||
|
var server = req.params.server,
|
||||||
|
bans = [];
|
||||||
|
|
||||||
|
this.db.search('nbans', {
|
||||||
|
'server': server
|
||||||
|
}, function (ban) {
|
||||||
|
bans.push(ban);
|
||||||
|
}, function () {
|
||||||
|
res.render('bans', {
|
||||||
|
'server': server,
|
||||||
|
'bans': bans
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function (dbot) {
|
||||||
|
return pages(dbot);
|
||||||
|
};
|
115
modules-stock/kick/strings.json
Normal file
115
modules-stock/kick/strings.json
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"user_kicks": {
|
||||||
|
"en": "{user} has been kicked {kicks} times and has kicked people {kicked} times.",
|
||||||
|
"es": "Se ha expulsado {user} {kicks} veces y {user} ha expulsado personas {kicked} veces.",
|
||||||
|
"na'vi": "Tuteol {user}it tsrame'i {kicks} hìmtxan ulte sute tsrame'i {kicked} hìmtxan.",
|
||||||
|
"cy": "Cafwyd {user} ei gicio {kicks} gwaith ac wedi cicio pobl {kicked} gwaith.",
|
||||||
|
"nl": "{user} is {kicks} keer gekickt en heeft anderen {kicked} keer gekickt.",
|
||||||
|
"de": "{user} wurde {kicks} mal gekickt und hat {kicked} mal andere Benutzer gekickt.",
|
||||||
|
"fr": "{user} a été kické {kicks} fois et a kické des personnes {kicked} fois.",
|
||||||
|
"it": "{user} ha ricevuto {kicks} pedata/e e ha dato {kicked} pedata/e a altri utenti"
|
||||||
|
},
|
||||||
|
"quieted": {
|
||||||
|
"en": "Quieted {quietee}. Remember: don't be a coconut.",
|
||||||
|
"fr": "{quietee} a été rendu silencieux. Rappelle-toi : ne sois pas têtu.",
|
||||||
|
"it": "{quietee} è stato silenziato. Ricordati: non essere testardo",
|
||||||
|
"de": "{quietee} stummgestellt. Denk dran: Sei kein Arschloch."
|
||||||
|
},
|
||||||
|
"tquieted": {
|
||||||
|
"en": "Quieted {quietee} for {minutes}. Remember: don't be a coconut.",
|
||||||
|
"fr": "{quietee} a été rendu silencieux pour {minutes} minutes. Rappelle-toi : ne sois pas têtu.",
|
||||||
|
"it": "{quietee} è stato silenziato per {minutes} minuto/i. Ricordati: non essere testardo",
|
||||||
|
"de": "{quietee} für {minutes} Minuten stummgestellt. Denk dran: Sei kein Arschloch."
|
||||||
|
},
|
||||||
|
"quiet_notify": {
|
||||||
|
"en": "{quieter} has quieted {quietee}. The reason given was \"{reason}\".",
|
||||||
|
"de": "{quieter} hat {quietee} stummgestellt. Der Grund ist \"{reason}\"."
|
||||||
|
},
|
||||||
|
"tquiet_notify": {
|
||||||
|
"en": "{quieter} has quieted {quietee} for {minutes} minutes. The reason given was \"{reason}\".",
|
||||||
|
"de": "{quieter} hat {quietee} für {minutes} Minuten stummgestellt. Der Grund ist \"{reason}\"."
|
||||||
|
},
|
||||||
|
"unquieted": {
|
||||||
|
"en": "Unquieted {quietee}. Remember: don't be a coconut.",
|
||||||
|
"fr": "{quietee} peut maintenant parler. Rappelle-toi : ne sois pas têtu.",
|
||||||
|
"it": "{quietee} può nuovamente parlare. Ricordati: non essere testardo.",
|
||||||
|
"de": "{quietee} ist nicht mehr stummgestellt. Denk dran: Sei kein Arschloch."
|
||||||
|
},
|
||||||
|
"unquiet_notify": {
|
||||||
|
"en": "{unquieter} has unquieted {quietee}.",
|
||||||
|
"de": "{unquieter} hat Stummstellung von {quietee} aufgehoben."
|
||||||
|
},
|
||||||
|
"kicked_dbot": {
|
||||||
|
"en": "Thou shalt not kick {botname}",
|
||||||
|
"es": "No expulsás {botname}",
|
||||||
|
"na'vi": "Ngal {botname}it ke tsun tsrive'i",
|
||||||
|
"cy": "Ni ddylech cicio {botname}",
|
||||||
|
"nl": "Gij zult {botname} niet kicken",
|
||||||
|
"de": "Du sollst {botname} nicht kicken",
|
||||||
|
"fr": "Tu ne kickeras pas {botname}",
|
||||||
|
"it": "Non dare pedata a {botname}"
|
||||||
|
},
|
||||||
|
"ckicked": {
|
||||||
|
"en": "{kicker} has kicked {kickee}. The reason given was: \"{reason}\".",
|
||||||
|
"cy": "Sylw: {kicker} wedi cicio'r {kickee} o {channel}. Y rheswm a roddwyd oedd: \"{reason}\".",
|
||||||
|
"de": "Achtung: {kicker} hat {kickee} von {channel} verwiesen. Grund: \"{reason}\".",
|
||||||
|
"fr": "Attention : {kicker} a kické {kickee} de {channel}. Raison donnée : \"{reason}\".",
|
||||||
|
"it": "Attenzione : {kicker} ha dato una pedata a {kickee} di {channel}. Motivo : \"{reason}\"."
|
||||||
|
},
|
||||||
|
"cbanned": {
|
||||||
|
"en": "Attention: {banner} has banned {banee} from {channel}. The reason given was \"{reason}\".",
|
||||||
|
"cy": "Sylw: {banner} wedi gwahardd {banee} o {channel}. Y rheswm a roddwyd oedd: \"{reason}\".",
|
||||||
|
"de": "Achtung: {banner} hat {banee} von {channel} gebannt. Grund: \"{reason}\".",
|
||||||
|
"fr": "Attention : {banner} a banni {banee} de {channel}. Raison donnée : \"{reason}\".",
|
||||||
|
"it": "Attenzione : {banner} ha bandito {banee} da {channel}. Motivo : \"{reason}\"."
|
||||||
|
},
|
||||||
|
"tbanned": {
|
||||||
|
"en": "Attention: {banner} has banned {banee} (host: {host}) from the {network} network for {hours}. The reason given was \"{reason}\".",
|
||||||
|
"de": "Achtung: {banner} hat {banee} vom {network} Netzwerk für {hours} Stunden verbannt. Der Grund war \"{reason}\".",
|
||||||
|
"fr": "Attention : {banner} a banni {banee} du réseau {network} pour {hours} heures. Raison donnée : \"{reason}\".",
|
||||||
|
"it": "Attenzione : {banner} ha bandito {banee} dalla rete {network} per {hours} ora/e. Motivo : \"{reason}\"."
|
||||||
|
},
|
||||||
|
"tbanned_notify": {
|
||||||
|
"en": "You have been banned from the {network} network for {hours} by {banner}. The reason given was \"{reason}\". You can join {admin_channel} for more information or to discuss the ban.",
|
||||||
|
"de": "Du wurdest von {banner} im {network} Netzwerk für {hours} verbannt. Der Grund war \"{reason}\". Du kannst {admin_channel} beitreten um mehr Informatonen zu erhalten oder über die Verbannung zu diskutieren.",
|
||||||
|
"fr": "Vous avez été banni du réseau {network} pour {hours} heures par {banner}. La raison donnée était \"{reason}\". Vous pouvez rejoindre {admin_channel} pour plus d'information or pour discuter du ban.",
|
||||||
|
"it": "Sei stato bandito dalla rete {network} per {hours} ora/e da {banner}. Motivo: \"{reason}\". Puoi ricongiungere {admin_channel} per ulteriori informazioni o discutere sulla messa al bando."
|
||||||
|
},
|
||||||
|
"nbanned": {
|
||||||
|
"en": "Attention: {banner} has banned {banee} (host: {host}) from the {network} network. The reason given was \"{reason}\".",
|
||||||
|
"cy": "Sylw: {banner} wedi gwahardd {banee} ledled y rhwydwaith. Y rheswm a roddwyd oedd: \"{reason}\".",
|
||||||
|
"de": "Achtung: {banner} hat {banee} auf dem gesamten Netzwerk gebannt. Grund: \"{reason}\".",
|
||||||
|
"fr": "Attention : {banner} a banni {banee} du réseau {network}. Raison donnée : \"{reason}\".",
|
||||||
|
"it": "Attentione : {banner} ha bandito {banee} dalla rete {network}. Motivo : \"{reason}\"."
|
||||||
|
},
|
||||||
|
"nbanned_notify": {
|
||||||
|
"en": "You have been banned from the {network} network by {banner}. The reason given was \"{reason}\". You can join {admin_channel} for more information or to discuss the ban.",
|
||||||
|
"de": "Du wurdest von {banner} im {network} Netzwerk verbannt. Der Grund war \"{reason}\". Du kannst {admin_channel} beitreten um mehr Informatonen zu erhalten oder über die Verbannung zu diskutieren.",
|
||||||
|
"fr": "Vous avez été banni du réseau {network} par {banner}. La raison donnée était \"{reason}\". Vous pouvez rejoindre {admin_channel} pour plus d'information or pour discuter du ban.",
|
||||||
|
"it": "Sei stato bandito dalla rete {network} da {banner}. Motivo: \"{reason}\". Puoi ricongiungere {admin_channel} per ulteriori informazioni o discutere sulla messa al bando."
|
||||||
|
},
|
||||||
|
"no_user": {
|
||||||
|
"en": "{user} doesn't seem to be online on this server.",
|
||||||
|
"de": "{user} scheint auf diesen Server nicht online zu sein.",
|
||||||
|
"fr": "{user} ne semble pas être connecté à ce serveur.",
|
||||||
|
"it": "{user} sembra non essere connesso a questo server"
|
||||||
|
},
|
||||||
|
"nunbanned": {
|
||||||
|
"en": "Attention: {unbanee} (host: {host}) has been unbanned from the {network} network by {unbanner}.",
|
||||||
|
"de": "Achtung: {unbanee} wurde im {network} Netzwerk durch {unbanner} entsperrt.",
|
||||||
|
"fr": "Attention : {unbanee} a été débanni du réseau {network} par {unbanner}.",
|
||||||
|
"it": "Attenzione : {unbanee} è stato riammesso alla rete {network} da {unbanner}."
|
||||||
|
},
|
||||||
|
"nunban_notify": {
|
||||||
|
"en": "You have been unbanned from the {network} network by {unbanner}.",
|
||||||
|
"de": "Du wurdest im {network} Netzwerk durch {unbanner} entsperrt.",
|
||||||
|
"fr": "Vous avez été débanni du réseau {network} par {unbanner}.",
|
||||||
|
"it": "Sei stato riammesso alla rete {network} da {unbanner}."
|
||||||
|
},
|
||||||
|
"nunban_error": {
|
||||||
|
"en": "It appears {unbanee} was not banned using the ~nban command.",
|
||||||
|
"de": "Es sieht so aus als ob {unbanee} nicht durch die Verwendung des ~nban Befehls verbannt wurde.",
|
||||||
|
"fr": "Il semble que {unbanee} n'a pas été banni en utilisant la commande ~nban.",
|
||||||
|
"it": "Sembar che {unbanee} non è stato bandito usano il commando ~nban."
|
||||||
|
}
|
||||||
|
}
|
9
modules-stock/kick/usage.json
Normal file
9
modules-stock/kick/usage.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"~ckick": "~ckick [#channel] [username] [reason]",
|
||||||
|
"~cban": "~cban [#channel] [username] [reason]",
|
||||||
|
"~nban": "~nban ([time in hours]) [username] [reason]",
|
||||||
|
"~kickstats": "~kickstats",
|
||||||
|
"~kickcount": "~kickcount [user]",
|
||||||
|
"~quiet": "~quiet (time) (#channel) username (reason)",
|
||||||
|
"~unquiet": "~unquiet (#channel) username"
|
||||||
|
}
|
11
modules-stock/kill_namespam/config.json
Normal file
11
modules-stock/kill_namespam/config.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"action": "kill",
|
||||||
|
"sensitivity": 10,
|
||||||
|
"exempt": [],
|
||||||
|
"advert_content": [
|
||||||
|
"________ ______"
|
||||||
|
],
|
||||||
|
"cliconn_channel": "#dnsbl",
|
||||||
|
"cliconn_patterns": [],
|
||||||
|
"exempt_channels": []
|
||||||
|
}
|
159
modules-stock/kill_namespam/kill_namespam.js
Normal file
159
modules-stock/kill_namespam/kill_namespam.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* Module name: kill_namespam
|
||||||
|
* Description: destroy those wot hilight too many nicks at once . usually
|
||||||
|
* advertising their rubbish irc server (do not)
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._;
|
||||||
|
|
||||||
|
var kill_namespam = function(dbot) {
|
||||||
|
this.saveConfig = function() { // eugh
|
||||||
|
dbot.customConfig.modules.kill_namespam = this.config;
|
||||||
|
dbot.modules.admin.internalAPI.saveConfig();
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.matchedKill = {};
|
||||||
|
|
||||||
|
this.listener = function(event) {
|
||||||
|
if(event.action == 'PRIVMSG') {
|
||||||
|
// Here we listen for atropos
|
||||||
|
if(event.channel == this.config.cliconn_channel) {
|
||||||
|
if(event.message.match('▶')) {
|
||||||
|
var matchedPattern = _.find(this.config.cliconn_patterns,
|
||||||
|
function(p) { try { return event.message.match(p); } catch(e) {}; }); // ok.jpg
|
||||||
|
if(matchedPattern) {
|
||||||
|
var nick = event.message.split(' ')[2];
|
||||||
|
dbot.api.nickserv.getUserHost(event.server, nick, function(host) {
|
||||||
|
var userIsAuthenticated = host && host.startsWith('tripsit/');
|
||||||
|
if (userIsAuthenticated) {
|
||||||
|
event.reply(dbot.t('clikill_spared', {
|
||||||
|
'user': nick,
|
||||||
|
'pattern': matchedPattern
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(!this.matchedKill[host]) {
|
||||||
|
// Defer killing this connection until after they join a non-exempted channel
|
||||||
|
this.matchedKill[host] = {
|
||||||
|
ip: event.message.split(' ')[1],
|
||||||
|
server: event.server,
|
||||||
|
matchedPattern: matchedPattern,
|
||||||
|
rUser: event.rUser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the namespam
|
||||||
|
if(event.channel == event.user) return; // return if pm
|
||||||
|
if(_.includes(this.config.exempt, event.user)) return;
|
||||||
|
|
||||||
|
var message;
|
||||||
|
var naughty = false;
|
||||||
|
|
||||||
|
// Check distinctive spam content match
|
||||||
|
if(_.any(this.config.advert_content, function(spam) { return event.message.indexOf(spam) != -1; })) {
|
||||||
|
message = dbot.t('spamcont_act', {
|
||||||
|
'user': event.user,
|
||||||
|
'channel': event.channel,
|
||||||
|
'action': this.config.action
|
||||||
|
}) + ' ' + dbot.t('sasl_hint');
|
||||||
|
naughty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name highlight spam
|
||||||
|
if(_.filter(event.message.split(' '), function(word) { return _.has(event.channel.nicks, word); }).length > this.config.sensitivity) {
|
||||||
|
message = dbot.t('namespam_act', {
|
||||||
|
'user': event.user,
|
||||||
|
'channel': event.channel,
|
||||||
|
'action': this.config.action,
|
||||||
|
'sensitivity': this.config.sensitivity
|
||||||
|
}) + ' ' + dbot.t('sasl_hint');
|
||||||
|
naughty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(naughty) {
|
||||||
|
switch(this.config.action) {
|
||||||
|
case 'kickban':
|
||||||
|
dbot.api.kick.ban(event.server, event.host, event.channel);
|
||||||
|
dbot.api.kick.kick(event.server, event.user, message);
|
||||||
|
break;
|
||||||
|
case 'kill':
|
||||||
|
dbot.api.kick.kill(event.server, event.user, message);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dbot.api.report.notify('spam', event.server, event.user, event.channel, message, event.host, event.user);
|
||||||
|
}
|
||||||
|
} else if (event.action == 'JOIN') {
|
||||||
|
|
||||||
|
if(this.matchedKill[event.host]) {
|
||||||
|
if(this.config.exempt_channels.indexOf(event.channel) == -1) {
|
||||||
|
var kill = this.matchedKill[event.host];
|
||||||
|
delete this.matchedKill[event.host];
|
||||||
|
|
||||||
|
// Alternatively you can just do dbot.api.kick.kill(event.server, event.user, message);
|
||||||
|
dbot.say(event.server, 'operserv', 'akill add *@'+ kill.ip +' !P Naughty Nelly Auto-kill v6.2. Matched pattern: /'+ kill.matchedPattern +'/');
|
||||||
|
|
||||||
|
var msg = dbot.t('clikill_act', {
|
||||||
|
'ip': kill.ip,
|
||||||
|
'pattern': kill.matchedPattern
|
||||||
|
});
|
||||||
|
dbot.api.report.notify('autokill', kill.server, kill.rUser,
|
||||||
|
dbot.config.servers[kill.server].admin_channel, msg, kill.ip, kill.ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.action == 'QUIT') {
|
||||||
|
if(this.matchedKill[event.host]) {
|
||||||
|
delete this.matchedKill[event.host];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
this.on = ['PRIVMSG', 'JOIN', 'QUIT'];
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~add_spamkill': function(event) {
|
||||||
|
this.config.advert_content.push(event.params.slice(1).join(' '))
|
||||||
|
this.saveConfig();
|
||||||
|
event.reply('Users daring to utter the above to be classified as spam.');
|
||||||
|
},
|
||||||
|
|
||||||
|
'~del_spamkill': function(event) {
|
||||||
|
this.config.advert_content = _.without(this.config.advert_content, event.params.slice(1).join(' '));
|
||||||
|
this.saveConfig();
|
||||||
|
event.reply('Users will no longer be killed for this utterance.');
|
||||||
|
},
|
||||||
|
|
||||||
|
'~add_clikill': function(event) {
|
||||||
|
var pattern = event.params.slice(1).join(' ');
|
||||||
|
this.config.cliconn_patterns.push(pattern);
|
||||||
|
this.saveConfig();
|
||||||
|
event.reply('Client connection notices matching pattern /'+ pattern +'/ shall henceforth get rekt.');
|
||||||
|
},
|
||||||
|
|
||||||
|
'~del_clikill': function(event) {
|
||||||
|
var pattern = event.params.slice(1).join(' ');
|
||||||
|
this.config.cliconn_patterns = _.without(this.config.cliconn_patterns, pattern);
|
||||||
|
this.saveConfig();
|
||||||
|
event.reply('Client connection notices matching pattern /'+ pattern +'/ will no longer get rekt.');
|
||||||
|
},
|
||||||
|
|
||||||
|
'~list_clikill': function(event) {
|
||||||
|
event.reply('Currently active "cliconn" kills (to use add/del, provide the pattern, not the index, and do not include the surrounding //');
|
||||||
|
_.each(this.config.cliconn_patterns, function(pattern, i) {
|
||||||
|
event.reply('['+i+'] /' + pattern + '/');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_.each(this.commands, function(c) {
|
||||||
|
c.access = 'moderator';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new kill_namespam(dbot);
|
||||||
|
};
|
17
modules-stock/kill_namespam/strings.json
Normal file
17
modules-stock/kill_namespam/strings.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"namespam_act": {
|
||||||
|
"en": "{user} triggered nickname hilight anti-spam (greater than {sensitivity}). Action: {action}."
|
||||||
|
},
|
||||||
|
"spamcont_act": {
|
||||||
|
"en": "{user} triggered advertising anti-spam (content). Action: {action}"
|
||||||
|
},
|
||||||
|
"clikill_act": {
|
||||||
|
"en": "Added K-Line for {ip}, due to matching pattern: /{pattern}/"
|
||||||
|
},
|
||||||
|
"clikill_spared": {
|
||||||
|
"en": "{user} spared from clikill matched pattern: /{pattern}/"
|
||||||
|
},
|
||||||
|
"sasl_hint": {
|
||||||
|
"en": "You have been killed by our spam filter. You can avoid broad VPN filters by using SASL."
|
||||||
|
}
|
||||||
|
}
|
66
modules-stock/lastfm/README.md
Normal file
66
modules-stock/lastfm/README.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
## LastFM
|
||||||
|
|
||||||
|
Adds various LastFM functionalities.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This module provides a command which allows users to show stats of LastFM and such stuff.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
It has following dependencies:
|
||||||
|
+ [request](https://github.com/mikeal/request)
|
||||||
|
+ [async](https://github.com/caolan/async)
|
||||||
|
+ [moment](https://github.com/moment/moment)
|
||||||
|
|
||||||
|
### config.json
|
||||||
|
|
||||||
|
api_key and output prefix can be set.
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"dependencies": [ "profile" ],
|
||||||
|
"api_key": "blah",
|
||||||
|
"outputPrefix": "\u000315,5last.fm\u000f"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
|
||||||
|
#### ~lastfm [user]
|
||||||
|
Display all scrobbles of a user.
|
||||||
|
Example:
|
||||||
|
+ ~lastfm reality
|
||||||
|
|
||||||
|
#### ~scrobbliest
|
||||||
|
Displays the users with the most scrobbles.
|
||||||
|
Example:
|
||||||
|
+ ~scrobbliest
|
||||||
|
|
||||||
|
#### ~suggestion
|
||||||
|
Displays a suggestion based on the listened scrobbles.
|
||||||
|
Example:
|
||||||
|
+ ~suggestion
|
||||||
|
|
||||||
|
#### ~listening
|
||||||
|
Displays the currently/last played song of the posting user.
|
||||||
|
Example:
|
||||||
|
+ ~listening
|
||||||
|
|
||||||
|
#### ~taste [user]
|
||||||
|
Compares two users (the posting user and the defined user).
|
||||||
|
Example:
|
||||||
|
+ ~taste reality
|
||||||
|
|
||||||
|
#### ~tastiest
|
||||||
|
Displays the users that matches the most in music taste.
|
||||||
|
Example:
|
||||||
|
+ ~tastiest
|
||||||
|
|
||||||
|
#### ~artists [user]
|
||||||
|
Compares two users (the posting user and the defined user) and displays their matching artists.
|
||||||
|
Example:
|
||||||
|
+ ~artists reality
|
||||||
|
|
||||||
|
### TODO
|
5
modules-stock/lastfm/config.json
Normal file
5
modules-stock/lastfm/config.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": [ "profile", "youtube", "spotify" ],
|
||||||
|
"api_key": "blah",
|
||||||
|
"outputPrefix": "\u000315,5last.fm\u000f"
|
||||||
|
}
|
526
modules-stock/lastfm/lastfm.js
Normal file
526
modules-stock/lastfm/lastfm.js
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
/**
|
||||||
|
* Module Name: Last.FM
|
||||||
|
* Description: Various lastfm functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
request = require('request'),
|
||||||
|
async = require('async'),
|
||||||
|
moment = require('moment');
|
||||||
|
|
||||||
|
var lastfm = function(dbot) {
|
||||||
|
this.ApiRoot = 'http://ws.audioscrobbler.com/2.0/';
|
||||||
|
|
||||||
|
this.internalAPI = {
|
||||||
|
'getLastFM': function(server, nick, callback) {
|
||||||
|
dbot.api.profile.getProfile(server, nick, function(err, user, profile) {
|
||||||
|
if(user) {
|
||||||
|
if(profile && _.has(profile.profile, 'lastfm') && _.isString(profile.profile.lastfm)) {
|
||||||
|
callback(user, profile.profile.lastfm.toLowerCase());
|
||||||
|
} else {
|
||||||
|
callback(user, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(null, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api = {
|
||||||
|
'getRandomArtistTrack': function(mbid, callback) {
|
||||||
|
request.get(this.ApiRoot, {
|
||||||
|
'qs': {
|
||||||
|
'method': 'artist.gettoptracks',
|
||||||
|
'mbid': mbid,
|
||||||
|
'api_key': this.config.api_key,
|
||||||
|
'format': 'json',
|
||||||
|
'limit': 10
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if(_.has(body, 'toptracks') && _.has(body.toptracks, 'track')) {
|
||||||
|
var tracks = body.toptracks.track;
|
||||||
|
choice = _.random(0, tracks.length - 1),
|
||||||
|
track = tracks[choice];
|
||||||
|
callback(null, track);
|
||||||
|
} else {
|
||||||
|
callback('idk', body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'getSimilarArtists': function(mbid, callback) {
|
||||||
|
request.get(this.ApiRoot, {
|
||||||
|
'qs': {
|
||||||
|
'method': 'artist.getsimilar',
|
||||||
|
'mbid': mbid,
|
||||||
|
'api_key': this.config.api_key,
|
||||||
|
'format': 'json',
|
||||||
|
'limit': 10
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if(_.has(body, 'similarartists') && _.has(body.similarartists, 'artist')) {
|
||||||
|
callback(null, body.similarartists.artist);
|
||||||
|
} else {
|
||||||
|
callback('idk', body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'getListening': function(username, callback) {
|
||||||
|
request.get(this.ApiRoot, {
|
||||||
|
'qs': {
|
||||||
|
'user': username,
|
||||||
|
'limit': 2,
|
||||||
|
'nowplaying': true,
|
||||||
|
'method': 'user.getrecenttracks',
|
||||||
|
'api_key': this.config.api_key,
|
||||||
|
'format': 'json'
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if(_.has(body, 'error') && body.error == 6) {
|
||||||
|
callback('no_user', null);
|
||||||
|
} else if(_.has(body, 'recenttracks') && _.has(body.recenttracks, 'track')
|
||||||
|
&& !_.isUndefined(body.recenttracks.track[0])) {
|
||||||
|
callback(null, body.recenttracks.track[0]);
|
||||||
|
} else {
|
||||||
|
callback('no_listen', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
'tasteCompare': function(user, oUser, callback) {
|
||||||
|
request.get(this.ApiRoot, {
|
||||||
|
'qs': {
|
||||||
|
'type1': 'user',
|
||||||
|
'type2': 'user',
|
||||||
|
'value1': user,
|
||||||
|
'value2': oUser,
|
||||||
|
'method': 'tasteometer.compare',
|
||||||
|
'api_key': this.config.api_key,
|
||||||
|
'format': 'json'
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
console.log(body);
|
||||||
|
if(_.has(body, 'error') && body.error == 6 || body.error == 7) {
|
||||||
|
callback('no_user', user, null);
|
||||||
|
} else if(_.has(body, 'comparison') && _.has(body.comparison, 'result')) {
|
||||||
|
callback(null, body.comparison.result);
|
||||||
|
} else {
|
||||||
|
callback('idk', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
'getInfo': function(lfm, callback) {
|
||||||
|
request.get(this.ApiRoot, {
|
||||||
|
'qs': {
|
||||||
|
'user': lfm,
|
||||||
|
'method': 'user.getinfo',
|
||||||
|
'api_key': this.config.api_key,
|
||||||
|
'format': 'json'
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if(_.has(body, 'error') && body.error == 6 || body.error == 7) {
|
||||||
|
callback('no_user', null);
|
||||||
|
} else if(_.has(body, 'user')) {
|
||||||
|
callback(null, body.user);
|
||||||
|
} else {
|
||||||
|
callback('idk', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~lastfm': function(event) {
|
||||||
|
var user = event.rUser,
|
||||||
|
lfm = event.rProfile.lastfm;
|
||||||
|
if(event.res[0]) {
|
||||||
|
user = event.res[0].user;
|
||||||
|
lfm = event.res[0].lfm;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.getInfo(lfm, function(err, profile) {
|
||||||
|
if(!err) {
|
||||||
|
console.log(profile);
|
||||||
|
event.reply(dbot.t('lfm_profile', {
|
||||||
|
'user': user.currentNick,
|
||||||
|
'plays': profile.playcount.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"),
|
||||||
|
'date': moment(profile.registered['#text'] * 1000).format('DD/MM/YYYY'),
|
||||||
|
'link': profile.url
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(err == 'no_user') {
|
||||||
|
event.reply(dbot.t('lfm_unknown'));
|
||||||
|
} else if(err == 'no_listen') {
|
||||||
|
event.reply(dbot.t('no_listen', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~scrobbliest': function(event) {
|
||||||
|
dbot.api.profile.getAllProfilesWith('lastfm', function(profiles) {
|
||||||
|
if(profiles) {
|
||||||
|
var plays = [];
|
||||||
|
async.each(profiles, function(profile, done) {
|
||||||
|
this.api.getInfo(profile.profile.lastfm, function(err, lProfile) {
|
||||||
|
if(!err) {
|
||||||
|
plays.push({
|
||||||
|
'user': profile.id,
|
||||||
|
'plays': parseInt(lProfile.playcount)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}.bind(this), function() {
|
||||||
|
var scrobbliest = _.chain(plays)
|
||||||
|
.sortBy(function(p) { return p.plays; })
|
||||||
|
.reverse()
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
async.each(scrobbliest, function(item, done) {
|
||||||
|
dbot.api.users.getUser(item.user, function(err, user) {
|
||||||
|
item.user = user;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
var output = dbot.t('lfm_scrobbliest');
|
||||||
|
_.each(scrobbliest, function(item) {
|
||||||
|
output += item.user.currentNick + ' (' +
|
||||||
|
item.plays.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")+ '), ';
|
||||||
|
});
|
||||||
|
event.reply(output.slice(0, -2));
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply('no suitable profiles');
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~suggestion': function(event) {
|
||||||
|
this.api.getListening(event.rProfile.lastfm, function(err, track) {
|
||||||
|
if(!err) {
|
||||||
|
this.api.getSimilarArtists(track.artist.mbid, function(err, similar) {
|
||||||
|
if(!err) {
|
||||||
|
var choice = _.random(0, similar.length - 1);
|
||||||
|
this.api.getRandomArtistTrack(similar[choice].mbid, function(err, track) {
|
||||||
|
if(!err) {
|
||||||
|
var output = dbot.t('lfm_suggestion', {
|
||||||
|
'user': event.user,
|
||||||
|
'name': track.name,
|
||||||
|
'artist': track.artist.name
|
||||||
|
});
|
||||||
|
var term = track.name + ' ' + track.artist.name;
|
||||||
|
|
||||||
|
async.parallel({
|
||||||
|
youtube: function(cb) {
|
||||||
|
dbot.api.youtube.search(term, function(body) {
|
||||||
|
if(_.isObject(body) && _.has(body, 'items') && body.items.length > 0) {
|
||||||
|
var link = body.items[0].id.videoId
|
||||||
|
if(link) {
|
||||||
|
cb(null,"https://youtu.be/" + link);
|
||||||
|
} else {
|
||||||
|
cb(null, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
spotify: function(cb) {
|
||||||
|
dbot.api.spotify.spotifySearch(term, function(body, url, uri) {
|
||||||
|
if(body) {
|
||||||
|
if (!dbot.modules.minify) {
|
||||||
|
cb(null, { url: url, uri:uri });
|
||||||
|
} else {
|
||||||
|
dbot.modules.minify.api.minify(url, "bitly", function(mini) {
|
||||||
|
cb(null, { url:mini || url, uri:uri });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(null, undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(err, results) {
|
||||||
|
if (results.youtube || results.spotify) output += " - "
|
||||||
|
|
||||||
|
if (results.youtube) output += results.youtube;
|
||||||
|
if (results.spotify) {
|
||||||
|
if (results.youtube) output += " | ";
|
||||||
|
output += results.spotify.url + " - " + results.spotify.uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(output);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply('Couldn\'t get any suggested tracks.');
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event.reply('Couldn\'t find any similar artists to what you\'re listening to.');
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
if(err == 'no_user') {
|
||||||
|
event.reply(dbot.t('lfm_unknown'));
|
||||||
|
} else if(err == 'no_listen') {
|
||||||
|
event.reply(dbot.t('no_listen', { 'user': event.user }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
'~listening': function(event) {
|
||||||
|
var user = event.rUser,
|
||||||
|
lfm = event.rProfile.lastfm;
|
||||||
|
if(event.res[0]) {
|
||||||
|
user = event.res[0].user;
|
||||||
|
lfm = event.res[0].lfm;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.getListening(lfm, function(err, track) {
|
||||||
|
if(!err) {
|
||||||
|
var term = track.name + ' ' + track.artist['#text'],
|
||||||
|
output = '';
|
||||||
|
if(_.has(track, '@attr') && _.has(track['@attr'], 'nowplaying') && track['@attr'].nowplaying == 'true') {
|
||||||
|
output = dbot.t('now_listening', {
|
||||||
|
'user': user.currentNick,
|
||||||
|
'track': track.name,
|
||||||
|
'artist': track.artist['#text']
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
output = dbot.t('last_listened', {
|
||||||
|
'user': user.currentNick,
|
||||||
|
'track': track.name,
|
||||||
|
'artist': track.artist['#text']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async.parallel({
|
||||||
|
youtube: function(cb) {
|
||||||
|
dbot.api.youtube.search(term, function(body) {
|
||||||
|
if(_.isObject(body) && _.has(body, 'items') && body.items.length > 0) {
|
||||||
|
var link = body.items[0].id.videoId
|
||||||
|
if(link) {
|
||||||
|
cb(null,"https://youtu.be/" + link);
|
||||||
|
} else {
|
||||||
|
cb(null, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
spotify: function(cb) {
|
||||||
|
dbot.api.spotify.spotifySearch(term, function(body, url, uri) {
|
||||||
|
if(body) {
|
||||||
|
if (!dbot.modules.minify) {
|
||||||
|
cb(null, { url: url, uri:uri });
|
||||||
|
} else {
|
||||||
|
dbot.modules.minify.api.minify(url, "bitly", function(mini) {
|
||||||
|
cb(null, { url:mini || url, uri:uri });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(null, undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(err, results) {
|
||||||
|
if (results.youtube || results.spotify) output += " - "
|
||||||
|
|
||||||
|
if (results.youtube) output += results.youtube;
|
||||||
|
if (results.spotify) {
|
||||||
|
if (results.youtube) output += " | ";
|
||||||
|
output += results.spotify.url + " - " + results.spotify.uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reply(output);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(err == 'no_user') {
|
||||||
|
event.reply(dbot.t('lfm_unknown'));
|
||||||
|
} else if(err == 'no_listen') {
|
||||||
|
event.reply(dbot.t('no_listen', { 'user': user.currentNick }));
|
||||||
|
}
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
'~taste': function(event) {
|
||||||
|
var u1 = event.rUser,
|
||||||
|
lfm1 = event.rProfile.lastfm,
|
||||||
|
u2 = event.res[0].user,
|
||||||
|
lfm2 = event.res[0].lfm;
|
||||||
|
|
||||||
|
this.api.tasteCompare(event.rProfile.lastfm, lfm2, function(err, comp) {
|
||||||
|
if(!err) {
|
||||||
|
var score = Math.floor(comp.score * 100);
|
||||||
|
event.reply(dbot.t('taste_compat', {
|
||||||
|
'user1': event.user,
|
||||||
|
'user2': u2.currentNick,
|
||||||
|
'score': score
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(err == 'no_user') {
|
||||||
|
event.reply('Unknown Last.FM user.');
|
||||||
|
} else {
|
||||||
|
event.reply('Well something went wrong and I don\'t know what it means');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'~tastiest': function(event) {
|
||||||
|
var sortGoodScores = function(goodScores) {
|
||||||
|
var tastiest = _.chain(goodScores)
|
||||||
|
.sortBy(function(p) { return p.score; })
|
||||||
|
.reverse()
|
||||||
|
.first(10)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
async.each(tastiest, function(pair, done) {
|
||||||
|
if(!_.isObject(pair.p1)) { // fix this
|
||||||
|
dbot.api.users.getUser(pair.p1, function(err, user) {
|
||||||
|
pair.p1 = user;
|
||||||
|
dbot.api.users.getUser(pair.p2, function(err, user) {
|
||||||
|
pair.p2 = user;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, function() {
|
||||||
|
var output = 'Most musically compatible users: ';
|
||||||
|
_.each(tastiest, function(pair) {
|
||||||
|
output += pair.p1.currentNick + ' & ' +
|
||||||
|
pair.p2.currentNick + ' (' + pair.score +
|
||||||
|
'%), ';
|
||||||
|
});
|
||||||
|
event.reply(output.slice(0, -2));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if(this.tastyCache && Date.now() - this.tastyCacheStamp <= 1800000) {
|
||||||
|
sortGoodScores(this.tastyCache);
|
||||||
|
} else {
|
||||||
|
event.reply('Updating tasty cache... Hold onto your coconuts...');
|
||||||
|
dbot.api.profile.getAllProfilesWith('lastfm', function(profiles) {
|
||||||
|
if(profiles) {
|
||||||
|
var scores = {}; // Using this structure first for easier testing in the async
|
||||||
|
async.eachSeries(profiles, function(p1, next) {
|
||||||
|
scores[p1.id] = {};
|
||||||
|
async.eachSeries(profiles, function(p2, subnext) {
|
||||||
|
if(p1.id == p2.id || p1.profile.lastfm == p2.profile.lastfm || _.has(scores, p2.id) && _.has(scores[p2.id], p1.id)) {
|
||||||
|
subnext();
|
||||||
|
} else {
|
||||||
|
this.api.tasteCompare(p1.profile.lastfm, p2.profile.lastfm, function(err, comp) {
|
||||||
|
if(!err) {
|
||||||
|
var score = Math.floor(comp.score * 100);
|
||||||
|
scores[p1.id][p2.id] = score;
|
||||||
|
}
|
||||||
|
subnext();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this), function() { next(); });
|
||||||
|
}.bind(this), function(err) {
|
||||||
|
// Now we better structure the scores for sorting
|
||||||
|
var goodScores = [];
|
||||||
|
_.each(scores, function(subscores, p1) {
|
||||||
|
_.each(subscores, function(aScore, p2) {
|
||||||
|
goodScores.push({
|
||||||
|
'p1': p1,
|
||||||
|
'p2': p2,
|
||||||
|
'score': aScore
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tastyCache = goodScores;
|
||||||
|
this.tastyCacheStamp = new Date().getTime();
|
||||||
|
sortGoodScores(goodScores);
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
event.reply('No suitable profiles');
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
'~artists': function(event) {
|
||||||
|
var u1 = event.rUser,
|
||||||
|
lfm1 = event.rProfile.lastfm,
|
||||||
|
u2 = event.res[0].user,
|
||||||
|
lfm2 = event.res[0].lfm;
|
||||||
|
|
||||||
|
this.api.tasteCompare(event.rProfile.lastfm, lfm2, function(err, comp) {
|
||||||
|
if(!err) {
|
||||||
|
var artists = _.pluck(comp.artists.artist, 'name').join(', ');
|
||||||
|
event.reply(dbot.t('common_artists', {
|
||||||
|
'user1': event.user,
|
||||||
|
'user2': u2.currentNick,
|
||||||
|
'common': artists
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if(err == 'no_user') {
|
||||||
|
event.reply(dbot.t('lfm_unknown'));
|
||||||
|
} else {
|
||||||
|
event.reply('Well something went wrong and I don\'t know what it means');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//this.commands['~taste'].regex = [/^taste ([\d\w[\]{}^|\\`_-]+?)/, 2];
|
||||||
|
this.commands['~artists'].regex = [/^artists ([\d\w[\]{}^|\\`_-]+?)/, 2];
|
||||||
|
|
||||||
|
_.each(this.commands, function(command) {
|
||||||
|
command.resolver = function(event, callback) {
|
||||||
|
if(event.rProfile && _.has(event.rProfile, 'lastfm')) {
|
||||||
|
if(event.params[1]) {
|
||||||
|
this.internalAPI.getLastFM(event.server, event.params[1], function(user, lfm) {
|
||||||
|
if(user && lfm) {
|
||||||
|
event.res.push({
|
||||||
|
'user': user,
|
||||||
|
'lfm': lfm
|
||||||
|
});
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
if(!user) {
|
||||||
|
event.reply('Unknown user.');
|
||||||
|
} else {
|
||||||
|
event.reply(user.currentNick + ': Set a lastfm username with "~set lastfm username"');
|
||||||
|
}
|
||||||
|
callback(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.reply(event.user + ': Set a lastfm username with "~set lastfm username"');
|
||||||
|
callback(true);
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new lastfm(dbot);
|
||||||
|
};
|
35
modules-stock/lastfm/strings.json
Normal file
35
modules-stock/lastfm/strings.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"now_listening": {
|
||||||
|
"en": "{user} is listening to {track} by {artist}",
|
||||||
|
"de": "{user} hört {track} von {artist}"
|
||||||
|
},
|
||||||
|
"last_listened": {
|
||||||
|
"en": "{user} last listened to {track} by {artist}",
|
||||||
|
"de": "{user} hörte zuletzt {track} von {artist}"
|
||||||
|
},
|
||||||
|
"no_listen": {
|
||||||
|
"en": "{user} doesn't seem to have listened to anything recently :'(",
|
||||||
|
"de": "{user} scheint in letzter Zeit nichts gehört zu haben :'("
|
||||||
|
},
|
||||||
|
"taste_compat": {
|
||||||
|
"en": "{user1} and {user2} are {score}% musically compatible!",
|
||||||
|
"de": "{user1} und {user2} sind {score}% musikalisch kompatibel!"
|
||||||
|
},
|
||||||
|
"common_artists": {
|
||||||
|
"en": "Artists {user1} and {user2} have in common: {common}",
|
||||||
|
"de": "Künstler, die {user1} und {user2} gemeinsam haben: {common}"
|
||||||
|
},
|
||||||
|
"lfm_suggestion": {
|
||||||
|
"en": "{user}: Try listening to {name} by {artist}",
|
||||||
|
"de": "{user}: Versuche mal, {name} von {artist} anzuhören"
|
||||||
|
},
|
||||||
|
"lfm_profile": {
|
||||||
|
"en": "{user} has scrobbled {plays} tracks since {date} - {link}"
|
||||||
|
},
|
||||||
|
"lfm_scrobbliest": {
|
||||||
|
"en": "Users with the most played tracks: "
|
||||||
|
},
|
||||||
|
"lfm_unknown": {
|
||||||
|
"en": "Unknown Last.FM user. Please set your Last.FM user by typing '~set lastfm <name>' where <name> is your Last.FM username."
|
||||||
|
}
|
||||||
|
}
|
5
modules-stock/leafly/config.json
Normal file
5
modules-stock/leafly/config.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"outputPrefix": "\u00033weed\u000f",
|
||||||
|
"app_key": "",
|
||||||
|
"app_id": ""
|
||||||
|
}
|
47
modules-stock/leafly/leafly.js
Normal file
47
modules-stock/leafly/leafly.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Module name: Leafly
|
||||||
|
* Description: Information from leafly
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _ = require('underscore')._,
|
||||||
|
request = require('request');
|
||||||
|
|
||||||
|
var leafly = function(dbot) {
|
||||||
|
var ApiRoot = 'http://data.leafly.com/';
|
||||||
|
|
||||||
|
this.commands = {
|
||||||
|
'~strain': function(event) {
|
||||||
|
request.post(ApiRoot + 'strains', {
|
||||||
|
'headers': {
|
||||||
|
'app_key': this.config.app_key,
|
||||||
|
'app_id': this.config.app_id
|
||||||
|
},
|
||||||
|
'body': {
|
||||||
|
'page': 0,
|
||||||
|
'take': 1,
|
||||||
|
'sort': 'rating',
|
||||||
|
'search': event.input[1]
|
||||||
|
},
|
||||||
|
'json': true
|
||||||
|
}, function(error, response, body) {
|
||||||
|
if(_.isObject(body) && _.has(body, 'Strains') && body.Strains.length > 0) {
|
||||||
|
var strain = body.Strains[0],
|
||||||
|
flavours = _.pluck(strain.Flavors, 'Name').join(', ');
|
||||||
|
|
||||||
|
event.reply(dbot.t('strain', {
|
||||||
|
'name': strain.Name,
|
||||||
|
'flavours': flavours,
|
||||||
|
'link': strain.permalink
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
event.reply(dbot.t('no_strains'));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.commands['~strain'].regex = [/^strain (.+)$/, 2];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.fetch = function(dbot) {
|
||||||
|
return new leafly(dbot);
|
||||||
|
};
|
8
modules-stock/leafly/strings.json
Normal file
8
modules-stock/leafly/strings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"strain": {
|
||||||
|
"en": "{name} tastes of {flavours} - {link}"
|
||||||
|
},
|
||||||
|
"no_strains": {
|
||||||
|
"en": "No strains found :("
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user