3
0
mirror of https://github.com/reality/dbot.git synced 2025-08-02 23:57:24 +02:00

Compare commits

..

No commits in common. "master" and "v0.3" have entirely different histories.
master ... v0.3

296 changed files with 2160 additions and 15723 deletions

10
.gitignore vendored
View File

@ -1,9 +1,5 @@
# Ignore the user config files # Ignore the user config files
./config.json
# Ignore the user database
db.json db.json
# ignore npm
node_modules/
# ignore assets
public/bootstrap/
public/d3/

11
.gitmodules vendored
View File

@ -1,6 +1,9 @@
[submodule "jsbot"] [submodule "jsbot"]
path = jsbot path = jsbot
url = git://github.com/reality/jsbot.git url = git://github.com/reality/jsbot.git
[submodule "modules/stats"]
path = modules/stats
url = git://github.com/SamStudio8/stats.git
[submodule "modules/github"] [submodule "modules/github"]
path = modules/github path = modules/github
url = git://github.com/zuzak/dbot-github.git url = git://github.com/zuzak/dbot-github.git

26
LICENCE
View File

@ -1,14 +1,18 @@
Copyright (c) 2012-2013 Luke Slater (tinmachin3@gmail.com) Copyright (c) 2012 Luke Slater (tinmachin3@gmail.com)
This program is free software: you can redistribute it and/or modify Permission is hereby granted, free of charge, to any person obtaining a copy of
it under the terms of the GNU General Public License as published by this software and associated documentation files (the "Software"), to deal in
the Free Software Foundation, either version 3 of the License, or the Software without restriction, including without limitation the rights to
(at your option) any later version. 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:
This program is distributed in the hope that it will be useful, The above copyright notice and this permission notice shall be included in all
but WITHOUT ANY WARRANTY; without even the implied warranty of copies or substantial portions of the Software.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
along with this program. If not, see <http://www.gnu.org/licenses/>. 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.

View File

@ -1,8 +1,8 @@
# DBot IRC Bot # Depressionbot IRC Bot
## Introduction ## Introduction
DBot is an IRC bot which aims to be the fanciest IRC bot around - On Depressionbot is an IRC bot which aims to be the fanciest IRC bot around - On
the general standard of software fanciness, dbot is statistically rated as being the general standard of software fanciness, dbot is statistically rated as being
'82% the same as bathing in fine, fine grape juice.' '82% the same as bathing in fine, fine grape juice.'
@ -11,27 +11,18 @@ given I started it rather a long time after I began development of the project.
Please don't judge me too harshly for this as I am, in fact, mildly allergic to Please don't judge me too harshly for this as I am, in fact, mildly allergic to
writing documentation. writing documentation.
## Getting Started Requirements:
To get started with DBot, you first need to decide on a database system to use. - Node JS
DBot uses the [databank](http://github.com/e14n/databank) library, and each - [JSBot](http://github.com/reality/JSBot "JSBot"), a Javascript library which
module can be configured to use any database driver databank supports in its handles the IRC protocol
respective config.json file. There is currently no default database driver - Underscore JS library
option. - Various modules have their own requirements also.
The default for all modules is the 'redis' driver, and you can simply install ### External Modules
the Redis server to get going.
Once you have that set up, you can install DBot's dependencies, configure and JSBot and externally developed modules can be imported by running the following
run the bot for the first time with the following command: commands in the cloned repository:
``` git submodule init
./install git submodule update
```
## Upgrading
If you have used a previous version of DBot, then you can migrate most data
using the [dbot-migrate](https://github.com/reality/dbot-migrate) module.
Instructions on how to run this are included in the repository - remember to
remove db.json after migration, otherwise the instance will be slow!

View File

@ -1,6 +1,5 @@
dbot version 0.5-dev depressionbot version 0.3 (relentless)
this.db.search('releases', { 'name': 'jimbletron' }, function(result) { "RELEASE" });
"the database is a grilled cheese" "the database is a grilled cheese"
_.each(dbot.modules, function(module) { "RESCORE EVERYTHING" }); _.each(dbot.modules, function(module) { "RESCORE EVERYTHING" });
"He called his bot depressionbot, and that's when he was happy." "He called his bot depressionbot, and that's when he was happy."

View File

@ -8,20 +8,12 @@
"password": "lolturtles", "password": "lolturtles",
"channels": [ "channels": [
"#realitest" "#realitest"
], ]
"admin_channel": "#realitest"
} }
}, },
"admins": [ "batman" ], "admins": [ "batman" ],
"moderators": [ "whatever" ], "moderators": [ "whatever" ],
"power_users": [], "moduleNames": [ "ignore", "admin", "command", "dice", "js", "kick", "quotes", "spelling", "youare", "stats", "users" ],
"moduleNames": [ "ignore", "admin", "command", "dice", "js", "kick", "quotes", "spelling", "users", "youare", "timers", "sstats", "link" ], "language": "english",
"language": "en", "debugMode": true
"debugMode": false,
"debugLevel": 1,
"dbType": "redis",
"redisPort": 6379,
"timezone": "Europe/London",
"repoRoot": "https://github.com/reality/depressionbot/",
"version": "Depressionbot IRC bot 0.5-dev - Lovingly crafted by The DepressionBot Foundation (a charity arm of the Official Aberystwyth Open Source International Development League)."
} }

View File

@ -1,33 +0,0 @@
var databank = require('databank'),
Databank = databank.Databank,
DatabankObject = databank.DatabankObject,
_ = require('underscore')._;
/**
* Multiplex databank objects
*/
var DatabaseDriver = function(config) {
this.config = config;
this.databank = null;
};
/**
* Connect to or create a new DataBank
*/
DatabaseDriver.prototype.createDB = function(name, driver, schema, callback) {
var params = { 'schema': schema };
if(driver == 'redis' && _.has(this.config, 'redisPort')) params.port = this.config.redisPort;
if(driver == 'disk') params.dir = 'db';
this.databank = Databank.get(driver, params);
this.databank.connect({}, function(err) {
if(err) {
console.log('Didn\'t manage to connect to the data source - ' + err);
} else {
callback(this.databank);
}
}.bind(this));
};
exports.DatabaseDriver = DatabaseDriver;

48
install
View File

@ -1,48 +0,0 @@
#!/bin/bash
cat LICENCE
git submodule init
git submodule update
command -v node > /dev/null
if [[ $? -gt 0 ]]; then
echo 'node.js is not installed. Please install it before running install.sh.'
exit 1
fi
command -v npm > /dev/null
if [[ $? -gt 0 ]]; then
echo 'npm is not installed. Please install it before running install.sh'
exit 1
fi
npm install googlemaps humanize feedparser node-units tvdb method-override 500px process async wordnik node-uuid underscore request request-promise-native vm2 express moment-timezone moment jade databank databank-redis ent passport passport-local password-hash connect-flash
cd public/
wget https://github.com/twbs/bootstrap/releases/download/v3.3.2/bootstrap-3.3.2-dist.zip
unzip bootstrap-3.3.2-dist.zip
mv bootstrap-3.3.2-dist bootstrap
rm bootstrap-3.3.2-dist.zip
mkdir d3
cd d3
wget https://github.com/mbostock/d3/releases/download/v3.5.5/d3.zip
unzip d3.zip
rm d3.zip
cd ../..
if [ ! -f config.json ];
then
echo 'Creating configuration file...'
cp config.json.sample config.json
$EDITOR config.json # Open config file with EDITOR variable, but if not available:
if [[ $? != 0 ]]; then vim config.json; fi # Find the return code from previous command, if failed then use vim to edit.
fi
read -p "Setup complete. Run dbot now? [y/N]"
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo 'Okay. To run the bot, use "node run.js"'
exit
fi
node run.js

23
install.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
git submodule init
git submodule update
npm install underscore request sandbox express moment jade@0.25
cd public/
wget http://twitter.github.com/bootstrap/assets/bootstrap.zip
unzip bootstrap.zip
rm bootstrap.zip
mkdir d3
cd d3
wget http://d3js.org/d3.v3.zip
unzip d3.v3.zip
rm d3.v3.zip
cd ..
cp config.json.sample config.json
echo 'Setup complete. Now edit config.json with your preferences and run the bot with "node run.js"'

2
jsbot

@ -1 +1 @@
Subproject commit 4e6af64655674ba1a331910f3ed35b935eaba147 Subproject commit 35910d9025fa3af15b24cecc3f6e7ee897aee4dc

View File

@ -1,28 +0,0 @@
## 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

View File

@ -1,4 +0,0 @@
{
"foo": true,
"foo": "bar"
}

View File

@ -1,39 +0,0 @@
/**
* 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
};

View File

@ -1,8 +0,0 @@
{
"foo": {
"en": "{output} bar"
},
"foo2": {
"en": "Something went wrong :( Example:'~foo bar'"
}
}

View File

@ -1,3 +0,0 @@
{
"~foo": "~foo [bar]"
}

View File

@ -26,15 +26,6 @@ channel, it will attempt to give the caller ops in the current channel.
Perform a git pull, and then execute the 'reload' command. Saves a lot of time Perform a git pull, and then execute the 'reload' command. Saves a lot of time
updating! 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
Reload all of the modules currently in use by DBot. By using this, all module 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 functionality should be reloadable and replaceable without having to restart the
@ -55,18 +46,12 @@ loaded by the standard DBot process.
Unload a currently loaded module. This removes the module, and then triggers a Unload a currently loaded module. This removes the module, and then triggers a
reload of all modules. reload of all modules.
#### setconfig [path] [value] #### ban [user] [command]
Set a config value at path to be a certain value persistently. For example, if Ban a user from using a command. Command may be replaced with '\*,' which will
you want to change the web module to listen on port 9001, you can run _setconfig ban a user from use of all commands. Users banned from all commands will still
web.webPort 9001_. be subject to module listeners.
#### pushconfig [path] [value] #### unban [user] [command]
Push a new value to an existing config array. For example, if you want to add Unban a user from using a given command. If a user was previously banned using
the user 'batman62' to the DBot moderators, you can run _pushconfig moderators the '\*' wildcard, they may also be unbanned from such by replacing command with
batman62_. an asterisk here as well.
#### 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.

View File

@ -1,62 +1,12 @@
/** /**
* Module Name: Admin * Module Name: Admin
* Description: Set of commands which only one who is a DepressionBot * Description: Set of commands which only one who is a DepressionBot
* administrator can run. * administrator can run - as such, it has its own command execution listener.
*/ */
var fs = require('fs'), var fs = require('fs'),
_ = require('underscore')._; _ = require('underscore')._;
var admin = function(dbot) { 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) { exports.fetch = function(dbot) {

View File

@ -1,15 +1,52 @@
var fs = require('fs'), var fs = require('fs'),
_ = require('underscore')._, _ = require('underscore')._,
sys = require('sys'), sys = require('sys'),
process = require('process'),
exec = require('child_process').exec; exec = require('child_process').exec;
var commands = function(dbot) { var commands = function(dbot) {
var noChangeConfig = [ 'servers', 'name', 'moduleNames' ]; var noChangeConfig = [ 'servers', 'name', 'moduleNames' ];
var getCurrentConfig = function(configKey) {
var defaultConfigPath = dbot.config;
var userConfigPath = dbot.db.config;
if(configKey) {
var configKey = configKey.split('.');
for(var i=0;i<configKey.length-1;i++) {
if(_.has(defaultConfigPath, configKey[i])) {
if(!_.has(userConfigPath, configKey[i])) {
userConfigPath[configKey[i]] = {};
}
userConfigPath = userConfigPath[configKey[i]];
defaultConfigPath = defaultConfigPath[configKey[i]];
} else {
return false;
}
}
}
var currentOption;
if(configKey.length != 1) {
configKey = _.last(configKey);
if(_.has(userConfigPath, configKey) && !_.isUndefined(userConfigPath[configKey])) {
currentOption = userConfigPath[configKey];
} else if(_.has(defaultConfigPath, configKey)) {
currentOption = defaultConfigPath[configKey];
}
} else {
currentOption = defaultConfigPath[configKey];
}
return {
'user': userConfigPath,
'default': defaultConfigPath,
'value': currentOption
};
};
var commands = { var commands = {
// Join a channel // Join a channel
'~join': function(event) { 'join': function(event) {
var channel = event.params[1]; var channel = event.params[1];
if(_.has(event.allChannels, channel)) { if(_.has(event.allChannels, channel)) {
event.reply(dbot.t('already_in_channel', {'channel': channel})); event.reply(dbot.t('already_in_channel', {'channel': channel}));
@ -20,7 +57,7 @@ var commands = function(dbot) {
}, },
// Leave a channel // Leave a channel
'~part': function(event) { 'part': function(event) {
var channel = event.params[1]; var channel = event.params[1];
if(!_.has(event.allChannels, channel)) { if(!_.has(event.allChannels, channel)) {
event.reply(dbot.t('not_in_channel', {'channel': channel})); event.reply(dbot.t('not_in_channel', {'channel': channel}));
@ -31,7 +68,7 @@ var commands = function(dbot) {
}, },
// Op admin caller in given channel // Op admin caller in given channel
'~opme': function(event) { 'opme': function(event) {
var channel = event.params[1]; var channel = event.params[1];
// If given channel isn't valid just op in current one. // If given channel isn't valid just op in current one.
@ -41,29 +78,22 @@ var commands = function(dbot) {
dbot.instance.mode(event, channel, ' +o ' + event.user); dbot.instance.mode(event, channel, ' +o ' + event.user);
}, },
// Op via OperServ in order to work with channels dbot is not joined to, or joined to without having +o itself
'~forceopme': function(event) {
var channel = event.params[1];
if(!_.has(event.allChannels, channel)) {
channel = event.channel.name;
}
dbot.say('tripsit', 'OperServ', ' MODE ' + channel + ' +o ' + event.user);
},
// Do a git pull and reload // Do a git pull and reload
'~greload': function(event) { 'greload': function(event) {
exec("git pull", function (error, stdout, stderr) { exec("git pull", function (error, stdout, stderr) {
exec("git submodule update", function (error, stdout, stderr) { exec("git submodule update", function (error, stdout, stderr) {
event.reply(dbot.t('gpull')); event.reply(dbot.t('gpull'));
commands['~reload'](event); commands.reload(event);
commands['~version'](event); event.message = 'version';
event.action = 'PRIVMSG';
event.params = event.message.split(' ');
dbot.instance.emit(event);
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
}, },
// Display commit information for part of dbot // Display commit information for part of dbot
'~version': function(event){ 'version': function(event){
var cmd = "git log --pretty=format:'%h (%s): %ar' -n 1 -- "; var cmd = "git log --pretty=format:'%h (%s): %ar' -n 1 -- ";
if(event.params[1]){ if(event.params[1]){
var input = event.params[1].trim(); var input = event.params[1].trim();
@ -80,266 +110,199 @@ var commands = function(dbot) {
event.reply(stdout); event.reply(stdout);
} }
else{ else{
event.reply(dbot.t("no_version")); event.reply("No version information or queried module not loaded");
} }
}.bind(this)); }.bind(this));
}, },
'~status': function(event) {
'status': function(event) {
var moduleName = event.params[1]; var moduleName = event.params[1];
if(_.has(dbot.status, moduleName)) { if(_.has(dbot.status, moduleName)) {
var status = dbot.status[moduleName]; var status = dbot.status[moduleName];
if(status === true) { if(status === true) {
event.reply(dbot.t('status_good', { event.reply(moduleName + ' status: Shit looks good.');
'module': moduleName,
'reason': status
}));
} else { } else {
event.reply(dbot.t('status_bad', { event.reply(moduleName + ' status: Failed to load: ' + status);
'module': moduleName,
'reason': status
}));
} }
} else { } else {
event.reply(dbot.t("status_unloaded")); event.reply('Either that module wasn\'t on the roster or shit is totally fucked.');
} }
}, },
// Reload DB, translations and modules. // Reload DB, translations and modules.
'~reload': function(event) { 'reload': function(event) {
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8')); dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
dbot.reloadModules(); dbot.reloadModules();
process.nextTick(function() { event.reply(dbot.t('reload'));
event.reply(dbot.t('reload'));
});
}, },
// Say something in a channel // Say something in a channel
'~say': function(event) { 'say': function(event) {
var channel = event.params[1]; var channel = event.params[1];
if(event.params[1] === "@") { if(event.params[1] === "@") {
channel = event.channel.name; var channel = event.channel.name;
} }
var message = event.params.slice(2).join(' '); var message = event.params.slice(2).join(' ');
dbot.say(event.server, channel, message); dbot.say(event.server, channel, message);
}, },
// Load new module // Load new module
'~load': function(event) { 'load': function(event) {
var moduleName = event.params[1]; var moduleName = event.params[1];
if(!_.include(dbot.config.moduleNames, moduleName)) { if(!_.include(dbot.config.moduleNames, moduleName)) {
dbot.customConfig.moduleNames.push(moduleName); dbot.config.moduleNames.push(moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules(); dbot.reloadModules();
process.nextTick(function() { if(dbot.status[moduleName] === true) {
if(dbot.status[moduleName] === true) { event.reply(dbot.t('load_module', {'moduleName': moduleName}));
event.reply(dbot.t('load_module', { 'moduleName': moduleName })); } else {
} else { event.reply('Failed to load ' + moduleName + '. See \'status ' + moduleName + '\'.');
event.reply(dbot.t('load_failed', { 'module': moduleName })); }
}
});
} else { } else {
if(moduleName == 'web') { if(moduleName == 'web') {
event.reply(dbot.t('already_loaded_web')); event.reply(dbot.t('already_loaded_web'));
} else { } else {
event.reply(dbot.t('already_loaded', { 'moduleName': moduleName })); event.reply(dbot.t('already_loaded', {'moduleName': moduleName}));
} }
} }
}, },
// Unload a loaded module // Unload a loaded module
'~unload': function(event) { 'unload': function(event) {
var moduleNames = dbot.config.moduleNames; var moduleNames = dbot.config.moduleNames;
var moduleName = event.params[1]; var moduleName = event.params[1];
if(_.include(moduleNames, moduleName)) { if(_.include(moduleNames, moduleName)) {
var moduleDir = '../' + moduleName + '/'; var moduleDir = '../' + moduleName + '/';
try { var cacheKey = require.resolve(moduleDir + moduleName);
var cacheKey = require.resolve(moduleDir + moduleName); delete require.cache[cacheKey];
delete require.cache[cacheKey]; dbot.config.moduleNames = _.without(dbot.config.moduleNames, moduleName);
} catch(err) { }
dbot.customConfig.moduleNames = _.without(dbot.config.moduleNames, moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules(); dbot.reloadModules();
process.nextTick(function() { event.reply(dbot.t('unload_module', {'moduleName': moduleName}));
event.reply(dbot.t('unload_module', { 'moduleName': moduleName }));
});
} else { } else {
event.reply(dbot.t('unload_error', { 'moduleName': moduleName })); event.reply(dbot.t('unload_error', {'moduleName': moduleName}));
}
},
// Ban user from command or *
'ban': function(event) {
var username = event.params[1];
var command = event.params[2];
if(!_.has(dbot.db.bans, command)) {
dbot.db.bans[command] = [ ];
}
dbot.db.bans[command].push(username);
event.reply(dbot.t('banned', {'user': username, 'command': command}));
},
// Unban a user from command or *
'unban': function(event) {
var username = event.params[1];
var command = event.params[2];
if(_.has(dbot.db.bans, command) && _.include(dbot.db.bans[command], username)) {
_.reject(dbot.db.bans[command], function(bans) {
return bans == username;
}, this);
event.reply(dbot.t('unbanned', {'user': username, 'command': command}));
} else {
event.reply(dbot.t('unban_error', {'user': username}));
} }
}, },
/*** Config options ***/ /*** Config options ***/
'~setconfig': function(event) { 'setconfig': function(event) {
var configPath = event.input[1], var configPathString = event.params[1],
newOption = event.input[2]; configKey = _.last(configPathString.split('.')),
newOption = event.params[2];
event.reply(event.input[1]); if(!_.include(noChangeConfig, configKey)) {
var configPath = getCurrentConfig(configPathString);
if(!_.include(noChangeConfig, configPath)) { if(configPath == false || _.isUndefined(configPath.value)) {
this.internalAPI.getCurrentConfig(configPath, function(config) { event.reply("Config key doesn't exist bro");
if(config !== null) { return;
// Convert to boolean type if config item boolean }
if(_.isBoolean(config)) { var currentOption = configPath.value;
newOption = (newOption == "true");
}
// Convert to integer type is config item integer // Convert to boolean type if config item boolean
if(_.isNumber(config)) { if(_.isBoolean(currentOption)) {
newOption = parseInt(newOption); newOption = (newOption == "true");
} }
if(_.isArray(config)) { if(_.isArray(currentOption)) {
event.reply(dbot.t("config_array", { "alternate": "pushconfig" })); event.reply("Config option is an array. Try 'pushconfig'.");
} }
} else {
event.reply(dbot.t("no_config_key", {'path': configPath})); event.reply(configPathString + ": " + currentOption + " -> " + newOption);
configPath = configPath.split('.'); configPath['user'][configKey] = newOption;
if(_.has(dbot.config.modules, configPath[0])) { dbot.reloadModules();
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 { } else {
event.reply(dbot.t("config_lock")); event.reply("This config option cannot be altered while the bot is running.");
} }
}, },
'~pushconfig': function(event) { 'pushconfig': function(event) {
var configPath = event.input[1], var configPathString = event.params[1],
newOption = event.input[2]; configKey = _.last(configPathString.split('.')),
newOption = event.params[2];
if(!_.include(noChangeConfig, configPath)) { if(!_.include(noChangeConfig, configKey)) {
this.internalAPI.getCurrentConfig(configPath, function(config) { var configPath = getCurrentConfig(configPathString);
if(config !== null) { if(configPath == false || _.isUndefined(configPath.value)) {
if(_.isArray(config)) { event.reply("Config key doesn't exist bro");
event.reply(configPath + ": " + config + " << " + newOption); return;
config.push(newOption); }
this.internalAPI.setConfig(configPath, config, function(err) {}); var currentArray = configPath.value;
} else {
event.reply(dbot.t("config_array", { "alternate": "setconfig" })); if(!_.isArray(currentArray)) {
} event.reply("Config option is not an array. Try 'setconfig'.");
} else { return
event.reply(dbot.t("no_config_key", { 'path': configPath })); }
}
}.bind(this)); event.reply(configPathString + ": " + currentArray + " << " + newOption);
} else { currentArray.push(newOption);
event.reply(dbot.t("config_lock")); dbot.reloadModules();
} }
}, },
'~rmconfig': function(event) { 'showconfig': function(event) {
var configPath = event.input[1], var configPathString = event.params[1];
rmOption = event.input[2]; var configPath = getCurrentConfig(configPathString);
if(configPathString) {
var configKey = _.last(configPathString.split('.'));
if(configKey == false) {
event.reply("Config path doesn't exist");
return;
}
if(!_.include(noChangeConfig, configPath)) { if(_.isArray(configPath.value)) {
this.internalAPI.getCurrentConfig(configPath, function(config) { event.reply(configKey + ': ' + configPath.value);
if(config !== null) { } else if(_.isObject(configPath.value)) {
if(_.isArray(config)) { event.reply('Config keys in ' + configPathString + ': ' + Object.keys(configPath.value));
event.reply(configPath + ": " + config + " - " + rmOption); } else {
config = _.without(config, rmOption) event.reply(configKey + ': ' + configPath.value);
this.internalAPI.setConfig(configPath, config, function(err) {}); }
} else {
event.reply(dbot.t("config_array", { "alternate": "nope" }));
}
} else {
event.reply(dbot.t("no_config_key", { 'path': configPath }));
}
}.bind(this));
} else { } else {
event.reply(dbot.t("config_lock")); event.reply('Config keys in root: ' + Object.keys(configPath['default']));
} }
},
'~showconfig': function(event) {
var configPath = event.params[1];
if(configPath.indexOf('servers') != -1) { return false; }
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) { commands['greload'].access = 'admin';
command.access = 'admin'; commands['reload'].access = 'admin';
}); commands['unload'].access = 'admin';
commands['load'].access = 'admin';
commands['~showconfig'].access = 'admin'; commands['setconfig'].access = 'admin';
commands['~join'].access = 'power_user'; commands['showconfig'].access = 'moderator';
commands['~part'].access = 'power_user'; commands['join'].access = 'moderator';
commands['~opme'].access = 'moderator'; commands['part'].access = 'moderator';
commands['~forceopme'].access = 'power_user'; commands['opme'].access = 'moderator';
commands['~say'].access = 'power_user'; commands['say'].access = 'moderator';
commands['~pushconfig'].access = 'moderator'; commands['ban'].access = 'moderator';
commands['~rmconfig'].access = 'moderator'; commands['unban'].access = 'moderator';
commands['~pushconfig'].regex = [/pushconfig ([^ ]+) ([^ ]+)/, 3];
commands['~rmconfig'].regex = [/rmconfig ([^ ]+) ([^ ]+)/, 3];
commands['~setconfig'].regex = [/setconfig ([^ ]+) ([^ ]+)/, 3];
return commands; return commands;
}; };

View File

@ -1,5 +1,6 @@
{ {
"ignorable": false, "ignorable": false,
"dbType": "redis", "dbKeys": [ "bans" ],
"dependencies": [ "command" ] "dependencies": [ "command" ],
"help": "http://github.com/reality/depressionbot/blob/master/modules/admin/README.md"
} }

View File

@ -1,230 +1,80 @@
{ {
"join": { "join": {
"en": "Joined {channel}", "english": "Joined {channel}",
"es" : "Entrado en {channel}", "spanish" : "Entrado en {channel}",
"na'vi": "fpxäkìm {channel}(nemfa)", "na'vi": "fpxäkìm {channel}(nemfa)",
"cy": "Wedi ymuno {channel}", "welsh": "Wedi ymuno {channel}"
"nl": "{channel} binnengekomen",
"de": "{channel} beigetreten",
"fr": "{channel} rejoint",
"it": "Aderito a {channel}"
}, },
"part": { "part": {
"en": "Left {channel}", "english": "Left {channel}",
"es" : "Abandonada {channel}", "spanish" : "Abandonada {channel}",
"na'vi": "Hum {channel}", "na'vi": "Hum {channel}",
"cy": "Wedi gadael {channel}", "welsh": "Wedi gadael {channel}"
"nl": "{channel} verlaten",
"de": "{channel} verlassen",
"fr": "{channel} quitté",
"it": "uscito da {channel}"
}, },
"gpull": { "gpull": {
"en": "Git pulled that shit.", "english": "Git pulled that shit.",
"es": "Hecho git pull en esta mierda.", "spanish": "Hecho git pull en esta mierda.",
"na'vi": "Gìtìl fì'uti stamarsìm.", "na'vi": "Gìtìl fì'uti stamarsìm.",
"cy": "Wedi tynnu git yr cach na i gyd", "welsh": "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": { "reload": {
"en": "Reloaded that shit.", "english": "Reloaded that shit.",
"es": "Recargado esta mierda.", "spanish": "Recargado esta mierda.",
"na'vi": "Oel fìuti stìyeftxaw.", "na'vi": "Oel fìuti stìyeftxaw.",
"cy": "Ail-lwytho'r cach na", "welsh": "Ail-lwytho'r cach na"
"nl": "Die zooi opnieuw geladen.",
"de": "Neu geladen",
"fr": "Bordel rechargé",
"it": "Ricaricato questa puttanata"
}, },
"load_module": { "load_module": {
"en": "Loaded new module: {moduleName}", "english": "Loaded new module: {moduleName}",
"es": "Cargado módulo nuevo: {moduleName}", "spanish": "Cargado módulo nuevo: {moduleName}",
"na'vi": "Oel {moduleName}it amip stìyeftxaw.", "na'vi": "Oel {moduleName}it amip stìyeftxaw.",
"cy": "Wedi llwytho modiwl newydd: {moduleName}", "welsh": "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": { "unload_module": {
"en": "Turned off module: {moduleName}", "english": "Turned off module: {moduleName}",
"es": "Descargado módulo: {moduleName}", "spanish": "Descargado módulo: {moduleName}",
"na'vi": "Oel {moduleName} tswìya'.", "na'vi": "Oel {moduleName} tswìya'.",
"cy": "Wedi troi ffwrdd y modiwl: {moduleName}", "welsh": "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": { "unload_error": {
"en": "{moduleName} isn't loaded. Idiot.", "english": "{moduleName} isn't loaded. Idiot.",
"es": "{moduleName} no está cargado. Idiota.", "spanish": "{moduleName} no está cargado. Idiota.",
"na'vi": "Oel {moduleName}it omum. Nga skxawng lu.", "na'vi": "Oel {moduleName}it omum. Nga skxawng lu.",
"cy": "Di {moduleName} ddim wedi llwytho. Twpsyn", "welsh": "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": { "banned": {
"en": "{user} banned from {command}", "english": "{user} banned from {command}",
"es": "{user} está prohibido de usar {command}", "spanish": "{user} está prohibido de usar {command}",
"na'vi": "{command}ìri {user} ke tung.", "na'vi": "{command}ìri {user} ke tung.",
"cy": "{user} wedi ei gohurio o {command}", "welsh": "{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": { "unbanned": {
"en": "{user} unbanned from {command}", "english": "{user} unbanned from {command}",
"es": "{user} no está prohibido de user {command}", "spanish": "{user} no está prohibido de user {command}",
"na'vi": "{command}ìri {user} tung set.", "na'vi": "{command}ìri {user} tung set.",
"cy": "{user} wedi ei dad-wahardd o {command}", "welsh": "{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": { "unban_error": {
"en": "{user} wasn't banned from that command, fool.", "english": "{user} wasn't banned from that command, fool.",
"es": "{user} no fue prohibido de esta instrucción, tont@.", "spanish": "{user} no fue prohibido de esta instrucción, tont@.",
"na'vi": "{user} fìtsu'oti tamung srekrr, nga skxawng lu.", "na'vi": "{user} fìtsu'oti tamung srekrr, nga skxawng lu.",
"cy": "Nid oedd {user} wedi ei wahardd o'r gorchymyn yna, twpsyn", "welsh": "Nid oedd {user} wedi ei wahardd o'r gyrchymun yna, fŵl"
"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": { "qlock": {
"en": "Locked quote category: {category}", "english": "Locked quote category: {category}",
"es": "Cerrado la categoría: {category}", "spanish": "Cerrado la categoría: {category}",
"na'vi": "{category}ìri oel 'upxareti fmoli", "na'vi": "{category}ìri oel 'upxareti fmoli",
"cy": "Categori wedi cloi: {category}", "welsh": "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": { "already_in_channel": {
"en": "I'm already in {channel}", "english": "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": { "not_in_channel": {
"en": "I'm not in {channel}", "english": "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": { "already_loaded_web": {
"en": "WHY CAN'T I LOAD ALL THIS WEB? (web already loaded)", "english": "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": { "already_loaded": {
"en": "{moduleName} is already loaded.", "english": "{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}"
} }
} }

View File

@ -1,38 +0,0 @@
/**
* 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);
};

View File

@ -1,10 +0,0 @@
{
"announces": {
"aberwiki": {
"#test": {
"category": "test",
"distance": 1
}
}
}
}

View File

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

View File

@ -1,73 +0,0 @@
/**
* 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);
};

View File

@ -1,16 +0,0 @@
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);
};

View File

@ -1,157 +0,0 @@
/**
* 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);
};

View File

@ -1,50 +0,0 @@
{
"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"
}
}

View File

@ -1,5 +0,0 @@
{
"akill": {
"en": "{host} has been AKilled for {duration} due to \"{reason}\""
}
}

View File

@ -21,17 +21,6 @@ Command flow:
This is the only module which is force loaded, even if it's not specified in This is the only module which is force loaded, even if it's not specified in
the configuration file. 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 ### Commands
#### ~usage [command] #### ~usage [command]

View File

@ -2,40 +2,44 @@ var _ = require('underscore')._;
var api = function(dbot) { var api = function(dbot) {
return { return {
'isBanned': function(user, command) {
var banned = false;
if(_.has(dbot.db.bans, command)) {
if(_.include(dbot.db.bans[command], user) || _.include(dbot.db.bans['*'], user)) {
banned = true;
}
}
return banned;
},
/** /**
* Does the user have the correct access level to use the command? * Does the user have the correct access level to use the command?
*/ */
'hasAccess': function(event, command, callback) { 'hasAccess': function(user, command) {
var accessNeeded = dbot.commands[command].access, var access = true;
allowedNicks, var accessNeeded = dbot.commands[command].access;
user = event.rUser;
if(_.isUndefined(accessNeeded) || accessNeeded == null) { if(accessNeeded == 'admin') {
return callback(true); if(!_.include(dbot.config.admins, user)) {
} else if(!_.isFunction(accessNeeded)) { access = false;
if(_.has(dbot.access, accessNeeded)) { }
accessNeeded = dbot.access[accessNeeded]; } else if(accessNeeded == 'moderator') {
} else { if(!_.include(dbot.config.moderators, user) &&
return callback(true); !_.include(dbot.config.admins, user)) {
access = false;
} }
} }
allowedNicks = accessNeeded(event);
if(!_.include(allowedNicks, user.primaryNick) && !_.include(allowedNicks, user.currentNick)) { return access;
callback(false); },
} else {
if(_.has(dbot.modules, 'nickserv') && this.config.useNickserv == true) { /**
dbot.api.nickserv.auth(user.server, user.currentNick, function(result, primary) { * Is item (user or channel) ignoring command?
if(result == true && primary == user.primaryNick) { */
callback(true); 'isIgnoring': function(item, command) {
} else { var module = dbot.commands[command].module;
callback(false); return (_.has(dbot.db.ignores, item) &&
} _.include(dbot.db.ignores[item], module));
});
} else {
callback(true);
}
}
}, },
/** /**
@ -44,26 +48,27 @@ var api = function(dbot) {
*/ */
'applyRegex': function(commandName, event) { 'applyRegex': function(commandName, event) {
var applies = false; var applies = false;
event.message = event.message.substring(1);
if(_.has(dbot.commands[commandName], 'regex')) { if(_.has(dbot.commands[commandName], 'regex')) {
var cRegex = 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]);
var q = event.message.valMatch(cRegex[0], cRegex[1]); if(q) {
if(q) { applies = true;
applies = true; event.input = q;
event.input = q;
}
} else {
var q = event.message.match(cRegex);
if(q) {
applies = true;
event.input = q;
}
} }
} else { } else {
applies = true; applies = true;
} }
return applies; return applies;
},
'addHook': function(command, callback) {
console.log('adding hook');
if(_.has(dbot.commands, command)) {
if(!_.has(dbot.commands[command], 'hooks')) {
dbot.commands[command].hooks = [];
}
dbot.commands[command].hooks.push(callback);
}
} }
}; };
}; };

View File

@ -4,158 +4,60 @@
* command and then runs that command, given the user isn't banned from or * command and then runs that command, given the user isn't banned from or
* ignoring that command. * ignoring that command.
*/ */
var _ = require('underscore')._, var _ = require('underscore')._;
cDomain = require('domain').create();
var command = function(dbot) { var command = function(dbot) {
this.dbot = dbot;
/** /**
* Run the appropriate command given the input. * Run the appropriate command given the input.
*/ */
this.listener = function(event) { this.listener = function(event) {
var commandName = event.params[0]; var commandName = event.params[0];
if(commandName.charAt(0) != this.config.commandPrefix || this.config.passiveMode == true) { if(!_.has(dbot.commands, commandName)) {
return; if(_.has(dbot.modules, 'quotes')) {
} commandName = '~';
commandName = commandName.substring(1); } else {
return;
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(this.api.isBanned(event.user, commandName)) {
if(isBanned) { event.reply(dbot.t('command_ban', {'user': event.user}));
if(this.config.banOutput && commandName != this.config.commandPrefix) { } else {
event.reply(dbot.t('command_ban', {'user': event.user})); if(!this.api.isIgnoring(event.user, commandName) &&
!this.api.isIgnoring(event.channel, commandName) &&
this.api.hasAccess(event.user, commandName) &&
dbot.commands[commandName].disabled !== true) {
if(this.api.applyRegex(commandName, event)) {
try {
var command = dbot.commands[commandName];
var results = command.apply(dbot.modules[command.module], [event]);
if(_.has(command, 'hooks') && results !== false) {
_.each(command['hooks'], function(hook) {
hook.apply(hook.module, _.values(results));
}, this);
} }
} else if(!hasAccess) { } catch(err) {
if(this.config.accessOutput) { if(dbot.config.debugMode == true) {
event.reply(dbot.t('access_denied', { 'user': event.user })); event.reply('- Error in ' + commandName + ':');
event.reply('- Message: ' + err);
event.reply('- Top of stack: ' + err.stack.split('\n')[1].trim());
} }
} else if(!isIgnoring && _.has(dbot.commands, commandName) && !dbot.commands[commandName].disabled) { }
if(!_.has(dbot.commands, commandName)) { dbot.save();
if(_.has(dbot.modules, 'quotes')) { } else {
var key = event.message.substring(1); if(commandName !== '~') {
dbot.api.quotes.getInterpolatedQuote(event.server, if(_.has(dbot.usage, commandName)) {
event.channel.name, event.user, key, function(quote) { event.reply('Usage: ' + dbot.usage[commandName]);
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 { } else {
return; event.reply(dbot.t('syntax_error'));
}
}
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); }.bind(this);
this.on = 'PRIVMSG'; 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) { exports.fetch = function(dbot) {

View File

@ -1,9 +1,8 @@
var _ = require('underscore')._, var _ = require('underscore')._;
request = require('request');
var commands = function(dbot) { var commands = function(dbot) {
var commands = { return {
'usage': function(event) { '~usage': function(event) {
var commandName = event.params[1]; var commandName = event.params[1];
if(_.has(dbot.usage, commandName)) { if(_.has(dbot.usage, commandName)) {
event.reply(dbot.t('usage', { event.reply(dbot.t('usage', {
@ -17,52 +16,26 @@ var commands = function(dbot) {
} }
}, },
'~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) { '~help': function(event) {
var moduleName = event.params[1]; var moduleName = event.params[1];
if(!moduleName || !_.has(dbot.modules, moduleName)) { if(!_.has(dbot.modules, moduleName)) {
event.reply(dbot.t('usage', { var moduleName = dbot.commands[moduleName].module;
'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 if(moduleName && _.has(dbot.config[moduleName], 'help')) {
var help = dbot.config[moduleName].help;
event.reply(dbot.t('help_link', { event.reply(dbot.t('help_link', {
'module': moduleName, 'module': moduleName,
'link': helpLink 'link': help
})); }));
} } else {
if(!moduleName) {
moduleName = event.params[1];
}
event.reply(dbot.t('no_help', { 'module': moduleName }))
}
} }
}; };
commands['usage'].regex = [/usage ([^ ]+)/, 2];
return commands;
}; };
exports.fetch = function(dbot) { exports.fetch = function(dbot) {

View File

@ -1,9 +1,5 @@
{ {
"ignorable": false, "ignorable": false,
"dependencies": [ "event", "ignore", "users" ], "help": "http://github.com/reality/depressionbot/blob/master/modules/command/README.md",
"useNickserv": false, "dbKeys": [ "ignores" ]
"accessOutput": false,
"banOutput": false,
"passiveMode": false,
"commandPrefix": "~"
} }

View File

@ -1,78 +1,26 @@
{ {
"command_ban": { "command_ban": {
"en": "{user} is banned from using this command. Commence incineration.", "english": "{user} is banned from using this command. Commence incineration.",
"es": "{user} está prohibido de usar esta instrucción. urrently loaded modules now default: {modules}.", "spanish": "{user} está prohibido de usar esta instrucción. Comenzar incineración.",
"na'vi": "Tsu'ori {user} ke tung. Nga skxawng lu.", "na'vi": "Tsu'ori {user} ke tung. Nga skxawng lu.",
"cy": "Mae {user} wedi ei gohurio gan ddefnyddio'r gorchymun yma. Cychwyn orfflosgiad", "welsh": "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": { "syntax_error": {
"en": "Invalid syntax. Initiate incineration.", "english": "Invalid syntax. Initiate incineration.",
"es": "Sintaxis no válida. Iniciar incineración.", "spanish": "Sintaxis no válida. Iniciar incineración.",
"na'vi": "Ngeyä pamrel keyawr lu. Nga skxawng lu.", "na'vi": "Ngeyä pamrel keyawr lu. Nga skxawng lu.",
"cy": "Cystrawen annilys. Cychwyn orfflosgiad", "welsh": "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": { "usage": {
"en": "Usage for {command}: {usage}.", "english": "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": { "no_usage_info": {
"en": "No usage information found for {command}.", "english": "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": { "help_link": {
"en": "Help for {module}: {link}", "english": "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": { "no_help": {
"en": "No help found for {module}.", "english": "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}."
} }
} }

View File

@ -1,4 +0,0 @@
{
"~usage": "~usage [command]",
"~help": "~help [module]"
}

View File

@ -1,15 +0,0 @@
## 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.

View File

@ -1,44 +0,0 @@
/**
* 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);
};

View File

@ -1,6 +0,0 @@
{
"~md5": "~md5 [text]",
"~sha1": "~sha1 [text]",
"~sha256": "~sha256 [text]",
"~aes": "~aes \"[text]\" \"[key]\""
}

View File

@ -1,3 +0,0 @@
{
"outputChannel": "#realitree"
}

View File

@ -1,62 +0,0 @@
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);
};

View File

@ -1,5 +0,0 @@
## CTCP
Responds to CTCP commands.
## Description
At the moment only CTCP VERSION, CTCP PING is handled in JSBot.

View File

@ -1,3 +0,0 @@
{
"ignorable": true
}

View File

@ -1,39 +0,0 @@
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);
};

View File

@ -1,8 +1,7 @@
{ {
"username": "youruserhere", "username": "youruserhere",
"password": "yourpasswordhere", "password": "yourpasswordhere",
"dependencies": [ "link" ], "dependencies": [ "command" ],
"ignorable": true, "ignorable": true,
"dentQuotes": true, "dentQuotes": false
"api": "http://quitter.se/"
} }

View File

@ -2,27 +2,19 @@ var request = require('request');
_ = require('underscore')._; _ = require('underscore')._;
var dent = function(dbot) { var dent = function(dbot) {
this.StatusRegex = { this.dbot = dbot;
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 = { this.api = {
'post': function(content) { 'post': function(content) {
var username = this.config.username, var username = dbot.config.dent.username,
password = this.config.password, password = dbot.config.dent.password,
info, info,
auth = "Basic " + auth = "Basic " +
new Buffer(username + ":" + password).toString("base64"); new Buffer(username + ":" + password).toString("base64");
request.post({ request.post({
'url': this.config.api + '/statuses/update.json?status=' + 'url': 'http://identi.ca/api/statuses/update.json?status=' +
escape(content), content,
'headers': { 'headers': {
'Authorization': auth 'Authorization': auth
} }
@ -33,40 +25,18 @@ var dent = function(dbot) {
} }
}; };
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 = { this.commands = {
'~dent': function(event) { '~dent': function(event) {
this.api.post(event.input[1]); this.api.post(event.input[1]);
event.reply('Dent posted (probably).'); event.reply('Dent posted (probably).');
} }
}; };
this.commands['~dent'].regex = [/^dent (.+)$/, 2]; this.commands['~dent'].regex = [/^~dent (.+)$/, 2];
this.onLoad = function() { this.onLoad = function() {
if(this.config.dentQuotes === true && _.has(dbot.modules, 'quotes')) { if(dbot.config.dent.dentQuotes === true && _.has(dbot.modules, 'quotes')) {
dbot.api.event.addHook('qadd', function(key, text) { dbot.api.command.addHook('~qadd', function(key, text) {
if(text.indexOf('~~') == -1) { this.api.post(key + ': ' + text);
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));
} }
}.bind(this); }.bind(this);

View File

@ -17,14 +17,14 @@ var parseDiceSpec = function (specString) {
var normalizeDiceSpec = function (specString) { var normalizeDiceSpec = function (specString) {
var diceSpec = parseDiceSpec(specString); var diceSpec = parseDiceSpec(specString);
if (diceSpec["sides"] > 10000) { if (diceSpec["sides"] > 10000) {
return false; return false;
} }
if (diceSpec["count"] > 1000) { if (diceSpec["count"] > 1000) {
return false; return false;
} }
if (diceSpec["count"] > 1) { if (diceSpec["count"] > 1) {
var count = diceSpec["count"]; var count = diceSpec["count"];
} else { } else {
@ -74,7 +74,7 @@ var dice = function(dbot) {
event.reply(rolls[i][0] + ": invalid dice spec"); event.reply(rolls[i][0] + ": invalid dice spec");
} else { } else {
if (rolls[i][1].length > 1) { if (rolls[i][1].length > 1) {
var total = " (total " + _.reduce(rolls[i][1], function(memo, num){ return memo + num; }, 0); var total = " (total " + rolls[i][1].sum();
if (rolls[i][2] != 0) { if (rolls[i][2] != 0) {
if (rolls[i][2] > 0) { if (rolls[i][2] > 0) {
total += " + "; total += " + ";

View File

@ -1,17 +0,0 @@
## 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.

View File

@ -1,96 +0,0 @@
/**
* 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);
};

View File

@ -1,32 +0,0 @@
{
"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})"
}
}

View File

@ -1,9 +0,0 @@
## 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.

View File

@ -1,45 +0,0 @@
/**
* 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);
};

View File

@ -1,21 +0,0 @@
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);
};

View File

@ -1,4 +0,0 @@
{
"outputPrefix": "\u000311food\u000f",
"api_key": "http://food2fork.com/about/api"
}

View File

@ -1,60 +0,0 @@
/**
* 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);
};

View File

@ -1,8 +0,0 @@
{
"recipe": {
"en": "{title} - {link}"
},
"no_recipe": {
"en": "No recipes found."
}
}

View File

@ -1,34 +0,0 @@
## 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.

View File

@ -1,4 +0,0 @@
{
"ignorable": true,
"api_key": "CONSUMERKEY_HERE"
}

View File

@ -1,33 +0,0 @@
/**
* 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);
};

View File

@ -1,8 +0,0 @@
{
"5px_result":{
"en": "{name} - http://500px.com/photo/{id}"
},
"5px_error": {
"en": "Something went wrong :( Example: '~r500px'"
}
}

View File

@ -1,3 +0,0 @@
{
"~r500px": "~r500px"
}

1
modules/github Submodule

@ -0,0 +1 @@
Subproject commit d8f3039e4e636e0fc82f87028b26f1e622273dd5

View File

@ -1,18 +0,0 @@
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.

View File

@ -1,28 +0,0 @@
## 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``

View File

@ -1,8 +0,0 @@
{
"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"
}

View File

@ -1,192 +0,0 @@
/**
* 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);
};

View File

@ -1,94 +0,0 @@
{
"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}."
}
}

View File

@ -1,29 +0,0 @@
/**
* 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);
};

View File

@ -1,4 +0,0 @@
{
"api_key": "sethere",
"outputPrefix": "\u00033goodreads\u000f"
}

View File

@ -1,293 +0,0 @@
/**
* 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);

View File

@ -1,23 +0,0 @@
{
"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:"
}
}

View File

@ -1,6 +0,0 @@
{
"~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"
}

View File

@ -1,49 +0,0 @@
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);
};

View File

@ -1,6 +1,6 @@
{ {
"ignorable": false, "ignorable": false,
"dependencies": [ "users" ], "dependencies": [ "command" ],
"dbKeys": [ "ignores", "bans" ], "dbKeys": [ "ignores" ],
"dbType": "redis" "help": "http://github.com/reality/depressionbot/blob/master/modules/ignore/README.md"
} }

View File

@ -5,35 +5,15 @@
* this information, since that actually performs the ignorance. Also provides * this information, since that actually performs the ignorance. Also provides
* commands for moderators to choose the bot to ignore certain channels. * commands for moderators to choose the bot to ignore certain channels.
*/ */
var _ = require('underscore')._, var _ = require('underscore')._;
databank = require('databank'),
uuid = require('node-uuid'),
NoSuchThingError = databank.NoSuchThingError;
var ignore = function(dbot) { 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 = { var commands = {
'~ignore': function(event) { '~ignore': function(event) {
var module = event.params[1]; var module = event.params[1];
var ignorableModules = _.chain(dbot.modules) var ignorableModules = _.chain(dbot.modules)
.filter(function(module, name) { .filter(function(module, name) {
return dbot.config.modules[module].ignorable === true; return dbot.config[module].ignorable === true;
}) })
.pluck('name') .pluck('name')
.value(); .value();
@ -44,31 +24,22 @@ var ignore = function(dbot) {
'modules': ignorableModules.join(', ') 'modules': ignorableModules.join(', ')
})); }));
} else { } else {
if(module == '*' || _.include(ignorableModules, module)) { if(_.include(ignorableModules, module)) {
this.api.getUserIgnores(event.rUser, function(err, ignores) { if(_.has(dbot.db.ignores, event.user) && _.include(dbot.db.ignores[event.user], module)) {
if(!ignores) { event.reply(dbot.t('already_ignoring', { 'user': event.user }));
ignores = { } else {
'id': event.rUser.id, if(_.has(dbot.db.ignores, module)) {
'ignores': [], dbot.db.ignores[event.user].push(module);
'bans': [] } else {
}; dbot.db.ignores[event.user] = [module];
} }
if(!_.include(ignores.ignores, module)) { dbot.instance.ignoreTag(event.user, module);
ignores.ignores.push(module); event.reply(dbot.t('ignored', {
this.db.save('ignores', event.rUser.id, ignores, function(err) { 'user': event.user,
if(!err) { 'module': module
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 { } else {
event.reply(dbot.t('invalid_ignore', { 'user': event.user })); event.reply(dbot.t('invalid_ignore', { 'user': event.user }));
} }
@ -76,192 +47,77 @@ var ignore = function(dbot) {
}, },
'~unignore': function(event) { '~unignore': function(event) {
var ignoredModules = [];
if(_.has(dbot.db.ignores, event.user)) {
ignoredModules = dbot.db.ignores[event.user];
}
var module = event.params[1]; var module = event.params[1];
this.api.getUserIgnores(event.rUser, function(err, ignores) { if(_.isUndefined(module)) {
if(err || !ignores || _.isUndefined(module)) { event.reply(dbot.t('unignore_usage', {
if(ignores) { 'user': event.user,
event.reply(dbot.t('unignore_usage', { 'modules': ignoredModules.join(', ')
'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 { } else {
event.reply(dbot.t('invalid_ban', { 'user': event.user })); if(_.include(ignoredModules, module)) {
dbot.db.ignores[event.user].splice(dbot.db.ignores[event.user].indexOf(module), 1);
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 }));
}
} }
}, },
'~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) { '~ignorechannel': function(event) {
var channelName = event.input[1], var channel = ((event.params[1] == '@') ? event.channel.name : event.params[1]);
module = event.input[2]; var module = event.params[2];
// Ignoring the value of 'ignorable' at the moment // Ignoring the value of 'ignorable' at the moment
if(module == '*' || _.include(dbot.config.moduleNames, module)) { if(_.include(dbot.config.moduleNames, module)) {
var channel = false; if(!_.has(dbot.db.ignores, channel)) dbot.db.ignores[channel] = [];
if(!_.include(dbot.db.ignores[channel], module)) {
this.db.search('channel_ignores', { dbot.db.ignores[channel].push(module);
'server': event.server, dbot.instance.ignoreTag(channel, module);
'name': channelName event.reply(dbot.t('ignoring_channel', {
}, function(result) { 'module': module,
channel = result; 'channel': channel
}, function(err) { }));
if(!channel) { } else {
var id = uuid.v4(); event.reply(dbot.t('already_ignoring_channel', {
channel = { 'module': module,
'id': id, 'channel': channel
'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 { } else {
event.reply(dbot.t('module_not_exist', { 'module': module })); event.reply(dbot.t('module_not_exist', { 'module': module }));
} }
}, },
'~unignorechannel': function(event) { '~unignorechannel': function(event) {
var channelName = event.input[1], var channel = ((event.params[1] == '@') ? event.channel.name : event.params[1]);
module = event.input[2], var module = event.params[2];
channel = false;
this.db.search('channel_ignores', { if(!_.has(dbot.db.ignores, channel)) dbot.db.ignores[channel] = [];
'server': event.server, if(_.include(dbot.db.ignores[channel], module)) {
'name': channelName dbot.db.ignores[channel] = _.without(dbot.db.ignores[channel], module);
}, function(result) { dbot.instance.removeIgnore(channel, module);
channel = result; event.reply(dbot.t('unignoring_channel', {
}, function(err) { 'module': module,
if(channel && _.include(channel.ignores, module)) { 'channel': channel
channel.ignores = _.without(channel.ignores, module); }));
this.db.save('channel_ignores', channel.id, channel, function(err) { } else {
dbot.instance.removeIgnore(channel.name, module); event.reply(dbot.t('not_ignoring_channel', {
event.reply(dbot.t('unignoring_channel', { 'module': module,
'module': module, 'channel': channel
'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['~ignorechannel'].access = 'moderator';
commands['~unignorechannel'].access = 'moderator'; commands['~unignorechannel'].access = 'moderator';
@ -269,34 +125,12 @@ var ignore = function(dbot) {
this.onLoad = function() { this.onLoad = function() {
dbot.instance.clearIgnores(); dbot.instance.clearIgnores();
_.each(dbot.db.ignores, function(ignores, item) {
this.db.scan('ignores', function(ignores) { _.each(ignores, function(ignore) {
dbot.api.users.getUser(ignores.id, function(err, user) { dbot.instance.ignoreTag(item, ignore);
if(user) { }, this);
_.each(ignores.ignores, function(module) { }, this);
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) { exports.fetch = function(dbot) {

View File

@ -1,183 +1,59 @@
{ {
"ignore_usage": { "ignore_usage": {
"en": "{user}: Usage: ~ignore [module]. Modules you can ignore are: {modules}.", "english": "{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}.", "spanish": "{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}.", "na'vi": "{user}: Sar: ~ignore ['u]. U, nga ke tìng mikyun: {modules}.",
"cy": "{user}: Defnydd: ~ignore [modiwl]. Modiwlau a allech anwybyddu yw: {modules}.", "welsh": "{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": { "already_ignoring": {
"en": "{user}: You're already ignoring that module.", "english": "{user}: You're already ignoring that module.",
"es": "{user}: Ya ignoras este módulo.", "spanish": "{user}: Ya ignoras este módulo.",
"na'vi": "{user}: 'uri nga ke tìng mikyun srekrr.", "na'vi": "{user}: 'uri nga ke tìng mikyun srekrr.",
"cy": "{user}: Mi rwyt ti'n anwybyddu'r modiwl yna'n barod.", "welsh": "{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": { "ignored": {
"en": "{user}: Now ignoring {module}.", "english": "{user}: Now ignoring {module}.",
"es": "{user}: Estás ignorando {module}.", "spanish": "{user}: Estás ignorando {module}.",
"na'vi": "{user}: Nga ke terìng mikyun {module}ne set.", "na'vi": "{user}: Nga ke terìng mikyun {module}ne set.",
"cy": "{user}: Nawr yn anwybyddu {module}", "welsh": "{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": { "invalid_ignore": {
"en": "{user}: That isn't a valid module name.", "english": "{user}: That isn't a valid module name.",
"es": "{user}: Ese no es un nombre de un módulo valido.", "spanish": "{user}: Ese no es un nombre de un módulo valido.",
"na'vi": "{user}: Tsatstxo eyawr ke lu.", "na'vi": "{user}: Tsatstxo eyawr ke lu.",
"cy": "{user}: Nid oedd hwna'n modiwl dilys", "welsh": "{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": { "unignore_usage": {
"en": "{user}: Usage: ~unignore [module]. Modules you are currently ignoring: {modules}.", "english": "{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}.", "spanish": "{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}.", "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}", "welsh": "{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": { "invalid_unignore": {
"en": "{user}: You're not ignoring that module or it doesn't exist.", "english": "{user}: You're not ignoring that module or it doesn't exist.",
"es": "{user}: No ignoras este módulo o no existe.", "spanish": "{user}: No ignoras este módulo o no existe.",
"na'vi":"{user}: Nga terìng mikyun fu fì'ul fìtsengit ke tok.", "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", "welsh": "{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": { "unignored": {
"en": "{user}: No longer ignoring {module}.", "english": "{user}: No longer ignoring {module}.",
"es": "{user}: Ya no ignoras {module}.", "spanish": "{user}: Ya no ignoras {module}.",
"na'vi": "{user}: Nga terìng mikyun {module}ne set", "na'vi": "{user}: Nga terìng mikyun {module}ne set",
"cy": "{user}: Ddim yn anwybyddu {module} bellach", "welsh": "{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": { "ignoring_channel": {
"en": "Now ignoring {module} in {channel}.", "english": "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": { "already_ignoring_channel": {
"en": "Already ignoring {module} in {channel}.", "english": "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": { "module_not_exist": {
"en": "{module} isn't loaded or doesn't exist.", "english": "{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": { "unignoring_channel": {
"en": "No longer ignoring {module} in {channel}.", "english": "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": { "not_ignoring_channel": {
"en": "{module} wasn't being ignored in {channel}.", "english": "{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}"
} }
} }

View File

@ -1,65 +0,0 @@
## 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.

View File

@ -1,14 +0,0 @@
{
"dbKeys": [ "imgur" ],
"dependencies": [ "web", "api", "link" ],
"imagelength": 5,
"ricachelength": 7,
"nsfwwarn": true,
"apikey": "86fd3a8da348b65",
"highscore": "ricount",
"autoadd": {
"e49e686582ce3f60cb51d00c10924861": "facebookman",
"b11b634c74562bcd4e5d17b0d90987be": "raffleguy"
},
"outputPrefix": "\u00038imgur\u000f"
}

View File

@ -1,330 +0,0 @@
/**
* 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);
}

View File

@ -1,23 +0,0 @@
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);
};

View File

@ -1,22 +0,0 @@
{
"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"
}
}

View File

@ -5,5 +5,6 @@
} }
}, },
"dependencies": [ "command" ], "dependencies": [ "command" ],
"ignorable": true "ignorable": true,
"help": "http://github.com/reality/depressionbot/blob/master/modules/js/README.md"
} }

View File

@ -4,38 +4,31 @@
* the channel. Also allows admins to run un-sandboxed Javascript code with * the channel. Also allows admins to run un-sandboxed Javascript code with
* access to the DepressionBot instance memory. * access to the DepressionBot instance memory.
*/ */
var VM = require('vm2').VM; var vm = require('vm');
var sbox = require('sandbox');
var js = function(dbot) { var js = function(dbot) {
var commands = { var commands = {
// Run JS code sandboxed, return result to channel. // Run JS code sandboxed, return result to channel.
'~js': function(event) { '~js': function(event) {
try { try {
var s = new VM({timeout: 1000, sandbox: {}}); var s = new sbox();
event.reply(s.run(event.input[1])); s.run(event.input[1], function(output) {
} catch(err) { event.reply(err); } event.reply(output.result);
}.bind(this));
} catch(err) {}
}, },
// Run JS code un-sandboxed, with access to DBot memory (admin-only). // Run JS code un-sandboxed, with access to DBot memory (admin-only).
'~ajs': function(event) { '~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]); var ret = eval(event.input[1]);
if(ret !== undefined) { if(ret !== undefined) {
event.reply(ret); event.reply(ret);
} }
} }
}; };
commands['~js'].regex = [/^js (.*)/, 2]; commands['~js'].regex = [/^~js (.*)/, 2];
commands['~ajs'].regex = [/^ajs (.*)/, 2]; commands['~ajs'].regex = [/^~ajs (.*)/, 2];
commands['~ajs'].access = 'admin'; commands['~ajs'].access = 'admin';
this.name = 'js'; this.name = 'js';

View File

@ -1,5 +0,0 @@
{
"announce": [ { "server": "tripsit", "name": "#dbot" } ],
"dbKeys": [ "karma" ],
"dbType": "redis"
}

View File

@ -1,201 +0,0 @@
/**
* 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);
};

View File

@ -1,8 +0,0 @@
{
"karma": {
"en": "[karma] {item} has {karma} karma"
},
"newkarma": {
"en": "{item} now has{value} karma"
}
}

View File

@ -15,15 +15,3 @@ people.
#### ~kickstats #### ~kickstats
Show a list of top kickers and kickees. 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.

View File

@ -1,512 +0,0 @@
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);
};

View File

@ -1,18 +1,6 @@
{ {
"dbKeys": [ "kicks", "kickers", "hosts", "tempBans" ], "dbKeys": [ "kicks", "kickers" ],
"dependencies": [ "command", "report", "users" ], "dependencies": [ "command" ],
"ignorable": true, "help": "http://github.com/reality/depressionbot/blob/master/modules/kick/README.md",
"countSilently": true, "ignorable": true
"quietBans": [
"#wherever"
],
"network_name": {
"aberwiki": "OAOSIDL"
},
"chanserv": "ChanServ",
"document_bans": false,
"dbType": "redis",
"requireWebLogin": true,
"webAccess": "power_user",
"timeoutTime": "10m"
} }

View File

@ -1,270 +1,84 @@
var _ = require('underscore')._; var _ = require('underscore')._;
var kick = function (dbot) { var kick = function(dbot) {
if (!_.has(dbot.db, 'recentTimeouts')) { var commands = {
dbot.db.recentTimeouts = {}; // Give the number of times a given user has been kicked and has kicked
} // other people.
this.recentTimeouts = dbot.db.recentTimeouts; '~kickcount': function(event) {
var username = event.params[1];
this.api = { if(!_.has(dbot.db.kicks, username)) {
'ban': function (server, host, channel) { var kicks = '0';
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 { } else {
duration += ' minutes'; var kicks = dbot.db.kicks[username];
} }
var vStatus = dbot.instance.connections[server].channels[channel].nicks[quietee].voice; if(!_.has(dbot.db.kickers, username)) {
dbot.api.timers.addTimeout(msTimeout, function () { var kicked = '0';
if (_.has(this.hosts[server], quietee)) { } else {
if (_.include(this.config.quietBans, channel)) { var kicked = dbot.db.kickers[username];
this.api.unban(server, this.hosts[server][quietee], channel); }
this.api.voice(server, quietee, channel);
} else { event.reply(dbot.t('user_kicks', {
this.api.unquiet(server, this.hosts[server][quietee], channel); '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] + "), ";
} }
dbot.api.users.resolveUser(server, dbot.config.name, function (err, user) { return kickString.slice(0, -2);
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); event.reply(orderedKickLeague(dbot.db.kicks, 'Kicked'));
event.reply(orderedKickLeague(dbot.db.kickers, 'Kickers'));
if (_.include(this.config.quietBans, channel)) { }
this.api.ban(server, this.hosts[server][quietee], channel); };
} else { this.commands = commands;
this.api.quiet(server, host, channel);
} this.listener = function(event) {
if(event.kickee == dbot.config.name) {
if (reason.indexOf('#warn') !== -1) { dbot.instance.join(event, event.channel);
dbot.api.warning.warn(server, quieter, quietee, event.reply(dbot.t('kicked_dbot', { 'botname': dbot.config.name }));
'Quieted in ' + channel + ' for ' + reason, channel, dbot.db.kicks[dbot.config.name] += 1;
function () {});
}
} else { } else {
event.reply(dbot.t('no_user', { if(!_.has(dbot.db.kicks, event.kickee)) {
'user': quietee 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;
}
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';
},
'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) { exports.fetch = function(dbot) {
return new kick(dbot); return new kick(dbot);
}; };

View File

@ -1,46 +0,0 @@
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);
};

View File

@ -1,115 +1,14 @@
{ {
"user_kicks": { "user_kicks": {
"en": "{user} has been kicked {kicks} times and has kicked people {kicked} times.", "english": "{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.", "spanish": "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.", "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.", "welsh": "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": { "kicked_dbot": {
"en": "Thou shalt not kick {botname}", "english": "Thou shalt not kick {botname}",
"es": "No expulsás {botname}", "spanish": "No expulsás {botname}",
"na'vi": "Ngal {botname}it ke tsun tsrive'i", "na'vi": "Ngal {botname}it ke tsun tsrive'i",
"cy": "Ni ddylech cicio {botname}", "welsh": "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."
} }
} }

View File

@ -1,9 +0,0 @@
{
"~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"
}

View File

@ -1,11 +0,0 @@
{
"action": "kill",
"sensitivity": 10,
"exempt": [],
"advert_content": [
"________ ______"
],
"cliconn_channel": "#dnsbl",
"cliconn_patterns": [],
"exempt_channels": []
}

View File

@ -1,159 +0,0 @@
/**
* 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);
};

View File

@ -1,17 +0,0 @@
{
"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."
}
}

View File

@ -1,66 +0,0 @@
## 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

View File

@ -1,5 +0,0 @@
{
"dependencies": [ "profile", "youtube", "spotify" ],
"api_key": "blah",
"outputPrefix": "\u000315,5last.fm\u000f"
}

View File

@ -1,526 +0,0 @@
/**
* 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);
};

View File

@ -1,35 +0,0 @@
{
"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."
}
}

View File

@ -1,5 +0,0 @@
{
"outputPrefix": "\u00033weed\u000f",
"app_key": "",
"app_id": ""
}

Some files were not shown because too many files have changed in this diff Show More