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

Merge pull request #535 from reality/database

Merge Database Branch to Master
This commit is contained in:
Luke Slater 2013-07-24 14:42:52 -07:00
commit aa40c78127
118 changed files with 3500 additions and 1894 deletions

3
.gitmodules vendored
View File

@ -4,6 +4,3 @@
[submodule "modules/github"]
path = modules/github
url = git://github.com/zuzak/dbot-github.git
[submodule "modules/stats"]
path = modules/stats
url = git@github.com:SamStudio8/dbot-stats.git

View File

@ -11,18 +11,11 @@ given I started it rather a long time after I began development of the project.
Please don't judge me too harshly for this as I am, in fact, mildly allergic to
writing documentation.
Requirements:
## Getting Started
- Node JS
- [JSBot](http://github.com/reality/JSBot "JSBot"), a Javascript library which
handles the IRC protocol
- Underscore JS library
- Various modules have their own requirements also.
You can install DBot's dependencies, configure and run the bot for the first
time with the following command:
### External Modules
JSBot and externally developed modules can be imported by running the following
commands in the cloned repository:
git submodule init
git submodule update
```
sh install.sh
```

33
database.js Normal file
View File

@ -0,0 +1,33 @@
var databank = require('databank'),
Databank = databank.Databank,
DatabankObject = databank.DatabankObject,
_ = require('underscore')._;
/**
* Multiplex databank objects
*/
var DatabaseDriver = function(config) {
this.config = config;
this.databanks = {};
};
/**
* 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.databanks[name] = Databank.get(driver, params);
this.databanks[name].connect({}, function(err) {
if(err) {
console.log('Didn\'t manage to connect to the data source - ' + err);
} else {
callback(this.databanks[name]);
}
}.bind(this));
};
exports.DatabaseDriver = DatabaseDriver;

View File

@ -14,7 +14,7 @@ then
exit 1
fi
npm install wordnik ent underscore request sandbox express moment jade@0.25
npm install wordnik node-uuid underscore request sandbox express moment jade databank databank-redis ent
cd public/
wget http://twitter.github.com/bootstrap/assets/bootstrap.zip

2
jsbot

@ -1 +1 @@
Subproject commit 11cbb75b504fbb703450a2f8c938d5a81680109c
Subproject commit bb1e72b173a136816287bc8d90509cc38a0986e3

View File

@ -26,6 +26,15 @@ 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
updating!
#### version [module]
Shows the git version of the currently loaded revision of DBot. If module is
provided, it will attempt to get the revision of the module (this is only useful
for submodules).
#### status [module]
Show the recorded status for a given module, this is helpful for debugging
issues when loading or for checking if a module is loaded.
#### reload
Reload all of the modules currently in use by DBot. By using this, all module
functionality should be reloadable and replaceable without having to restart the
@ -46,12 +55,18 @@ loaded by the standard DBot process.
Unload a currently loaded module. This removes the module, and then triggers a
reload of all modules.
#### ban [user] [command]
Ban a user from using a command. Command may be replaced with '\*,' which will
ban a user from use of all commands. Users banned from all commands will still
be subject to module listeners.
#### setconfig [path] [value]
Set a config value at path to be a certain value persistently. For example, if
you want to change the web module to listen on port 9001, you can run _setconfig
web.webPort 9001_.
#### unban [user] [command]
Unban a user from using a given command. If a user was previously banned using
the '\*' wildcard, they may also be unbanned from such by replacing command with
an asterisk here as well.
#### pushconfig [path] [value]
Push a new value to an existing config array. For example, if you want to add
the user 'batman62' to the DBot moderators, you can run _pushconfig moderators
batman62_.
#### showconfig [path]
Use this to explore and view the DBot configuration. If called without a path,
it will display the config keys in the root; if the path is a subkey, it will
show all config keys under that key. If you give it an actual key, it'll show you
the currently effective config value.

View File

@ -1,12 +1,56 @@
/**
* Module Name: Admin
* Description: Set of commands which only one who is a DepressionBot
* administrator can run - as such, it has its own command execution listener.
* administrator can run.
*/
var fs = require('fs'),
_ = require('underscore')._;
var admin = function(dbot) {
this.internalAPI = {
'getCurrentConfig': function(configKey, callback) {
var configPath = dbot.config;
configKey = configKey.split('.');
for(var i=0;i<configKey.length;i++) {
if(_.has(configPath, configKey[i])) {
configPath = configPath[configKey[i]];
} else {
configPath = null;
break;
}
}
callback(configPath);
},
'setConfig': function(configKey, newOption, callback) {
var configPath = dbot.customConfig,
oldOption = null;
configKey = configKey.split('.');
for(var i=0;i<configKey.length-1;i++) {
if(!_.has(configPath, configKey[i])) {
configPath[configKey[i]] = {};
}
configPath = configPath[configKey[i]];
}
if(_.has(configPath, configKey[i])) {
oldOption = configPath[configKey[i]];
}
configPath[configKey[i]] = newOption;
this.internalAPI.saveConfig();
dbot.reloadModules();
callback(null, oldOption);
}.bind(this),
'saveConfig': function() {
var config = dbot.customConfig;
fs.writeFileSync('config.json', JSON.stringify(config, null, ' '));
}
};
};
exports.fetch = function(dbot) {

View File

@ -5,45 +5,7 @@ var fs = require('fs'),
var commands = function(dbot) {
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 && 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 = {
// Join a channel
'~join': function(event) {
@ -120,9 +82,15 @@ var commands = function(dbot) {
if(_.has(dbot.status, moduleName)) {
var status = dbot.status[moduleName];
if(status === true) {
event.reply(dbot.t("status_good",{"module":moduleName, "reason": status}));
event.reply(dbot.t('status_good', {
'module': moduleName,
'reason': status
}));
} else {
event.reply(dbot.t("status_bad",{"module":moduleName, "reason": status}));
event.reply(dbot.t('status_bad', {
'module': moduleName,
'reason': status
}));
}
} else {
event.reply(dbot.t("status_unloaded"));
@ -133,7 +101,9 @@ var commands = function(dbot) {
'~reload': function(event) {
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
dbot.reloadModules();
event.reply(dbot.t('reload'));
process.nextTick(function() {
event.reply(dbot.t('reload'));
});
},
// Say something in a channel
@ -150,18 +120,21 @@ var commands = function(dbot) {
'~load': function(event) {
var moduleName = event.params[1];
if(!_.include(dbot.config.moduleNames, moduleName)) {
dbot.config.moduleNames.push(moduleName);
dbot.customConfig.moduleNames.push(moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules();
if(dbot.status[moduleName] === true) {
event.reply(dbot.t('load_module', {'moduleName': moduleName}));
} else {
event.reply(dbot.t("load_failed",{"module": moduleName}));
}
process.nextTick(function() {
if(dbot.status[moduleName] === true) {
event.reply(dbot.t('load_module', { 'moduleName': moduleName }));
} else {
event.reply(dbot.t('load_failed', { 'module': moduleName }));
}
});
} else {
if(moduleName == 'web') {
event.reply(dbot.t('already_loaded_web'));
} else {
event.reply(dbot.t('already_loaded', {'moduleName': moduleName}));
event.reply(dbot.t('already_loaded', { 'moduleName': moduleName }));
}
}
},
@ -176,98 +149,136 @@ var commands = function(dbot) {
var cacheKey = require.resolve(moduleDir + moduleName);
delete require.cache[cacheKey];
} catch(err) { }
dbot.config.moduleNames = _.without(dbot.config.moduleNames, moduleName);
dbot.customConfig.moduleNames = _.without(dbot.config.moduleNames, moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules();
event.reply(dbot.t('unload_module', {'moduleName': moduleName}));
process.nextTick(function() {
event.reply(dbot.t('unload_module', { 'moduleName': moduleName }));
});
} else {
event.reply(dbot.t('unload_error', {'moduleName': moduleName}));
event.reply(dbot.t('unload_error', { 'moduleName': moduleName }));
}
},
/*** Config options ***/
'~setconfig': function(event) {
var configPathString = event.params[1],
configKey = _.last(configPathString.split('.')),
newOption = event.params[2];
var configPath = event.input[1],
newOption = event.input[2];
if(!_.include(noChangeConfig, configKey)) {
var configPath = getCurrentConfig(configPathString);
if(!_.include(noChangeConfig, configPath)) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
// Convert to boolean type if config item boolean
if(_.isBoolean(config)) {
newOption = (newOption == "true");
}
if(configPath == false || _.isUndefined(configPath.value)) {
event.reply(dbot.t("no_config_key"));
return;
}
var currentOption = configPath.value;
// Convert to integer type is config item integer
if(_.isNumber(config)) {
newOption = parseInt(newOption);
}
// Convert to boolean type if config item boolean
if(_.isBoolean(currentOption)) {
newOption = (newOption == "true");
}
if(_.isArray(config)) {
event.reply(dbot.t("config_array", { "alternate": "pushconfig" }));
}
} else {
topConfigPath = configPath.split('.')[0];
if(_.has(dbot.config.modules, topConfigPath)) {
configPath.splice(0, 0, 'modules');
event.params[1] = configPath.join('.');
this.commands['~showconfig'](event);
return;
} else {
event.reply(dbot.t('new_config_key', { 'key': configPath }));
}
}
// Convert to integer type is config item integer
if(_.isNumber(currentOption)) {
newOption = parseInt(newOption);
}
if(_.isArray(currentOption)) {
event.reply(dbot.t("config_array",{"alternate": "pushconfig"}));
}
event.reply(configPathString + ": " + currentOption + " -> " + newOption);
configPath['user'][configKey] = newOption;
dbot.reloadModules();
this.internalAPI.setConfig(configPath, newOption, function(err) {
event.reply(configPath + ": " + config + " -> " + newOption);
});
}.bind(this));
} else {
event.reply(dbot.t("config_lock"));
}
},
'~pushconfig': function(event) {
var configPathString = event.params[1],
configKey = _.last(configPathString.split('.')),
newOption = event.params[2];
var configPath = event.input[1],
newOption = event.input[2];
if(!_.include(noChangeConfig, configKey)) {
var configPath = getCurrentConfig(configPathString);
if(configPath == false || _.isUndefined(configPath.value)) {
event.reply(dbot.t("no_config_key"));
return;
}
var currentArray = configPath.value;
if(!_.isArray(currentArray)) {
event.reply(dbot.t("config_array",{"alternate": "setconfig"}));
return;
}
event.reply(configPathString + ": " + currentArray + " << " + newOption);
currentArray.push(newOption);
dbot.reloadModules();
if(!_.include(noChangeConfig, configPath)) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
if(_.isArray(config)) {
event.reply(configPath + ": " + config + " << " + newOption);
config.push(newOption);
this.internalAPI.setConfig(configPath, config, function(err) {});
} else {
event.reply(dbot.t("config_array", { "alternate": "setconfig" }));
}
} else {
event.reply(dbot.t("no_config_key", { 'path': configPath }));
}
}.bind(this));
} else {
event.reply(dbot.t("config_lock"));
}
},
'~showconfig': function(event) {
var configPathString = event.params[1];
var configPath = getCurrentConfig(configPathString);
if(configPathString) {
var configKey = _.last(configPathString.split('.'));
if(!configKey) {
event.reply(dbot.t("no_config_path"));
return;
}
var configPath = event.params[1];
if(configPath) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
if(_.isArray(config)) {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": config
}));
} else if(_.isObject(config)) {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": _.keys(config)
}));
} else {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": config
}));
}
} else {
event.reply(dbot.t("no_config_key", {'path': configPath}));
if(_.isArray(configPath.value)) {
event.reply(configKey + ': ' + configPath.value);
} else if(_.isObject(configPath.value)) {
event.reply(dbot.t("config_keys_location",{"path":configPathString,"value":Object.keys(configPath.value)}));
} else {
event.reply(configKey + ': ' + configPath.value);
}
configPath = configPath.split('.');
if(_.has(dbot.config.modules, configPath[0])) {
configPath.splice(0, 0, 'modules');
} else {
configPath.pop();
}
event.params[1] = configPath.join('.');
this.commands['~showconfig'](event);
}
}.bind(this));
} else {
event.reply(dbot.t("config_keys_location",{"path":"root","value":Object.keys(configPath['default'])}));
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) }));
});
});
}
};
@ -281,6 +292,9 @@ var commands = function(dbot) {
commands['~opme'].access = 'moderator';
commands['~say'].access = 'moderator';
commands['~pushconfig'].regex = [/~pushconfig ([^ ]+) ([^ ]+)/, 3];
commands['~setconfig'].regex = [/~setconfig ([^ ]+) ([^ ]+)/, 3];
return commands;
};

View File

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

View File

@ -5,7 +5,8 @@
"na'vi": "fpxäkìm {channel}(nemfa)",
"cy": "Wedi ymuno {channel}",
"nl": "{channel} binnengekomen",
"de": "{channel} beigetreten"
"de": "{channel} beigetreten",
"fr": "{channel} rejoint"
},
"part": {
"en": "Left {channel}",
@ -13,7 +14,8 @@
"na'vi": "Hum {channel}",
"cy": "Wedi gadael {channel}",
"nl": "{channel} verlaten",
"de": "{channel} verlassen"
"de": "{channel} verlassen",
"fr": "{channel} quitté"
},
"gpull": {
"en": "Git pulled that shit.",
@ -21,7 +23,8 @@
"na'vi": "Gìtìl fì'uti stamarsìm.",
"cy": "Wedi tynnu git yr cach na i gyd",
"nl": "Git heeft die zooi binnengehaald.",
"de": "Git hat es gezogen"
"de": "Git hat es gezogen",
"fr": "Git a pullé cette merde"
},
"reload": {
"en": "Reloaded that shit.",
@ -29,7 +32,8 @@
"na'vi": "Oel fìuti stìyeftxaw.",
"cy": "Ail-lwytho'r cach na",
"nl": "Die zooi opnieuw geladen.",
"de": "Neu geladen"
"de": "Neu geladen",
"fr": "Bordel rechargé"
},
"load_module": {
"en": "Loaded new module: {moduleName}",
@ -37,7 +41,8 @@
"na'vi": "Oel {moduleName}it amip stìyeftxaw.",
"cy": "Wedi llwytho modiwl newydd: {moduleName}",
"nl": "Nieuwe module geladen: {moduleName}",
"de": "Neues Modul geladen: {moduleName}"
"de": "Neues Modul geladen: {moduleName}",
"fr": "Nouveau module chargé : {moduleName}"
},
"unload_module": {
"en": "Turned off module: {moduleName}",
@ -45,7 +50,8 @@
"na'vi": "Oel {moduleName} tswìya'.",
"cy": "Wedi troi ffwrdd y modiwl: {moduleName}",
"nl": "Module uitgeschakeld: {moduleName}",
"de": "Modul ausgeschaltet: {moduleName}"
"de": "Modul ausgeschaltet: {moduleName}",
"fr": "Module déchargé : {moduleName}"
},
"unload_error": {
"en": "{moduleName} isn't loaded. Idiot.",
@ -53,7 +59,8 @@
"na'vi": "Oel {moduleName}it omum. Nga skxawng lu.",
"cy": "Di {moduleName} ddim wedi llwytho. Twpsyn",
"nl": "{moduleName} is niet geladen. Idioot.",
"de": "{moduleName} ist nicht geladen, du Idiot."
"de": "{moduleName} ist nicht geladen, du Idiot.",
"fr": "{moduleName} n'est pas chargé. Idiot."
},
"banned": {
"en": "{user} banned from {command}",
@ -61,7 +68,8 @@
"na'vi": "{command}ìri {user} ke tung.",
"cy": "{user} wedi ei gohurio o {command}",
"nl": "{user} mag {command} niet meer gebruiken",
"de": "{user} wurde von {command} gebannt"
"de": "{user} wurde von {command} gebannt",
"fr": "{user} a été interdit d'utiliser {command}"
},
"unbanned": {
"en": "{user} unbanned from {command}",
@ -69,7 +77,8 @@
"na'vi": "{command}ìri {user} tung set.",
"cy": "{user} wedi ei dad-wahardd o {command}",
"nl": "{user} mag {command} weer gebruiken",
"de": "{user} wurde von {command} entbannt"
"de": "{user} wurde von {command} entbannt",
"fr": "{user} peut de nouveau utiliser {command}"
},
"unban_error": {
"en": "{user} wasn't banned from that command, fool.",
@ -77,7 +86,8 @@
"na'vi": "{user} fìtsu'oti tamung srekrr, nga skxawng lu.",
"cy": "Nid oedd {user} wedi ei wahardd o'r gorchymyn yna, twpsyn",
"nl": "{user} mag dat commando sowieso al gebruiken, mafketel.",
"de": "{user} wurde nicht von {command} gebannt, du Trottel"
"de": "{user} wurde nicht von {command} gebannt, du Trottel",
"fr": "{user} n'a pas été interdit d'utiliser cette commande, imbécile."
},
"qlock": {
"en": "Locked quote category: {category}",
@ -85,84 +95,107 @@
"na'vi": "{category}ìri oel 'upxareti fmoli",
"cy": "Categori wedi cloi: {category}",
"nl": "Quote categorie vergrendeld: {category}",
"de": "Zitat-Kategorie geschlossen: {category}"
"de": "Zitat-Kategorie geschlossen: {category}",
"fr": "Catégorie de citations verrouillée : {category}"
},
"already_in_channel": {
"en": "I'm already in {channel}",
"na'vi": "Oel {channel}it tok li",
"cy": "Rydw i eisoes yn {channel}",
"nl": "Ik ben al in {channel}",
"de": "Ich bin schon in {channel}"
"de": "Ich bin schon in {channel}",
"fr": "Je suis déjà dans {channel}"
},
"not_in_channel": {
"en": "I'm not in {channel}",
"na'vi": "Oel {channel}it ke tok",
"cy": "Rydw i ddim yn {channel}",
"nl": "Ik ben niet aanwezig in {channel}",
"de": "Ich bin noch nicht in {channel}"
"de": "Ich bin noch nicht in {channel}",
"fr": "Je ne suis pas dans {channel}"
},
"already_loaded_web": {
"en": "WHY CAN'T I LOAD ALL THIS WEB? (web already loaded)",
"na'vi": "PELUN OEL KE TSUN OMUM FÌWETIT NÌWOTX (wetìri oe omum li)",
"cy": "PAM ALLA I DDIM YN LWYTHO POB Y WE? (We eisoes yn lwytho)",
"nl": "AL DIT WEB WORDT ME TOCH EEN BEETJE TE VEEL! (web is al geladen)",
"de": "WARUM KANN DAS NICHT GELADEN WERDEN? (bereits geladen)"
"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é)"
},
"already_loaded": {
"en": "{moduleName} is already loaded.",
"na'vi": "Oel omum teri {moduleName}it li.",
"cy": "{moduleName} eisoes yn lwytho",
"nl": "{moduleName} is al geladen.",
"de": "{moduleName} ist bereits geladen."
"de": "{moduleName} ist bereits geladen.",
"fr": "{moduleName} est déjà chargé."
},
"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."
"de": "Keine Informationen verfügbar oder gewünschtes Modul wurde noch nicht geladen.",
"fr": "Aucune information de version ou module demandé non chargé."
},
"status_good": {
"en": "{module} status: Shit looks good",
"cy": "{module} statws: Cachu yn edrych yn dda",
"de": "Sieht gut aus"
"de": "Sieht gut aus",
"fr": "Statut de {module} : Cette merde tourne bien"
},
"status_bad": {
"en": "{module} status: Failed to load: {reason}",
"cy": "{module} statws: Methu i lwytho: {reason}",
"de": "{module} Status: Konnte nicht geladen werden: {reason}"
"de": "{module} Status: Konnte nicht geladen werden: {reason}",
"fr": "Statut de {module} : échec de chargement : {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"
"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é"
},
"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}'."
"de": "Konnte {module} nicht laden. Siehe 'status {module}'.",
"fr": "Echec du chargement de {module}. Voir 'status {module}'."
},
"no_config_key": {
"en": "Config key doesn't exist bro",
"cy": "Nid yw allwedd cyfluniad yn bodoli, fy mrawd",
"de": "Einstellung existiert nicht, Bruder"
"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"
},
"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."
"de": "Einstellung ist ein Array, probiere '{alternate}' aus.",
"fr": "L'option de configuration est un array. Essaye '{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"
"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é."
},
"no_config_path": {
"en": "Config path doesn't exist bro",
"cy": "Nid yw llwybr cyfluniad yn bodoli, fy mrawd",
"de": "Konfigurationspfad nicht vorhanden, Bruder"
"de": "Konfigurationspfad nicht vorhanden, Bruder",
"fr": "Le chemin de configuration n'existe pas, fréro"
},
"new_config_key": {
"en": "Warning: Creating new config key: {key}.",
"fr": "Attention : création d'une nouvelle clé de configuration : {key}."
},
"config_keys_location": {
"en": "Config keys in {path}: {value}",
"cy": "Allweddi cyfluniad yn {path}: {value}",
"de": "Die Konfiguration in {path}: {value}"
"de": "Die Konfiguration in {path}: {value}",
"fr": "Clés de configuration dans {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}"
}
}

View File

@ -21,6 +21,17 @@ Command flow:
This is the only module which is force loaded, even if it's not specified in
the configuration file.
### Config
#### useNickserv: false
Use the nickserv module to ensure a user is logged into their account before
running any elevated commands. Note you will still have to load and configure
the nickserv module yourself.
#### accessOutput: false
Show a message to a user if they attempt to run a command they don't have the
access level for.
### Commands
#### ~usage [command]

View File

@ -2,22 +2,10 @@ var _ = require('underscore')._;
var api = function(dbot) {
return {
'isBanned': function(user, command) {
var banned = false;
if(_.has(dbot.db.bans, user)) {
if(_.include(dbot.db.bans[user], command) ||
_.include(dbot.db.bans[user], dbot.commands[command].module) ||
_.include(dbot.db.bans[user], '*')) {
banned = true;
}
}
return banned;
},
/**
* Does the user have the correct access level to use the command?
*/
'hasAccess': function(server, user, command, callback) {
'hasAccess': function(user, command, callback) {
var accessNeeded = dbot.commands[command].access;
if(accessNeeded == 'admin' || accessNeeded == 'moderator' || accessNeeded == 'power_user') {
@ -25,11 +13,11 @@ var api = function(dbot) {
if(accessNeeded == 'moderator') allowedNicks = _.union(allowedNicks, dbot.config.moderators);
if(accessNeeded == 'power_user') allowedNicks = _.union(allowedNicks, dbot.config.power_users);
if(!_.include(allowedNicks, user)) {
if(!_.include(allowedNicks, user.primaryNick)) {
callback(false);
} else {
if(_.has(dbot.modules, 'nickserv') && this.config.useNickserv == true) {
dbot.api.nickserv.auth(server, user, function(result) {
dbot.api.nickserv.auth(user.server, user.currentNick, function(result) {
callback(result);
});
} else {
@ -41,16 +29,6 @@ var api = function(dbot) {
}
},
/**
* Is item (user or channel) ignoring command?
*/
'isIgnoring': function(item, command) {
var module = dbot.commands[command].module;
return (_.has(dbot.db.ignores, item) &&
(_.include(dbot.db.ignores[item], module) ||
_.include(dbot.db.ignores[item], '*')));
},
/**
* Apply Regex to event message, store result. Return false if it doesn't
* apply.
@ -76,16 +54,6 @@ var api = function(dbot) {
applies = true;
}
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

@ -5,9 +5,8 @@
* ignoring that command.
*/
var _ = require('underscore')._;
var command = function(dbot) {
this.dbot = dbot;
/**
* Run the appropriate command given the input.
*/
@ -20,24 +19,23 @@ var command = function(dbot) {
return;
}
}
if(this.api.isBanned(event.user, commandName)) {
event.reply(dbot.t('command_ban', {'user': event.user}));
} else {
this.api.hasAccess(event.server, event.user, commandName, function(result) {
if(result) {
if(!this.api.isIgnoring(event.user, commandName) &&
!this.api.isIgnoring(event.channel, commandName) &&
dbot.commands[commandName].disabled !== true) {
this.api.hasAccess(event.rUser, commandName, function(hasAccess) {
dbot.api.ignore.isUserIgnoring(event.rUser, commandName, function(isIgnoring) {
dbot.api.ignore.isUserBanned(event.rUser, commandName, function(isBanned) {
if(isBanned) {
if(this.config.banOutput && commandName != '~') {
event.reply(dbot.t('command_ban', {'user': event.user}));
}
} else if(!hasAccess) {
if(this.config.accessOutput) {
event.reply(dbot.t('access_denied', { 'user': event.user }));
}
} else if(!isIgnoring && !dbot.commands[commandName].disabled) {
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);
}
} catch(err) {
if(dbot.config.debugMode == true) {
var stack = err.stack.split('\n').slice(1, dbot.config.debugLevel + 1);
@ -51,7 +49,7 @@ var command = function(dbot) {
});
}
}
dbot.api.event.emit('command', [ event ]);
if(!_.include(['~reload', '~load', '~unload'], commandName)) dbot.api.event.emit('command', [ event ]);
dbot.save();
} else {
if(commandName !== '~') {
@ -63,9 +61,9 @@ var command = function(dbot) {
}
}
}
}
}.bind(this));
}.bind(this));
}
}.bind(this));
}.bind(this);
this.on = 'PRIVMSG';
};

View File

@ -48,8 +48,8 @@ var commands = function(dbot) {
} else {
var helpLink = dbot.config.repoRoot +
'blob/master/modules/' + moduleName + '/README.md';
if(dbot.config[moduleName].help) {
helpLink = dbot.config[moduleName].help;
if(dbot.config.modules[moduleName].help) {
helpLink = dbot.config.modules[moduleName].help;
}
// TODO: Check it exists

View File

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

View File

@ -1,11 +1,12 @@
{
"command_ban": {
"en": "{user} is banned from using this command. Commence incineration.",
"es": "{user} está prohibido de usar esta instrucción. Comenzar incineración.",
"es": "{user} está prohibido de usar esta instrucción. urrently loaded modules now default: {modules}.",
"na'vi": "Tsu'ori {user} ke tung. Nga skxawng lu.",
"cy": "Mae {user} wedi ei gohurio gan ddefnyddio'r gorchymun yma. Cychwyn orfflosgiad",
"nl": "{user} mag dit commando niet meer gebruiken. Bereid het verbrandingsritueel voor.",
"de": "{user} wurde von diesem Befehl gebannt. Verbrennung einleiten"
"de": "{user} wurde von diesem Befehl gebannt. Verbrennung einleiten",
"fr": "{user} est interdit d'utiliser cette commande. Commencer l'incinération."
},
"syntax_error": {
"en": "Invalid syntax. Initiate incineration.",
@ -13,43 +14,54 @@
"na'vi": "Ngeyä pamrel keyawr lu. Nga skxawng lu.",
"cy": "Cystrawen annilys. Cychwyn orfflosgiad",
"nl": "Ongeldige syntax. Bereid het verbrandingsritueel voor.",
"de": "Syntax ungültig. Verbrennung einleiten"
"de": "Syntax ungültig. Verbrennung einleiten",
"fr": "Syntaxe invalide. Initier l'incinération."
},
"usage": {
"en": "Usage for {command}: {usage}.",
"na'vi": "Nga tsun sivar ìlä {command}: {usage}.",
"cy": "Defnydd o {command}: {usage}.",
"nl": "{command} wordt op de volgende manier gebruikt: {usage}.",
"de": "Benutzung von {command}: [usage}."
"de": "Benutzung von {command}: [usage}.",
"fr": "Utilisation de {command}: {usage}."
},
"no_usage_info": {
"en": "No usage information found for {command}.",
"na'vi": "Oel ke tsun sivar {comamnd}it",
"cy": "Ni chanfuwyd gwybodaeth am ddefnydd o {command}",
"nl": "Geen gebruiksinformatie gevonden voor {command}.",
"de": "Keine Gebrauchsanweisung gefunden für {command}."
"de": "Keine Gebrauchsanweisung gefunden für {command}.",
"fr": "Aucune information d'utilisation trouvée pour {command}"
},
"help_link": {
"en": "Help for {module}: {link}",
"na'vi": "{module}ä srungìl {link} it tok",
"cy": "Cymorth am {module}: {link}",
"nl": "Hulp voor {module}: {link}",
"de": "Hilfe für {module}: {link}"
"de": "Hilfe für {module}: {link}",
"fr": "Aide pour {module}: {link}"
},
"no_help": {
"en": "No help found for {module}.",
"na'vi": "Fì{module}ìri oel ke tsun run srungit",
"cy": "Ni chanfuwyd cymorth am {module}",
"nl": "Geen hulp gevonden voor {module}.",
"de": "Keine Hilfe gefunden für {module}."
"de": "Keine Hilfe gefunden für {module}.",
"fr": "Aucune aide trouvée pour {module}."
},
"loaded_modules": {
"en": "Loaded modules: {modules}.",
"cy": "Modiwlau sy'n lwythodd: {modules}.",
"nl": "Geladen modules: {modules}.",
"de": "Module geladen: {modules}."
"de": "Module geladen: {modules}.",
"fr": "Modules chargés: {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."
},
"module_commands": {
"en": "Commands in {module}: {commands}."
"en": "Commands in {module}: {commands}.",
"fr": "Commandes de {module}: {commands}."
}
}

View File

@ -1,4 +1,3 @@
{
"ignorable": true,
"help": "https://github.com/reality/depressionbot/blob/master/modules/ctcp/README.md"
"ignorable": true
}

View File

@ -1,8 +1,7 @@
{
"username": "youruserhere",
"password": "yourpasswordhere",
"dependencies": [ "command" ],
"dependencies": [ "link" ],
"ignorable": true,
"help": "https://github.com/reality/depressionbot/blob/master/modules/dent/README.md",
"dentQuotes": false
"dentQuotes": true
}

View File

@ -2,7 +2,6 @@ var request = require('request');
_ = require('underscore')._;
var dent = function(dbot) {
this.dbot = dbot;
this.StatusRegex = {
identica: /\bhttps?:\/\/identi\.ca\/notice\/(\d+)\b/ig,
twitter: /\bhttps?:\/\/twitter\.com\/\w+\/status\/(\d+)\b/ig
@ -15,8 +14,8 @@ var dent = function(dbot) {
this.api = {
'post': function(content) {
var username = dbot.config.dent.username,
password = dbot.config.dent.password,
var username = this.config.username,
password = this.config.password,
info,
auth = "Basic " +
new Buffer(username + ":" + password).toString("base64");
@ -57,8 +56,8 @@ var dent = function(dbot) {
this.commands['~dent'].regex = [/^~dent (.+)$/, 2];
this.onLoad = function() {
if(dbot.config.dent.dentQuotes === true && _.has(dbot.modules, 'quotes')) {
dbot.api.command.addHook('~qadd', function(key, text) {
if(this.config.dentQuotes === true && _.has(dbot.modules, 'quotes')) {
dbot.api.event.addHook('~qadd', function(key, text) {
if(text.indexOf('~~') == -1) {
this.api.post(key + ': ' + text);
}

View File

@ -1,3 +0,0 @@
{
"help": "https://github.com/reality/depressionbot/blob/master/modules/dns/README.md"
}

View File

@ -16,6 +16,7 @@ var dns = function(dbot) {
}
});
},
'~rdns': function(event) {
ip = event.params[1];
dnsmod.reverse(ip, function (error, domain) {

View File

@ -3,21 +3,25 @@
"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})"
"de": "{domain} ist \u000303VERFÜGBAR! \u000314({code})",
"fr": "{domain} est \u000303DISPONIBLE! \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})"
"de": "{domain} ist \u000305BELEGT! \u000314({address})",
"fr": "{domain} est \u000305PRIS! \u000314({address})"
},
"rdns": {
"en": "{ip} \u2192 {domain}"
"en": "{ip} \u2192 {domain}",
"fr": "{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})"
"de": "Kann {ip} nicht auflösen. \u000314({error})",
"fr": "Impossible de rechercher {ip}. \u000314({error})"
}
}

View File

@ -1,3 +0,0 @@
{
"help": "https://github.com/reality/depressionbot/blob/master/modules/event/README.md"
}

View File

@ -1,3 +0,0 @@
{
"help": "https://github.com/reality/depressionbot/blob/master/modules/flashy/README.md"
}

@ -1 +0,0 @@
Subproject commit 5f4e3dc8335000e97af528fee289b880a3c99e81

18
modules/github/LICENSE Normal file
View File

@ -0,0 +1,18 @@
Copyright (c) 2013 Douglas Gardner <douglas@chippy.ch>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

28
modules/github/README.md Normal file
View File

@ -0,0 +1,28 @@
## Github
Grabs interesting data from the GitHub API.
### Description
This module for [depressionbot](https://github.com/reality/depressionbot) takes some interesting information about Github and parses it in a pleasing manner.
### Configuration
#### defaultrepo
When repository information is lacking from the command, this repository will be used.
#### sortorder
Defines the behaviour of ~issue when no arguments are given. Options are ``created``, ``updated``, or ``comments``.
### Commands
#### ~commits
Returns the number of commits in the repository of the current depressionbot instance.
#### ~gstatus
Returns the [current status of Github](https://status.github.com), and a message explaining the current state of affairs.
#### ~issue (user/repo) [id]
Gives information about the isse pecified, from the default repository if one is not explicitly stated.
#### ~milestone [milestone name]
Returns milestone progress for any given milestone, with a link to the milestone in question.
#### ~repo (repo name)
Returns information about the repo given as a parameter. The repo should be specified as ``user/name``; for example, ``twitter/snowflake``.
#### ~repocount [user]
Returns the number of public Github repositories for the specified user.
### Dependencies
* [request](https://github.com/mikeal/request/):``$ npm install request``

View File

@ -0,0 +1,8 @@
{
"dependencies": [ "command" ],
"ignorable": true,
"help": "http://github.com/zuzak/dbot-github/blob/master/README.md",
"defaultrepo": "reality/dbot",
"sortorder": "updated",
"useragent": "reality/depressionbot github module"
}

200
modules/github/github.js Normal file
View File

@ -0,0 +1,200 @@
/**
* 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;
request({method: 'POST', uri: 'http://git.io', form:{url: longurl}}, function(error, response, body){
event.reply(dbot.t('location')+" "+response.headers["location"]);
});
});
},
'~gstatus': function(event) {
data = this.api.githubStatus(function(data){
console.log(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";
var longurl = "http://github.com/" + repo + "/issues?milestone=" + milestone["number"];
request({method: 'POST', uri: 'http://git.io', form:{url: longurl}}, function(error, response, body){
event.reply(response.headers["location"]);
});
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 (data["pull_request"]["html_url"]){
console.log(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

@ -0,0 +1,81 @@
{
"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)."
},
"statusgood": {
"en": "\u000309Shit's fine",
"cy": "\u000309Cachu'n ddirwy",
"de": "\u000309Alles in Ordnung",
"fr": "\u000309Cette merde tourne bien"
},
"statusminor": {
"en": "\u000308Shit's touchy",
"cy": "\u000308Cachu'n fregus",
"de": "\u000308Kleinere Probleme vorhanden",
"fr": "\u000308Cette merde a un petit problème"
},
"statusmajor": {
"en": "\u000304Shit's fucked:",
"cy": "\u000304Cachu wedi cyrraedd y ffan:",
"de": "\u000304Du bist am Arsch",
"fr": "\u000304Cette merde est foutue : "
},
"location": {
"en": "You can find that shit at:",
"cy": "Gallwch ddod o hyd y cachu yn:",
"de": "Kann nicht gefunden werden:",
"fr": "Tu peux trouver cette merde ici : "
},
"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]"
},
"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]"
},
"usernotfound": {
"en": "User not found.",
"cy": "Defnyddiwr heb ei ganfod.",
"de": "Benutzer nicht gefunden.",
"fr": "Utilisateur non trouvé."
},
"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."
},
"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}"
},
"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 überstellt.",
"fr": "Mon code a été modifié {count} fois."
},
"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})."
},
"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}."
}
}

49
modules/ignore/api.js Normal file
View File

@ -0,0 +1,49 @@
var _ = require('underscore')._;
var api = function(dbot) {
return {
// Is user ignoring command/module?
'isUserIgnoring': function(user, item, callback) {
this.internalAPI.isUserImpeded(user, item, 'ignores', callback);
},
// Is user banned from command/module?
'isUserBanned': function(user, item, callback) {
this.internalAPI.isUserImpeded(user, item, 'bans', callback);
},
// Is channel ignoring module?
// TODO: Command support
'isChannelIgnoring': function(channelName, item, callback) {
var isIgnoring = false,
channel = false;
this.db.search('channel_ignores', {
'server': server,
'name': channel
}, function(result) {
channel = result;
}, function(err) {
if(!err && channel && _.include(channel.ignores, item)) {
isIgnoring = true;
}
callback(isIgnoring);
});
},
// Resolve a nick and return their user and ignores object
'getUserIgnores': function(user, callback) {
this.db.read('ignores', user.id, function(err, ignores) {
if(!err && ignores) {
callback(false, ignores);
} else {
callback(true, null);
}
});
}
};
}
exports.fetch = function(dbot) {
return api(dbot);
};

View File

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

View File

@ -5,15 +5,35 @@
* this information, since that actually performs the ignorance. Also provides
* 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) {
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)) {
item = dbot.commands[item].module;
}
if(_.include(ignores[by], item)) {
isImpeded = true;
}
}
callback(isImpeded);
});
}.bind(this)
};
var commands = {
'~ignore': function(event) {
var module = event.params[1];
var ignorableModules = _.chain(dbot.modules)
.filter(function(module, name) {
return dbot.config[module].ignorable === true;
return dbot.config.modules[module].ignorable === true;
})
.pluck('name')
.value();
@ -25,21 +45,30 @@ var ignore = function(dbot) {
}));
} else {
if(module == '*' || _.include(ignorableModules, module)) {
if(_.has(dbot.db.ignores, event.user) && _.include(dbot.db.ignores[event.user], module)) {
event.reply(dbot.t('already_ignoring', { 'user': event.user }));
} else {
if(_.has(dbot.db.ignores, module)) {
dbot.db.ignores[event.user].push(module);
} else {
dbot.db.ignores[event.user] = [module];
this.api.getUserIgnores(event.rUser, function(err, ignores) {
if(!ignores) {
ignores = {
'id': event.rUser.id,
'ignores': [],
'bans': []
};
}
dbot.instance.ignoreTag(event.user, module);
event.reply(dbot.t('ignored', {
'user': event.user,
'module': module
}));
}
if(!_.include(ignores.ignores, module)) {
ignores.ignores.push(module);
this.db.save('ignores', event.rUser.id, ignores, function(err) {
if(!err) {
dbot.instance.ignoreTag(event.user, module);
event.reply(dbot.t('ignored', {
'user': event.user,
'module': module
}));
}
});
} else {
event.reply(dbot.t('already_ignoring', { 'user': event.user }));
}
}.bind(this));
} else {
event.reply(dbot.t('invalid_ignore', { 'user': event.user }));
}
@ -47,137 +76,192 @@ var ignore = function(dbot) {
},
'~unignore': function(event) {
var ignoredModules = [];
if(_.has(dbot.db.ignores, event.user)) {
ignoredModules = dbot.db.ignores[event.user];
}
var module = event.params[1];
if(_.isUndefined(module)) {
event.reply(dbot.t('unignore_usage', {
'user': event.user,
'modules': ignoredModules.join(', ')
}));
} else {
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
}));
this.api.getUserIgnores(event.rUser, function(err, ignores) {
if(err || !ignores || _.isUndefined(module)) {
if(ignores) {
event.reply(dbot.t('unignore_usage', {
'user': event.user,
'modules': ignores.ignores.join(', ')
}));
} else {
event.reply(dbot.t('empty_unignore_usage', {
'user': event.user
}));
}
} else {
event.reply(dbot.t('invalid_unignore', { 'user': event.user }));
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 user = event.params[1];
var module = event.params[2];
var nick = event.input[1],
item = event.input[2];
if(_.isUndefined(user) || _.isUndefined(module)) {
event.reply(dbot.t('ban_usage', {'user': event.user}));
return;
}
if(module == '*' || _.include(dbot.config.moduleNames, module) || _.include(dbot.commands, module)) {
if(_.has(dbot.db.bans, user) && _.include(dbot.db.bans[user], module)) {
event.reply(dbot.t('already_banned', {
'user': event.user,
'banned': user
}));
return;
}
if(module == '*' || _.include(dbot.config.moduleNames, item) || _.include(dbot.commands, item)) {
dbot.api.users.resolveUser(event.server, nick, function(user) {
this.api.getUserIgnores(user, function(err, ignores) {
if(!err) {
if(!ignores) {
ignores = {
'id': user.id,
'ignores': [],
'bans': []
};
}
if(_.has(dbot.db.bans, event.params[1])) {
dbot.db.bans[event.params[1]].push(module);
} else {
dbot.db.bans[event.params[1]] = [module];
}
event.reply(dbot.t('banned_success', {
'user': event.user,
'banned': user,
'module': module
}));
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));
});
} else {
event.reply(dbot.t('invalid_ban', {'user': event.user}));
event.reply(dbot.t('invalid_ban', { 'user': event.user }));
}
},
'~unban': function(event) {
var bannedModules = [];
var nick = event.input[1],
item = event.input[2];
var user = event.params[1];
var module = event.params[2];
if(_.isUndefined(user) || _.isUndefined(module)) {
event.reply(dbot.t('unban_usage', {'user': event.user}));
} else {
if(_.has(dbot.db.bans, user) && _.include(dbot.db.bans[user], module)) {
dbot.db.bans[user].splice(dbot.db.bans[user].indexOf(module), 1);
event.reply(dbot.t('unbanned_success', {
'user': event.user,
'banned': user,
'module': module
}));
} else {
event.reply(dbot.t('invalid_unban', {
'user': event.user,
'banned': user
}));
}
}
dbot.api.users.resolveUser(event.server, nick, function(user) {
this.api.getUserIgnores(user, function(err, ignores) {
if(err || !ignores) {
event.reply(dbot.t('invalid_unban', {
'user': event.user,
'banned': nick
}));
} else {
if(_.include(ignores.bans, item)) {
ignores.bans = _.without(ignores.bans, item);
this.db.save('ignores', user.id, ignores, function(err) {
event.reply(dbot.t('unbanned_success', {
'user': event.user,
'banned': nick,
'module': item
}));
});
} else {
event.reply(dbot.t('invalid_unban', {
'user': event.user,
'banned': nick
}));
}
}
}.bind(this));
}.bind(this));
},
'~ignorechannel': function(event) {
var channel = ((event.params[1] == '@') ? event.channel.name : event.params[1]);
var module = event.params[2];
var channelName = event.input[1],
module = event.input[2];
// Ignoring the value of 'ignorable' at the moment
if(module == '*' || _.include(dbot.config.moduleNames, module)) {
if(!_.has(dbot.db.ignores, channel)) dbot.db.ignores[channel] = [];
if(!_.include(dbot.db.ignores[channel], module)) {
dbot.db.ignores[channel].push(module);
dbot.instance.ignoreTag(channel, module);
event.reply(dbot.t('ignoring_channel', {
'module': module,
'channel': channel
}));
} else {
event.reply(dbot.t('already_ignoring_channel', {
'module': module,
'channel': channel
}));
}
var channel = false;
this.db.search('channel_ignores', {
'server': event.server,
'name': channelName
}, function(result) {
channel = result;
}, function(err) {
if(!channel) {
var id = uuid.v4();
channel = {
'id': id,
'server': event.server,
'name': channelName,
'ignores': []
};
}
if(!_.include(channel.ignores, module)) {
channel.ignores.push(module);
this.db.save('channel_ignores', channel.id, channel, function(err) {
dbot.instance.ignoreTag(channel, module);
event.reply(dbot.t('ignoring_channel', {
'module': module,
'channel': channelName
}));
});
} else {
event.reply(dbot.t('already_ignoring_channel', {
'module': module,
'channel': channelName
}));
}
}.bind(this));
} else {
event.reply(dbot.t('module_not_exist', { 'module': module }));
}
},
'~unignorechannel': function(event) {
var channel = ((event.params[1] == '@') ? event.channel.name : event.params[1]);
var module = event.params[2];
var channelName = event.input[1],
module = event.input[2],
channel = false;
if(!_.has(dbot.db.ignores, channel)) dbot.db.ignores[channel] = [];
if(_.include(dbot.db.ignores[channel], module)) {
dbot.db.ignores[channel] = _.without(dbot.db.ignores[channel], module);
dbot.instance.removeIgnore(channel, module);
event.reply(dbot.t('unignoring_channel', {
'module': module,
'channel': channel
}));
} else {
event.reply(dbot.t('not_ignoring_channel', {
'module': module,
'channel': channel
}));
}
this.db.search('channel_ignores', {
'server': event.server,
'name': channelName
}, function(result) {
channel = result;
}, function(err) {
if(channel && _.include(channel.ignores, module)) {
channel.ignores = _.without(channel.ignores, module);
this.db.save('channel_ignores', channel.id, channel, function(err) {
dbot.instance.removeIgnore(channel, module);
event.reply(dbot.t('unignoring_channel', {
'module': module,
'channel': channelName
}));
});
} else {
event.reply(dbot.t('not_ignoring_channel', {
'module': module,
'channel': channelName
}));
}
}.bind(this));
}
};
commands['~ban'].regex = [/^~ban ([^ ]+) ([^ ]+)$/, 3];
commands['~unban'].regex = [/^~unban ([^ ]+) ([^ ]+)$/, 3];
commands['~ignorechannel'].regex = [/^~ignorechannel ([^ ]+) ([^ ]+)$/, 3];
commands['~unignorechannel'].regex = [/^~unignorechannel ([^ ]+) ([^ ]+)$/, 3];
commands['~ban'].access = 'moderator';
commands['~unban'].access = 'moderator';
commands['~ignorechannel'].access = 'moderator';
@ -187,11 +271,22 @@ var ignore = function(dbot) {
this.onLoad = function() {
dbot.instance.clearIgnores();
_.each(dbot.db.ignores, function(ignores, item) {
_.each(ignores, function(ignore) {
dbot.instance.ignoreTag(item, ignore);
}, this);
}, this);
this.db.scan('ignores', function(ignores) {
dbot.api.users.getUser(ignores.id, function(user) {
if(user) {
_.each(ignores.ignores, function(module) {
dbot.instance.ignoreTag(user.primaryNick, module);
});
}
});
}, function(err) { });
this.db.scan('channel_ignores', function(channel) {
_.each(channel.ignores, function(module) {
dbot.instance.ignoreTag(channel, module);
});
}, function(err) { });
};
};

View File

@ -5,7 +5,8 @@
"na'vi": "{user}: Sar: ~ignore ['u]. U, nga ke tìng mikyun: {modules}.",
"cy": "{user}: Defnydd: ~ignore [modiwl]. Modiwlau a allech anwybyddu yw: {modules}.",
"nl": "{user}: Gebruik: ~ignore [module]. Modules die negeert kunnen worden zijn: {modules}.",
"de": "{user}: Benutzung: ~ignore [module]. Module, die ignoriert werden können: {modules}."
"de": "{user}: Benutzung: ~ignore [module]. Module, die ignoriert werden können: {modules}.",
"fr": "{user}: Utilisation: ~ignore [module]. Les modules que vous pouvez ignorer sont: {modules}."
},
"already_ignoring": {
"en": "{user}: You're already ignoring that module.",
@ -13,7 +14,8 @@
"na'vi": "{user}: 'uri nga ke tìng mikyun srekrr.",
"cy": "{user}: Mi rwyt ti'n anwybyddu'r modiwl yna'n barod.",
"nl": "{user}: Je negeert deze module al.",
"de": "{user}: Dieses Modul wird bereits ignoriert."
"de": "{user}: Dieses Modul wird bereits ignoriert.",
"fr": "{user}: Vous ignorez déjà ce module."
},
"ignored": {
"en": "{user}: Now ignoring {module}.",
@ -21,7 +23,8 @@
"na'vi": "{user}: Nga ke terìng mikyun {module}ne set.",
"cy": "{user}: Nawr yn anwybyddu {module}",
"nl": "{user}: {module} wordt nu genegeerd.",
"de": "{user}: {module} wird nun ignoriert."
"de": "{user}: {module} wird nun ignoriert.",
"fr": "{user}: {module} désormais ignoré."
},
"invalid_ignore": {
"en": "{user}: That isn't a valid module name.",
@ -29,7 +32,8 @@
"na'vi": "{user}: Tsatstxo eyawr ke lu.",
"cy": "{user}: Nid oedd hwna'n modiwl dilys",
"nl": "{user}: Dat is geen geldige modulenaam.",
"de": "{user}: Dies ist nicht der Name eines Moduls."
"de": "{user}: Dies ist nicht der Name eines Moduls.",
"fr": "{user}: Ceci ne correspond pas à un nom de module valide."
},
"unignore_usage": {
"en": "{user}: Usage: ~unignore [module]. Modules you are currently ignoring: {modules}.",
@ -37,7 +41,17 @@
"na'vi": "{user}: Sar: ~unignore ['u]. Uri, nga ke terìng mikyun: {modules}.",
"cy": "{user}: Defnydd: ~unignore [modiwl]. Modiwlau rydech yn anwybyddu ar hyn o bryd: {modules}",
"nl": "{user}: Gebruik: ~unignore [module]. Modules die momenteel worden genegeerd: {modules}.",
"de": "{user}: Benutzung: ~unignore [module]. Module, die im Moment ignoriert werden: {modules}."
"de": "{user}: Benutzung: ~unignore [module]. Module, die im Moment ignoriert werden: {modules}.",
"fr": "{user}: Utilisation: ~unignore [module]. Modules que vous ignorez actuellement: {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]."
},
"invalid_unignore": {
"en": "{user}: You're not ignoring that module or it doesn't exist.",
@ -45,7 +59,8 @@
"na'vi":"{user}: Nga terìng mikyun fu fì'ul fìtsengit ke tok.",
"cy": "{user}: Nid wyt ti'n anwybyddu'r modiwl yna neu nid yw e'n bodoli",
"nl": "{user}: Deze module bestaat niet of wordt niet genegeerd.",
"de": "{user}: Dieses Modul wird entweder nicht ignoriert oder existiert nicht."
"de": "{user}: Dieses Modul wird entweder nicht ignoriert oder existiert nicht.",
"fr": "{user}: Vous n'ignorez pas ce module ou il n'existe pas."
},
"unignored": {
"en": "{user}: No longer ignoring {module}.",
@ -53,83 +68,96 @@
"na'vi": "{user}: Nga terìng mikyun {module}ne set",
"cy": "{user}: Ddim yn anwybyddu {module} bellach",
"nl": "{user}: {module} wordt niet langer genegeerd.",
"de": "{user}: {module} wird nicht länger ignoriert."
"de": "{user}: {module} wird nicht länger ignoriert.",
"fr": "{user}: {module} n'est plus ignoré à présent."
},
"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."
"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."
},
"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."
"de": "{user}: {banned} ist bereits von diesem Modul gebannt.",
"fr": "{user}: {banned} est déjà interdit d'utiliser ce module."
},
"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."
"de": "{user}: {banned} ist nun von {module} gebannt.",
"fr": "{user}: {banned} est maintenant interdit d'utiliser {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 nicht der Name eines Moduls."
"de": "{user}: Dies ist nicht der Name eines Moduls.",
"fr": "{user}: Ceci n'est pas un nom de module valide."
},
"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]."
"de": "{user}: Benutzung: ~unban [Benutzer] [module].",
"fr": "{user}: Utilisation: ~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."
"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."
},
"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."
"de": "{user}: {banned} wurde von {module} entbannt.",
"fr": "{user}: {banned} n'est plus interdit d'utiliser {module}."
},
"ignoring_channel": {
"en": "Now ignoring {module} in {channel}.",
"na'vi": "Oe ke stayawm {module}ur mì {channel}",
"cy": "Bellach yn anwybyddu {module} yn {channel}.",
"nl": "{module} wordt nu genegeerd in {channel}.",
"de": "{module} in [channel} wird nun ignoriert."
"de": "{module} in {channel} wird nun ignoriert.",
"fr": "{module} dans {channel} maintenant ignoré."
},
"already_ignoring_channel": {
"en": "Already ignoring {module} in {channel}.",
"na'vi": "Oe ke stayawm {module}ur mì {channel} li",
"cy": "Eisoes anwybyddu {module} yn {channel}",
"nl": "{module} wordt al genegeerd in {channel}.",
"de": "{module} in {channel} wird bereits ignoriert."
"de": "{module} in {channel} wird bereits ignoriert.",
"fr": "{module} dans {channel} déjà ignoré."
},
"module_not_exist": {
"en": "{module} isn't loaded or doesn't exist.",
"na'vi": "Oel ke omum teri {module}it fu {module} ke fkeytok",
"cy": "Dydy {module} ddim yn lwythodd, neu ddim yn bodoli.",
"nl": "{module} is niet geladen of bestaat niet.",
"de": "{module} ist nicht geladen oder existiert nicht."
"de": "{module} ist nicht geladen oder existiert nicht.",
"fr": "{module} n'est pas chargé ou n'existe pas."
},
"unignoring_channel": {
"en": "No longer ignoring {module} in {channel}.",
"na'vi": "Oel stayawm {module}ur mì {channel} set.",
"cy": "Nid anwybyddu {module} yn {channel} mwyach.",
"nl": "{module} wordt niet meer genegeerd in {channel}.",
"de": "{module} in {channel} wird nicht länger ignoriert."
"de": "{module} in {channel} wird nicht länger ignoriert.",
"fr": "{module} dans {channel} n'est plus ignoré à présent."
},
"not_ignoring_channel": {
"en": "{module} wasn't being ignored in {channel}.",
"na'vi": "Oel stayawm {module}ur mì {channel} li.",
"cy": "Nid yw {module} yn cael ei anwybyddu yn {channel}.",
"nl": "{module} werd niet genegeerd in {channel}.",
"de": "{module} wurde in {channel} nicht ignoriert."
"de": "{module} wurde in {channel} nicht ignoriert.",
"fr": "{module} n'était pas ignoré dans {channel}."
}
}

View File

@ -1,6 +1,6 @@
{
"dbKeys": [ "imgur" ],
"dependencies": [ "web", "api" ],
"dependencies": [ "web", "api", "link" ],
"imagelength": 5,
"nsfwwarn": true,
"apikey": "86fd3a8da348b65",

View File

@ -8,7 +8,6 @@ var _ = require('underscore')._,
crypto = require('crypto');
var imgur = function(dbot) {
this.db = dbot.db.imgur;
this.internalAPI = {
'infoString': function(imgData) {
info = '';
@ -77,21 +76,21 @@ var imgur = function(dbot) {
var testUrl = 'http://i.imgur.com/' +
testSlug +
'.' + ext[_.random(0, ext.length - 1)];
this.db.totalHttpRequests += 1;
dbot.db.imgur.totalHttpRequests += 1;
var image = request(testUrl, function(error, response, body) {
// 492 is body.length of a removed image
if(!error && response.statusCode == 200 && body.length != 492) {
this.db.totalImages += 1;
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.imgur.autoadd,hash)){
if(_.has(dbot.config.modules.imgur.autoadd,hash)){
var category = dbot.config.imgur.autoadd[hash];
if (_.contains(category, testUrl)){
// there's probably less than 62^5 chance of this happening
} else {
if(!_.has(dbot.db.quoteArrs, category)) dbot.db.quoteArrs[category] = [];
dbot.db.quoteArrs[category].push(testUrl);
dbot.api.quotes.addQuote(category, testUrl,
dbot.config.name, function() { });
}
}
}
@ -113,10 +112,10 @@ var imgur = function(dbot) {
'url': 'https://api.imgur.com/3/image/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + dbot.config.imgur.apikey
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
this.db.totalApiRequests += 1;
dbot.db.imgur.totalApiRequests += 1;
callback(body);
}.bind(this));
},
@ -126,7 +125,7 @@ var imgur = function(dbot) {
'url': 'https://api.imgur.com/3/album/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + dbot.config.imgur.apikey
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
this.db.totalApiRequests += 1;
@ -139,7 +138,7 @@ var imgur = function(dbot) {
'url': 'https://api.imgur.com/3/gallery/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + dbot.config.imgur.apikey
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
this.db.totalApiRequests += 1;
@ -197,6 +196,7 @@ var imgur = function(dbot) {
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);
};

View File

@ -4,9 +4,11 @@
"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"
"de": "Könnte 18+ Material enthalten",
"fr": "peut être risqué pour le travail (NSFW)"
},
"imgurinfo": {
"en": "[{info}]"
"en": "[{info}]",
"fr": "[{info}]"
}
}

View File

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

View File

@ -21,6 +21,16 @@ var js = function(dbot) {
// Run JS code un-sandboxed, with access to DBot memory (admin-only).
'~ajs': function(event) {
var callback = function() {
var args = Array.prototype.slice.call(arguments);
for(var i=0;i<args.length;i++) {
var arg = args[i];
if(_.isObject(arg) && !_.isArray(arg)) {
arg = '[object Object]: ' + _.keys(arg).join(', ');
}
event.reply('Callback[' + i + ']: ' + arg);
}
};
var ret = eval(event.input[1]);
if(ret !== undefined) {
event.reply(ret);

View File

@ -91,16 +91,13 @@ var commands = function(dbot) {
timeout = event.input[1],
banee = event.input[2],
reason = event.input[3],
adminChannel = this.config.admin_channel[event.server];
adminChannel = dbot.config.servers[server].admin_channel,
channels = dbot.config.servers[server].channels,
network = event.server;
if(this.config.network_name[event.server]) {
network = this.config.network_name[event.server];
}
console.log(timeout);
console.log(banee);
console.log(reason);
dbot.api.nickserv.getUserHost(event.server, banee, function(host) {
// Add host record entry
@ -148,34 +145,37 @@ var commands = function(dbot) {
// Add qutoe category documenting ban
if(this.config.document_bans && _.has(dbot.modules, 'quotes')) {
dbot.db.quoteArrs['ban_' + banee.toLowerCase()] = [ quoteString ];
dbot.api.quotes.addQuote('ban_' + banee.toLowerCase(),
quoteString, banee, function() {});
notifyString += ' ' + dbot.t('quote_recorded', { 'user': banee });
}
// Notify moderators, banee
if(this.config.admin_channel[event.server]) {
if(!_.isUndefined(adminChannel)) {
channels = _.without(channels, adminChannel);
} else {
adminChannel = event.channel.name;
}
dbot.api.report.notify(server, adminChannel, notifyString);
dbot.say(event.server, adminChannel, notifyString);
dbot.api.report.notify(server, adminChannel, notifyString);
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
}));
}
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
}));
}
// Ban the user from all channels

View File

@ -1,12 +1,8 @@
{
"dbKeys": [ "kicks", "kickers", "hosts", "tempBans" ],
"dependencies": [ "command", "report", "users" ],
"help": "http://github.com/reality/depressionbot/blob/master/modules/kick/README.md",
"ignorable": true,
"countSilently": true,
"admin_channel": {
"aberwiki": "#fishbox"
},
"network_name": {
"aberwiki": "OAOSIDL"
},

View File

@ -28,22 +28,24 @@ var kick = function(dbot) {
'networkUnban': function(server, unbanee, unbanner, callback) {
var channels = dbot.config.servers[server].channels,
network = this.config.network_name[server] || server,
adminChannel = this.config.admin_channel[server];
adminChannel = dbot.config.servers[server].admin_channel;
if(_.has(this.hosts, server) && _.has(this.hosts[server], unbanee)) {
var host = this.hosts[server][unbanee];
// Notify Staff
if(!_.isUndefined(adminChannel)) {
var notifyString = dbot.t('nunbanned', {
'network': network,
'unbanee': unbanee,
'unbanner': unbanner
});
dbot.api.report.notify(server, adminChannel, notifyString);
dbot.say(server, adminChannel, notifyString);
if(_.isUndefined(adminChannel)) {
adminChannel = event.channel.name;
}
var notifyString = dbot.t('nunbanned', {
'network': network,
'unbanee': unbanee,
'unbanner': unbanner
});
dbot.api.report.notify(server, adminChannel, notifyString);
dbot.say(server, adminChannel, notifyString);
// Notify Unbanee
dbot.say(server, unbanee, dbot.t('nunban_notify', {
'network': network,
@ -74,7 +76,7 @@ var kick = function(dbot) {
} else {
callback(true); // No host could be found
}
});
}.bind(this));
}
}
};

View File

@ -5,16 +5,20 @@
"na'vi": "Tuteol {user}it tsrame'i {kicks} hìmtxan ulte sute tsrame'i {kicked} hìmtxan.",
"cy": "Cafwyd {user} ei gicio {kicks} gwaith ac wedi cicio pobl {kicked} gwaith.",
"nl": "{user} is {kicks} keer gekickt en heeft anderen {kicked} keer gekickt.",
"de": "{user} wurde {kicks} mal gekickt und hat {kicked} mal andere Benutzer gekickt."
"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."
},
"quieted": {
"en": "Quieted {quietee}. Remember: don't be a coconut."
"en": "Quieted {quietee}. Remember: don't be a coconut.",
"fr": "{quietee} a été rendu silencieux. Rappelle-toi : ne sois pas têtu."
},
"tquieted": {
"en": "Quieted {quietee} for {minutes} minutes. Remember: don't be a coconut."
"en": "Quieted {quietee} for {minutes} minutes. Remember: don't be a coconut.",
"fr": "{quietee} a été rendu silencieux pour {minutes} minutes. Rappelle-toi : ne sois pas têtu."
},
"unquieted": {
"en": "Unquieted {quietee}. Remember: don't be a coconut."
"en": "Unquieted {quietee}. Remember: don't be a coconut.",
"fr": "{quietee} peut maintenant parler. Rappelle-toi : ne sois pas têtu."
},
"kicked_dbot": {
"en": "Thou shalt not kick {botname}",
@ -22,52 +26,65 @@
"na'vi": "Ngal {botname}it ke tsun tsrive'i",
"cy": "Ni ddylech cicio {botname}",
"nl": "Gij zult {botname} niet kicken",
"de": "Du sollst {botname} nicht kicken"
"de": "Du sollst {botname} nicht kicken",
"fr": "Tu ne kickeras pas {botname}"
},
"ckicked": {
"en": "Attention: {kicker} has kicked {kickee} from {channel}. 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}.\""
"de": "Achtung: {kicker} hat {kickee} von {channel} verwiesen. Grund: \"{reason}.\"",
"fr": "Attention : {kicker} a kické {kickee} de {channel}. Raison donnée : \"{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}.\""
"de": "Achtung: {banner} hat {banee} von {channel} gebannt. Grund: \"{reason}.\"",
"fr": "Attention : {banner} a banni {banee} de {channel}. Raison donnée : \"{reason}.\""
},
"tbanned": {
"en": "Attention: {banner} has banned {banee} from the {network} network for {hours} hours. The reason given was \"{reason}.\""
"en": "Attention: {banner} has banned {banee} from the {network} network for {hours} hours. The reason given was \"{reason}.\"",
"fr": "Attention : {banner} a banni {banee} du réseau {network} pour {hours} heures. Raison donnée : \"{reason}.\""
},
"tbanned_notify": {
"en": "You have been banned from the {network} network for {hours} hours by {banner}. The reason given was \"{reason}.\" You can join {admin_channel} for more information or to discuss the ban."
"en": "You have been banned from the {network} network for {hours} hours by {banner}. The reason given was \"{reason}.\" You can join {admin_channel} for more information or to discuss the ban.",
"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."
},
"nbanned": {
"en": "Attention: {banner} has banned {banee} 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}.\""
"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}.\""
},
"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."
"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.",
"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."
},
"nban_quote": {
"en": "{banee} was banned from the network by {banner} on {time}. The reason given was \"{reason}.\"",
"cy": "Cafodd {banee} ei wahardd o'r rhwydwaith gan {banner} ar {time}. Y rheswm a roddwyd oedd: \"{reason}.\"",
"de": "{banee} wurde von {banner} auf dem gesamten Netzwerk um {time} gebannt. Grund: \"{reason}.\""
"de": "{banee} wurde von {banner} auf dem gesamten Netzwerk um {time} gebannt. Grund: \"{reason}.\"",
"fr": "{banee} a été banni du réseau par {banner} à {time}. Raison donnée : \"{reason}.\""
},
"no_user": {
"en": "{user} doesn't seem to be online on this server."
"en": "{user} doesn't seem to be online on this server.",
"fr": "{user} ne semble pas être connecté à ce serveur."
},
"nunbanned": {
"en": "Attention: {unbanee} has been unbanned from the {network} network by {unbanner}."
"en": "Attention: {unbanee} has been unbanned from the {network} network by {unbanner}.",
"fr": "Attention : {unbanee} a été débanni du réseau {network} par {unbanner}."
},
"nunban_notify": {
"en": "You have been unbanned from the {network} network by {unbanner}."
"en": "You have been unbanned from the {network} network by {unbanner}.",
"fr": "Vous avez été débanni du réseau {network} par {unbanner}."
},
"nunban_error": {
"en": "It appears {unbanee} was not banned using the ~nban command."
"en": "It appears {unbanee} was not banned using the ~nban command.",
"fr": "Il semble que {unbanee} n'a pas été banni en utilisant la commande ~nban."
},
"quote_recorded": {
"en": "This has been recorded in ~ban_{user}.",
"cy": "Mae hyn wedi cael ei gofnodi yn ~ban_{user}.",
"de": "Dies wurde in ~ban_{user} aufgenommen."
"de": "Dies wurde in ~ban_{user} aufgenommen.",
"fr": "Cela a été enregistré dans ~ban_{user}."
}
}

View File

@ -1,6 +1,5 @@
{
"autoTitle": false,
"dependencies": [ "command" ],
"autoTitle": true,
"ignorable": true,
"outputPrefix": "\u00032link\u000f",
"help": "http://github.com/reality/depressionbot/blob/master/modules/link/README.md"

View File

@ -126,7 +126,7 @@ var link = function(dbot) {
var urlMatches = event.message.match(this.urlRegex);
if(urlMatches !== null) {
this.links[event.channel.name] = urlMatches[0];
if(dbot.config.link.autoTitle == true) {
if(this.config.autoTitle == true) {
var handlerFound = false;
for(var i=0;i<this.handlers.length;i++) {
var matches = this.handlers[i].regex.exec(urlMatches[0]);

View File

@ -4,19 +4,23 @@
"na'vi": "Oel ke tsun run 'upxare atxin.",
"cy": "Ni chanfuwyd teitl y dudalen",
"nl": "Er is geen paginatitel gevonden.",
"de": "Kein Seitenname gefunden."
"de": "Kein Seitenname gefunden.",
"fr": "Aucun titre de page trouvé."
},
"xkcd": {
"en": "xkcd {num}: {title} https://xkcd.com/{num}"
"en": "xkcd {num}: {title} https://xkcd.com/{num}",
"fr": "xkcd {num}: {title} https://xkcd.com/{num}"
},
"no-hits": {
"en": "No hits.",
"na'vi": "Oel ke tsun rivum ayuoti.",
"cy": "Dim canlyniadau.",
"nl": "Geen resultaten.",
"de": "Keine Treffer."
"de": "Keine Treffer.",
"fr": "Aucun résultat."
},
"link": {
"en": "{link}"
"en": "{link}",
"fr": "{link}"
}
}

View File

@ -1,5 +1,6 @@
{
"log_message": {
"en": "[{time}] {user}: {command}"
"en": "[{time}] {user}: {command}",
"fr": "[{time}] {user}: {command}"
}
}

View File

@ -1,10 +1,9 @@
{
"servers": {
"nc": {
"aberwiki": {
"matcher": "STATUS ([^ ]+) (\\d)$",
"acceptableState": 3,
"infoCommand": "status"
}
},
"help": "https://github.com/reality/depressionbot/blob/master/modules/nickserv/README.md"
}
}

View File

@ -72,8 +72,9 @@ var nickserv = function(dbot) {
if(match[1]) match[1] = match[1].replace('\*', '');
if(match && _.has(this.userStack, event.server) && _.has(this.userStack[event.server], match[1])) {
this.userStack[event.server][match[1]](match[3].trim());
var callback = this.userStack[event.server][match[1]];
delete this.userStack[event.server][match[1]];
callback(match[3].trim());
}
}
}.bind(this);

View File

@ -1,14 +1,18 @@
{
"authed": {
"en": "{nick} is authed with nickserv."
"en": "{nick} is authed with nickserv.",
"fr": "{nick} est authentifié avec nickserv."
},
"not_authed": {
"en": "{nick} is not authed with nickserv."
"en": "{nick} is not authed with nickserv.",
"fr": "{nick} n'est pas authentifié avec nickserv."
},
"hostmask": {
"en": "Hostmask for {nick}: {host}"
"en": "Hostmask for {nick}: {host}",
"fr": "Hostmask pour {nick}: {host}"
},
"no_hostmask": {
"en": "No hostmask could be found for {nick}"
"en": "No hostmask could be found for {nick}",
"fr": "Aucun hostmask trouvé pour {nick}"
}
}

View File

@ -1,183 +1,196 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
databank = require('databank'),
AlreadyExistsError = databank.AlreadyExistsError,
NoSuchThingError = databank.NoSuchThingError;
var commands = function(dbot) {
var polls = dbot.db.polls;
var commands = {
'~newpoll': function(event) {
var name = event.input[1].toLowerCase(),
options = event.input[2].toLowerCase().split(','),
description = event.input[3];
if(_.has(polls, name)) {
event.reply(dbot.t('poll_exists', { 'name': name }));
} else {
polls[name] = {
'name': name,
'description': description,
'owner': dbot.api.users.resolveUser(event.server, event.user),
'votes': {},
'votees': {}
};
for(var i=0;i<options.length;i++) {
polls[name]['votes'][options[i]] = 0;
}
event.reply(dbot.t('poll_created', {
'name': name,
'description': description,
'url': dbot.api.web.getUrl('/poll/' + name)
}));
}
_.each(options, function(item, index, list) { list[index] = [ item, 0 ]; })
votes = _.object(options);
this.db.create('poll', name, {
'name': name,
'description': description,
'owner': event.rUser.id,
'votes': votes,
'votees': {}
}, function(err, value) {
if(!err) {
event.reply(dbot.t('poll_created', {
'name': name,
'description': description,
'url': dbot.api.web.getUrl('poll/' + name)
}));
} else if(err instanceof AlreadyExistsError) {
event.reply(dbot.t('poll_exists', { 'name': name }));
}
});
},
'~addoption': function(event) {
var name = event.input[1].toLowerCase(),
option = event.input[2].toLowerCase(),
user = dbot.api.users.resolveUser(event.server, event.user);
option = event.input[2].toLowerCase();
if(_.has(polls, name)) {
if(polls[name].owner === user) {
if(!_.has(polls[name].votes, option)) {
polls[name]['votes'][option] = 0;
event.reply(dbot.t('option_added', {
'user': event.user,
'name': name,
'option': option
}));
this.db.read('poll', name, function(err, poll) {
if(!err) {
if(poll.owner === event.rUser.id) {
if(!_.has(poll.votes, option)) {
poll.votes[option] = 0;
this.db.save('poll', name, poll, function(err) {
event.reply(dbot.t('option_added', {
'user': event.user,
'name': name,
'option': option
}));
});
} else {
event.reply(dbot.t('option_exists', {
'option': option,
'name': name,
'user': event.user
}));
}
} else {
event.reply(dbot.t('option_exists', {
'option': option,
'name': name,
'user': event.user
event.reply(dbot.t('not_poll_owner', {
'user': event.user,
'name': name
}));
}
} else {
event.reply(dbot.t('not_poll_owner', {
'user': event.user,
'name': name
}));
if(err instanceof NoSuchThingError) {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}
} else {
event.reply(dbot.t('poll_unexistent', {'name': name}));
}
}.bind(this));
},
'~rmoption': function(event) {
var name = event.input[1].toLowerCase(),
option = event.input[2].toLowerCase(),
user = dbot.api.users.resolveUser(event.server, event.user);
if(_.has(polls, name)) {
if(polls[name].owner === user) {
if(_.has(polls[name].votes, option)) {
delete polls[name]['votes'][option];
event.reply(dbot.t('option_removed', {
'user': event.user,
'name': name,
'option': option
}));
option = event.input[2].toLowerCase();
this.db.read('poll', name, function(err, poll) {
if(!err) {
if(poll.owner === event.rUser.id) {
if(_.has(poll.votes, option)) {
delete poll.votes[option];
this.db.save('poll', name, poll, function(err) {
event.reply(dbot.t('option_removed', {
'user': event.user,
'name': name,
'option': option
}));
}.bind(this));
} else {
event.reply(dbot.t('invalid_vote', { 'vote': option }));
}
} else {
event.reply(dbot.t('invalid_vote', { 'vote': option }));
event.reply(dbot.t('not_poll_owner', { 'name': name }));
}
} else {
event.reply(dbot.t('not_poll_owner', { 'name': name }));
if(err instanceof NoSuchThingError) {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}
} else {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}.bind(this));
},
'~vote': function(event) {
var name = event.input[1].toLowerCase(),
vote = event.input[2].toLowerCase(),
user = dbot.api.users.resolveUser(event.server, event.user);
vote = event.input[2].toLowerCase();
this.db.read('poll', name, function(err, poll) {
if(!err) {
if(_.has(poll.votes, vote)) {
if(_.has(poll.votees, event.rUser.id)) {
var oldVote = poll.votees[event.rUser.id];
poll.votes[oldVote]--;
poll.votes[vote]++;
poll.votees[event.rUser.id] = vote;
} else {
poll.votes[vote]++;
poll.votees[event.rUser.id] = vote;
}
if(_.has(polls, name)) {
if(_.has(polls[name].votes, vote)) {
if(_.has(polls[name].votees, user)) {
var oldVote = polls[name].votees[user];
polls[name].votes[oldVote]--;
polls[name].votes[vote]++;
polls[name].votees[user] = vote;
event.reply(dbot.t('changed_vote', {
'vote': vote,
'poll': name,
'count': polls[name].votes[vote],
'user': event.user
}));
this.db.save('poll', name, poll, function(err) {
event.reply(dbot.t('voted', {
'vote': vote,
'poll': name,
'count': poll.votes[vote],
'user': event.user
}));
}.bind(this));
} else {
polls[name].votes[vote]++;
polls[name].votees[user] = vote;
event.reply(dbot.t('voted', {
'vote': vote,
'poll': name,
'count': polls[name].votes[vote],
'user': event.user
}));
event.reply(dbot.t('invalid_vote', { 'vote': vote }));
}
} else {
event.reply(dbot.t('invalid_vote', { 'vote': vote }));
if(err instanceof NoSuchThingError) {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}
} else {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}.bind(this));
},
'~pdesc': function(event) {
var name = event.input[1].toLowerCase();
this.db.read('poll', name, function(err, poll) {
if(!err) {
var options = _.keys(poll.votes);
var optionString = " Choices: ";
for(var i=0;i<options.length;i++) {
optionString += options[i] + ', ';
}
optionString = optionString.slice(0, -2) + '.';
if(_.has(polls, name)) {
var options = _.keys(polls[name].votes);
var optionString = " Choices: ";
for(var i=0;i<options.length;i++) {
optionString += options[i] + ', ';
event.reply(dbot.t('poll_describe', {
'name': name,
'description': poll.description,
'url': dbot.api.web.getUrl('poll/' + name)
}) + optionString);
} else {
if(err instanceof NoSuchThingError) {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
}
optionString = optionString.slice(0, -2) + '.';
event.reply(dbot.t('poll_describe', {
'name': name,
'description': polls[name].description,
'url': dbot.t('url', {
'host': dbot.config.web.webHost,
'port': dbot.config.web.webPort,
'path': 'polls/' + name
})
}) + optionString);
} else {
event.reply(dbot.t('poll_unexistent', { 'name': name }));
}
});
},
'~count': function(event) {
var name = event.input[1].toLowerCase();
if(_.has(polls, name)) {
var order;
var votesArr = [];
this.db.read('poll', name, function(err, poll) {
if(!err) {
var order;
var votesArr = [];
var order = _.chain(polls[name].votes)
.pairs()
.sortBy(function(option) { return option[1] })
.reverse()
.value();
var order = _.chain(poll.votes)
.pairs()
.sortBy(function(option) { return option[1]; })
.reverse()
.value();
var orderString = "";
for(var i=0;i<order.length;i++) {
orderString += order[i][0] +
" (" + order[i][1] + "), ";
var orderString = "";
for(var i=0;i<order.length;i++) {
orderString += order[i][0] +
" (" + order[i][1] + "), ";
}
orderString = orderString.slice(0, -2);
event.reply(dbot.t('count', {
'poll': name,
'description': poll.description,
'places': orderString
}));
} else {
if(err instanceof NoSuchThingError) {
event.reply(dbot.t('poll_unexistent', {'name': name}));
}
}
orderString = orderString.slice(0, -2);
event.reply(dbot.t('count', {
'poll': name,
'description': polls[name].description,
'places': orderString
}));
} else {
event.reply(dbot.t('poll_unexistent', {'name': name}));
}
});
}
};
commands['~newpoll'].regex = [/~newpoll ([^ ]+) options=([^ ]+) (.+)/, 4];

View File

@ -1,6 +1,12 @@
{
"help": "http://github.com/reality/depressionbot/blob/master/modules/poll/README.md",
"dbType": "redis",
"dbKeys": [ "polls" ],
"ignorable": true,
"dependencies": [ "users", "command" ]
"dependencies": [ "users", "web", "event" ],
"schema": {
"poll": {
"pkey": "name",
"fields": [ "description", "owner", "votes", "votees" ]
}
}
}

View File

@ -1,39 +1,57 @@
var _ = require('underscore')._;
var pages = function(dbot) {
var polls = dbot.db.polls;
var pages = {
// Shows the results of a poll
'/polls/:key': function(req, res) {
'/poll/:key': function(req, res) {
var key = req.params.key.toLowerCase();
if(_.has(dbot.db.polls, key)) {
var totalVotes = _.reduce(dbot.db.polls[key].votes,
function(memo, option) {
this.db.read('poll', key, function(err, poll) {
if(!err) {
var totalVotes = _.reduce(poll.votes, function(memo, option) {
return memo += option;
}, 0);
res.render('polls', {
'name': dbot.config.name,
'description': dbot.db.polls[key].description,
'votees': Object.keys(dbot.db.polls[key].votees),
'options': dbot.db.polls[key].votes,
locals: {
'totalVotes': totalVotes,
'url_regex': RegExp.prototype.url_regex()
}
});
} else {
res.render('error', {
'name': dbot.config.name,
'message': 'No polls under that key.'
});
}
},
var voterNicks = [];
/* TODO: Fix stupid fucking async issue bullshit
var voterNicks = _.map(poll.votees, function(vote, id) {
dbot.api.users.getUser(id, function(user) {
return user.primaryNick;
});
});*/
process.nextTick(function() {
console.log(voterNicks);
res.render('polls', {
'name': dbot.config.name,
'description': poll.description,
'votees': voterNicks,
'options': poll.votes,
locals: {
'totalVotes': totalVotes,
'url_regex': RegExp.prototype.url_regex()
}
});
});
} else {
res.render('error', {
'name': dbot.config.name,
'message': 'No polls under that key.'
});
}
});
},
// Lists all of the polls
'/poll': function(req, res) {
res.render('polllist', {
'name': dbot.config.name,
'polllist': Object.keys(dbot.db.polls)
var pollKeys = [];
this.db.scan('poll', function(result) {
if(result) pollKeys.push(result.name);
}, function(err) {
console.log(pollKeys);
res.render('polllist', {
'name': dbot.config.name,
'polllist': pollKeys
});
});
}
};

View File

@ -1,22 +1,26 @@
var poll = function(dbot) {
this.internalAPI = {
'updatePollNicks': function(server, oldNick) {
var newNick = dbot.api.users.resolveUser(server, oldNick);
_.each(dbot.db.polls, function(poll) {
if(poll.owner === oldNick) {
poll.owner = newNick;
'updatePollNicks': function(server, oldUser, newUser) {
this.db.scan('poll', function(poll) {
var needsUpdating = false;
if(poll.owner == oldUser.id) {
poll.owner = newUser.id;
needsUpdating = true;
}
if(_.has(poll.votees, oldNick)) {
poll.votees[newNick] = poll.votees[oldNick];
delete poll.votees[oldNick];
if(_.has(poll.votees, oldUser.id)) {
poll.votes[poll.votees[oldUser.id]]--;
delete poll.votees[oldUser.id];
needsUpdating = true;
}
}, this);
}
if(needsUpdating) {
this.db.save('poll', poll.name, poll, function(err) {});
}
}.bind(this), function(err) {});
}.bind(this)
};
this.onLoad = function() {
dbot.api.command.addHook('~setaliasparent', this.internalAPI.updatePollNicks);
dbot.api.command.addHook('~mergeusers', this.internalAPI.updatePollNicks);
dbot.api.event.addHook('~mergeusers', this.internalAPI.updatePollNicks);
}.bind(this);
};

View File

@ -5,7 +5,8 @@
"na'vi": "Usage: ~newpoll tstxo [sìftxey=tìfxey1,tìfxey2,fìfxey3] tìsla'tsu",
"cy": "Defnydd: ~newpoll enw [optiynau=opt1,opt2,op3] disgrifiad",
"nl": "Gebruik: ~newpoll name [options=opt1,opt2,opt3] description",
"de": "Benutzung: ~newpoll name [options=opt1,opt2,opt3] Beschreibung"
"de": "Benutzung: ~newpoll name [options=opt1,opt2,opt3] Beschreibung",
"fr": "Utilisation: ~newpoll nom [options=opt1,opt2,opt3] description"
},
"poll_exists": {
"en": "Poll '{name}' already exists.",
@ -13,17 +14,20 @@
"na'vi": "sìpawm sna'o '{name}' fkeytok srekrr.",
"cy": "Mae'r pôl {name} bodoli'n barod",
"nl": "Poll '{name}' bestaat al.",
"de": "Umfrage '{name}' existiert bereits."
"de": "Umfrage '{name}' existiert bereits.",
"fr": "Le sondage '{name}' existe déjà."
},
"poll_created": {
"en": "Poll '{name}' created ({description}). Cast thy votations! - {url}",
"es" : "Votación '{name}' creado ({description}). ¡Emited sus votas! - {url}",
"na'vi": "sìpawm sna'o '{name}' ngìyop ({description}). Nga tìpe'unit Pe'eiun - {url}",
"nl": "Poll '{name}' aangemaakt ({description}). Laat uw stem horen! - {url}",
"de": "Umfrage '{name}' gestartet ({description}). Lasst Antworten ertönen! - {url}"
"de": "Umfrage '{name}' gestartet ({description}). Lasst Antworten ertönen! - {url}",
"fr": "Sondage '{name}' créé ({description}). Tous aux ballots de vote ! - {url}"
},
"poll_describe": {
"en": "{name}: {description} - {url}."
"en": "{name}: {description} - {url}.",
"fr": "{name}: {description} - {url}."
},
"changed_vote": {
"en": "{user} changed their vote in {poll} to '{vote}' ({count}).",
@ -31,7 +35,8 @@
"na'vi": "{user} lìyatem ngeyä tìpe'un {poll}mì, ngeyä tìpe'un amip '{vote}'({count}) lu.",
"cy": "Newidiodd {user} eu pleidlais yn {poll} i '{vote}' ({count}).",
"nl": "{user} heeft zijn stem voor {poll} veranderd in '{vote}' ({count}).",
"de": "{user} hat seine Antwort in {poll} zu '{vote}' geändert ({count})."
"de": "{user} hat seine Antwort in {poll} zu '{vote}' geändert ({count}).",
"fr": "{user} a changé son vote dans {sondage} pour '{vote}' ({count})."
},
"voted": {
"en": "{user} voted for '{vote}' in {poll} ({count}).",
@ -39,7 +44,8 @@
"na'vi": "'{vote}'ìri {user} pìye'un {poll}mì ({count}).",
"cy": "Pledleisiodd {user} am '{vote}' yn {poll} ({count}).",
"nl": "{user} heeft gestemd voor '{vote}' in {poll} ({count}).",
"de": "{user} hat für '{vote}' in {poll} gestimmt ({count})."
"de": "{user} hat für '{vote}' in {poll} gestimmt ({count}).",
"fr":"{user} a voté pour '{vote}' dans {poll} ({count})."
},
"invalid_vote": {
"en": "Invalid vote: {vote}",
@ -47,7 +53,8 @@
"na'vi": "Ngeyä tìpe'un keyawr lu ({vote}).",
"cy": "Pleidlais annilys: {vote}",
"nl": "Ongeldige stem: {vote}",
"de": "Ungültige Antwort: {vote}"
"de": "Ungültige Antwort: {vote}",
"fr": "Vote invalide: {vote}"
},
"poll_unexistent": {
"en": "Poll '{name}' doesn't exist.",
@ -55,7 +62,8 @@
"na'vi": "sìpawm sna'o '{name}' ke fkeytok.",
"cy": "Nid yw pôl '{name}' yn bodoli",
"nl": "Poll '{name}' bestaat niet.",
"de": "Umfrage '{name}' existiert nicht."
"de": "Umfrage '{name}' existiert nicht.",
"fr": "Le sondage '{name}' n'existe pas."
},
"option_added": {
"en": "{user}: '{option}' added to '{name}'.",
@ -63,7 +71,8 @@
"na'vi": "'{name}'ur {user}ìl '{option}'it sung.",
"cy": "{user}: Ychwanegwyd '{option}' i '{name}'",
"nl": "{user}: '{option}' toegevoegd aan '{name}'.",
"de": "{user}: '{option}'in '{name}' hinzugefügt."
"de": "{user}: '{option}'in '{name}' hinzugefügt.",
"fr": "{user}: '{option}' ajouté à '{name}'."
},
"option_exists": {
"en": "{user}: '{option}' already exists in '{name}'.",
@ -71,7 +80,8 @@
"na'vi": "{user}: '{option}' fkeytok srekrr '{name}'mì.",
"cy": "{user}: Mae '{option}' yn bodoli'n barod yn '{name}'.",
"nl": "{user}: '{option}' bestaat al in '{name}'.",
"de": "{user}: '{option}' existiert bereits in '{name}'."
"de": "{user}: '{option}' existiert bereits in '{name}'.",
"fr": "{user}: '{option} existe déjà dans '{name}'."
},
"not_poll_owner": {
"en": "{user}: You don't own the '{name}' poll.",
@ -79,7 +89,8 @@
"na'vi": "{user}: ngaru '{name}' sìpawm sna'o ke lu.",
"cy": "{user}: Nid ydech chi'n berchen y pôl '{name}'.",
"nl": "{user}: Je bent niet de eigenaar van de poll '{name}'.",
"de": "{user}: Du darfst die Umfrage '{name}' nicht verändern."
"de": "{user}: Du darfst die Umfrage '{name}' nicht verändern.",
"fr": "{user}: Le sondage '{name}' ne t'appartient pas."
},
"option_removed": {
"en": "{user}: '{option}' removed from '{name}'",
@ -87,7 +98,8 @@
"na'vi": "{user}: '{option}'it 'aku '{name}'ta",
"cy": "{user}: '{option}' wedi ei ddileu o '{name}'",
"nl": "{user}: '{option}' verwijderd van '{name}'",
"de": "{user}: '{option}' entfernt aus '{name}'"
"de": "{user}: '{option}' entfernt aus '{name}'",
"fr": "{user}: '{option}' enlevé de '{name}'"
},
"av_voted": {
"en": "{user} voted '{vote}' in {poll}.",
@ -95,7 +107,8 @@
"na'vi": "{user}ìl '{vote}'it pìye'un '{poll}'mì.",
"cy": "Pledleisiodd {user} am '{vote}' yn {poll}",
"nl": "{user} heeft '{vote}' gestemd in {poll}.",
"de": "{user} hat in {poll} für '{vote}' gestimmt."
"de": "{user} hat in {poll} für '{vote}' gestimmt.",
"fr": "{user} a voté '{vote}' dans {poll}."
},
"av_changed_vote": {
"en": "{user} changed their vote in {poll} to '{vote}'.",
@ -103,13 +116,15 @@
"na'vi": "{user}ìl lìyatem ngeyä tìpa'unit '{poll}'mì, ngeyä tìpe'un '{vote} lu set.",
"cy": "Newidiodd {user} eu pleidlais yn {poll} i '{vote}'",
"nl": "{user} heeft zijn stem veranderd naar '{vote}' in {poll}.",
"de": "{user} hat seine Stimme in {poll} auf '{vote}' geändert."
"de": "{user} hat seine Stimme in {poll} auf '{vote}' geändert.",
"fr": "{user} a changé son vote dans {sondage} pour '{vote}'."
},
"count": {
"en": "The running-order of poll '{poll}' ({description}) is: {places}.",
"na'vi": "Sute tsnì pole'un '{poll}'mì ({description}) lu: {places}.",
"cy": "Trefn yr pôl '{poll}' ({description}) yw: {places}",
"nl": "De volgorde van poll '{poll}' ({description}) is: {places}.",
"de": "Das Ergebnis der Umfrage '{poll}' ({description}) lautet: {places}."
"de": "Das Ergebnis der Umfrage '{poll}' ({description}) lautet: {places}.",
"fr": "L'ordre de passage du sondage '{poll}' ({description}) est : {places}."
}
}

View File

@ -4,64 +4,98 @@ var api = function(dbot) {
return {
/**
* Create a profile for a new primary user on a given server.
* If the server does not already exist, create it.
* Create a new profile for a given "databanked" user.
* Typically only called as a hook to the new_user emit event.
* TODO(@samstudio8) Migrate to internalAPI
*/
"createProfile": function(server, primary){
var primaryLower = primary.toLowerCase();
if(!_.has(this.profiles, server)){
this.profiles[server] = {};
"createProfile": function(user, callback){
if(user){
this.db.create('profiles', user.id, {
'id': user.id,
'profile': this.config.schema.profile,
'preferences': this.config.schema.preferences
}, function(err, result){
if(err){
console.log(err);
}
});
}
if(!_.has(this.profiles[server], primaryLower)){
this.profiles[server][primaryLower] = {
"profile": {},
"preferences": {}
};
this.profiles[server][primaryLower].profile.primary = primary;
}
// Ensure all profiles have the keys specified by config.json
//TODO(samstudio8) Currently only handles "top-level"
_.defaults(this.profiles[server][primaryLower].profile, this.config.schema.profile);
_.defaults(this.profiles[server][primaryLower].preferences, this.config.schema.preferences);
},
//TODO(samstudio8) Merge Profiles
'mergeProfile': function(server, nick, callback){
console.log("mergeProfile not implemented");
},
/**
* Given a server and "new" alias, resolve this alias to the user's
* new primary name and move profile data pertaining to the alias to
* the new primary name.
*/
'renameProfile': function(server, alias){
if(!_.has(this.profiles, server)) return;
var profiles = dbot.db.profiles[server];
'getProfile': function(server, nick, callback){
dbot.api.users.resolveUser(server, nick, function(user){
if(user){
this.db.read('profiles', user.id, function(err, profile){
if(!err){
callback(false, user, profile);
}
else{
callback(true, user, null);
}
});
}
else{
callback(true, null, null);
}
}.bind(this));
},
if(_.has(profiles, alias)){
var primary = dbot.api.users.resolveUser(server, alias, true);
var primaryLower = primary.toLowerCase();
alias = alias.trim().toLowerCase();
profiles[primaryLower] = profiles[alias];
profiles[primaryLower].profile.primary = primary;
delete profiles[alias];
'getProfileByUUID': function(uuid, callback){
if(uuid){
this.db.read('profiles', uuid, function(err, profile){
if(!err){
callback(false, uuid, profile);
}
else{
callback(true, uuid, null);
}
});
}
else{
callback(true, null, null);
}
},
/**
* Given a server and a primary username which has been converted to a
* secondary alias find and remove the profile for the alias.
*/
'mergeProfile': function(server, mergeFromPrimary){
if(!_.has(this.profiles, server)) return;
var profiles = dbot.db.profiles[server];
'getAllProfiles': function(callback){
var profiles = [];
this.db.scan('profiles', function(profile){
profiles.push(profile);
}, function(err){
if(!err){
callback(profiles);
}
else{
console.log(err);
}
});
},
mergeFromPrimary = mergeFromPrimary.toLowerCase();
var mergeToPrimary = dbot.api.users.resolveUser(server, mergeFromPrimary, true).toLowerCase();
if(!_.has(profiles, mergeToPrimary)
|| !_.has(profiles, mergeFromPrimary)) return;
'setProperty': function(server, nick, field, value, callback){
this.api.getProfile(server, nick, function(err, user, profile){
if(!err){
profile.profile[field] = value;
this.db.save('profiles', user.id, profile, function(err){
if(!err){
callback("Ok!");
}
});
}
}.bind(this));
},
// Remove the profile of the alias
delete profiles[mergeFromPrimary];
'getProperty': function(server, nick, field, callback){
this.api.getProfile(server, nick, function(err, user, profile){
if(!err){
if(profile.profile[field]){
callback(profile.profile[field]);
}
}
}.bind(this));
},
}
};

View File

@ -2,49 +2,33 @@ var _ = require('underscore')._;
var commands = function(dbot){
var commands = {
"~getprop": function(event){
'~getprop': function(event){
if(event.params[1]){
var primary = dbot.api.users.resolveUser(event.server, event.user);
var res = dbot.db.profiles[event.server][primary.toLowerCase()].profile[event.params[1]];
if(res){
event.reply(res);
}
else{
event.reply("Nope.");
if(_.has(this.config.schema.profile, event.params[1])){
this.api.getProperty(event.server, event.user, event.params[1], function(reply){
event.reply(reply);
});
} else {
event.reply('Invalid property. Go home.');
}
}
},
"~setprop": function(event){
'~setprop': function(event){
if(event.input[1] && event.input[2]){
if(_.has(this.config.schema.profile, event.input[1])){
var primary = dbot.api.users.resolveUser(event.server, event.user);
dbot.db.profiles[event.server][primary.toLowerCase()].profile[event.input[1]] = event.input[2];
event.reply("Property set, maybe?");
}
else{
event.reply("Invalid property. Go home.");
this.api.setProperty(event.server, event.user, event.input[1], event.input[2], function(reply){
event.reply(reply);
});
} else {
event.reply('Invalid property. Go home.');
}
}
},
"~profile": function(event){
if(event.params[1]){
var primary = dbot.api.users.resolveUser(event.server, event.params[1]);
if(_.has(dbot.db.profiles[event.server], primary.toLowerCase())){
event.reply(dbot.api.web.getUrl("profile/"+event.server+"/"+primary.toLowerCase()));
}
else{
event.reply("No profile found for "+event.params[1]);
}
}
else{
event.message = '~profile ' + event.user;
event.action = 'PRIVMSG';
event.params = event.message.split(' ');
dbot.instance.emit(event);
}
'~profile': function(event) {
var user = event.params[1] || event.user;
event.reply(dbot.api.web.getUrl('profile/' + event.server + '/' + user));
}
};
commands['~setprop'].regex = [/~setprop ([^ ]+) (.+)/, 3];

View File

@ -1,7 +1,6 @@
{
"ignorable": false,
"dbKeys": [ "profiles" ],
"help": "https://github.com/reality/depressionbot/blob/master/modules/profile/README.md",
"dbType": "redis",
"schema": {
"profile": {
"primary": null,
@ -17,5 +16,5 @@
"timezone": null
}
},
"dependencies": [ "quotes", "users", "command" ]
"dependencies": [ "web", "quotes", "users" ]
}

View File

@ -5,58 +5,80 @@ var pages = function(dbot) {
return {
'/profile/:connection/:user': function(req, res) {
var connection = req.params.connection;
var user = dbot.cleanNick(req.params.user);
var nick = req.params.user;
var primary = dbot.api.users.resolveUser(connection, user, true);
//var profile = dbot.api.profile.getProfile(primary);
var profile = dbot.db.profiles[connection][primary.toLowerCase()].profile;
var stats = dbot.api.stats.getUserChansStats(connection, primary.toLowerCase(), [
"lines", "words", "lincent", "wpl", "in_mentions"]
);
dbot.api.users.resolveUser(connection, nick, function(user){
if(user){
dbot.api.profile.getProfile(connection, user.primaryNick, function(err, user, profile){
if(!err){
var stats = [];
res.render('profile', {
'name': dbot.config.name,
'connection': connection,
'primary': primary,
'profile': profile,
'stats': stats.channels,
/*TODO(@samstudio8)
* stats functionality currently disabled as it has not been databanked
*/
//var stats = dbot.api.stats.getUserChansStats(connection, user.primaryNick, [
// "lines", "words", "lincent", "wpl", "in_mentions"]
//);
res.render('profile', {
'name': dbot.config.name,
'connection': connection,
'primary': user.primaryNick,
'profile': profile.profile,
'stats': stats.channels,
});
}
else{
res.render('error', {
});
}
});
}
else{
res.render('not_found', {
});
}
});
},
'/profile/:connection': function(req, res) {
var connection = req.params.connection;
var profiles = dbot.db.profiles[connection];
dbot.api.profile.getAllProfiles(function(profiles){
var thumbnails = [];
_.each(profiles, function(profile){
var nick = dbot.api.users.getUser(profile.id, function(user){
if(user){
// TODO: Clean up
_.each(profiles, function(profile) {
if(_.has(dbot.db.quoteArrs, profile.profile.primary) && !profile.profile.avatar) {
var category = dbot.db.quoteArrs[profile.profile.primary];
var avatar = _.find(category, function(quote) {
return quote.match(/(\.jpg|\.png|\.jpeg)$/i);
/*TODO(@tmenari / @samstudio8)
* if username has a quote array and no avatar:
* search their quote array for a jpg, png, jpeg or gif
* set this as their new avatar
*/
if(profile.profile.avatar){
thumbnails.push({
"avatar": profile.profile.avatar,
"nick": user.primaryNick
});
}
}
});
if(avatar) profile.profile.avatar = avatar;
}
});
});
var nicks = [];
for (var p in profiles) {
if (profiles.hasOwnProperty(p) && profiles[p].profile.avatar) {
nicks.push(p);
}
}
nicks.sort(function(a, b) {
var x = profiles[a].profile.primary.toLowerCase();
var y = profiles[b].profile.primary.toLowerCase();
if(x > y) return 1;
if(x < y) return -1;
return 0;
});
process.nextTick(function(){
thumbnails.sort(function(a, b) {
var x = a.nick.toLowerCase();
var y = b.nick.toLowerCase();
if(x > y) return 1;
if(x < y) return -1;
return 0;
});
res.render('profile_grid', {
'name': dbot.config.name,
'connection': connection,
'nicks': nicks,
'profiles': profiles,
res.render('profile_grid', {
'name': dbot.config.name,
'connection': req.params.connection,
'thumbnails': thumbnails,
});
});
});
}
}

View File

@ -2,28 +2,28 @@ var _ = require('underscore')._;
var profile = function(dbot) {
this.profiles = dbot.db.profiles;
/**
* Iterate over known user profiles and ensure they contain all the
* required properties as defined in the configuation.
*/
this.onLoad = function(){
var api = this.api;
var schema = this.config.schema;
// Ensure all known users have a profile
_.each(dbot.api.users.getAllUsers(), function(server, serverName){
_.each(server, function(primary, primaryi){
api.createProfile(serverName, primary);
});
});
dbot.save();
// Ensure all users have a profile
dbot.api.users.getAllUsers(function(users){
if(users){
_.each(users, function(user){
this.api.getProfileByUUID(user.id, function(err, uuid, profile){
// If function returns an error and uuid, create a new profile
if(err && uuid){
this.api.createProfile(user);
}
}.bind(this));
}.bind(this));
}
}.bind(this));
// Add API Hooks
dbot.api.command.addHook('~setaliasparent', this.api.renameProfile);
dbot.api.command.addHook('~mergeusers', this.api.mergeProfile);
dbot.api.event.addHook('new_user', this.api.createProfile);
//TODO(@samstudio8) Profile Merging
//dbot.api.command.addHook('~mergeusers', this.api.mergeProfile);
};
};

View File

@ -3,8 +3,7 @@ var exec = require('child_process').exec,
_ = require('underscore');
var pages = function(dbot) {
var quoteCat = dbot.db.quoteArrs[dbot.config.name],
rev, diff, branch, credit, authors = [];
var rev, diff, branch, credit, authors = [];
exec("git log --format='%cN¬' | sort -u | tr -d '\n'", function (error, stdout, sderr) {
var credit = stdout.split("¬"); // nobody uses ¬, do they?
for (var i = 0; i < credit.length; i++) {
@ -30,72 +29,71 @@ var pages = function(dbot) {
/* TODO: merge back into github module */
var milestones;
request({"url":"https://api.github.com/repos/" + dbot.config.github.defaultrepo + "/milestones?state=open","headers":{"User-Agent":"reality/depressionbot (project module)"}}, function(error, response, body){
request({"url":"https://api.github.com/repos/" + dbot.config.modules.github.defaultrepo + "/milestones?state=open","headers":{"User-Agent":"reality/depressionbot (project module)"}}, function(error, response, body){
milestones = JSON.parse(body);
});
return {
'/project': function(req, res) {
var quote = dbot.config.name;
if(quoteCat) {
quote = quoteCat[Math.floor(Math.random()*quoteCat.length)];
}
dbot.api.quotes.getQuote(dbot.config.name, function(quote) {
if(!quote) quote = dbot.config.name;
res.render('project', {
"translation": dbot.modules.project.api.translationProgress(),
"configList": dbot.modules.project.api.configList(),
"authors": authors,
"credits": dbot.t("credits"),
"thanks": dbot.t("thanks"),
"name": dbot.config.name,
"intro": dbot.t("dbotintro", {
"botname": dbot.config.name
}),
"curr839": dbot.config.language,
"repo": dbot.config.github.defaultrepo,
"branch": dbot.t("branch",{
"branch": branch
}),
"currver": dbot.config.version,
"currlang": dbot.t("dbotspeaks",{
"lang839": dbot.config.language,
"langen": dbot.strings[dbot.config.language]["en"],
"lang": dbot.t(dbot.config.language),
"name": dbot.config.name
}),
"projectstatus": dbot.t("projectstatus"),
"revnum": dbot.t("revnum",{
res.render('project', {
"translation": dbot.modules.project.api.translationProgress(),
"configList": dbot.modules.project.api.configList(),
"authors": authors,
"credits": dbot.t("credits"),
"thanks": dbot.t("thanks"),
"name": dbot.config.name,
"rev": rev
}),
"modules": dbot.config.moduleNames,
"loadmod": dbot.t("loadedmodules"),
"debugmode": dbot.t("debugmode-" + dbot.config.debugMode),
"milestones": milestones,
"milestoneprog": dbot.t("milestoneprog"),
"config": dbot.t("configoptions"),
"milestonename": dbot.t("milestonename"),
"openmilestone": dbot.t("openmilestone"),
"closedmilestone": dbot.t("closedmilestone"),
"development": dbot.t("development"),
"dquote": quote,
"diff": diff,
"pagetitle": dbot.t("pagetitle", {
"botname": dbot.config.name
}),
"git": dbot.t("git"),
"milestonehead": dbot.t("milestones"),
"propaganda": dbot.t("propaganda"),
"languagecurr": dbot.t(dbot.config.language),
"languagenati": dbot.t("langhead-native"),
"languageeng": dbot.t("en"),
"languageprog": dbot.t("langhead-progress"),
"languagetrans": dbot.t("langhead-translations"),
"languagetranshead": dbot.t("translations"),
"pullreqs": dbot.t("outstanding-pullreq")
});
},
"intro": dbot.t("dbotintro", {
"botname": dbot.config.name
}),
"curr839": dbot.config.language,
"repo": dbot.config.modules.github.defaultrepo,
"branch": dbot.t("branch",{
"branch": branch
}),
"currver": dbot.config.version,
"currlang": dbot.t("dbotspeaks",{
"lang839": dbot.config.language,
"langen": dbot.strings[dbot.config.language]["en"],
"lang": dbot.t(dbot.config.language),
"name": dbot.config.name
}),
"projectstatus": dbot.t("projectstatus"),
"revnum": dbot.t("revnum",{
"name": dbot.config.name,
"rev": rev
}),
"modules": dbot.config.moduleNames,
"loadmod": dbot.t("loadedmodules"),
"debugmode": dbot.t("debugmode-" + dbot.config.debugMode),
"milestones": milestones,
"milestoneprog": dbot.t("milestoneprog"),
"config": dbot.t("configoptions"),
"milestonename": dbot.t("milestonename"),
"openmilestone": dbot.t("openmilestone"),
"closedmilestone": dbot.t("closedmilestone"),
"development": dbot.t("development"),
"dquote": quote,
"diff": diff,
"pagetitle": dbot.t("pagetitle", {
"botname": dbot.config.name
}),
"git": dbot.t("git"),
"milestonehead": dbot.t("milestones"),
"propaganda": dbot.t("propaganda"),
"languagecurr": dbot.t(dbot.config.language),
"languagenati": dbot.t("langhead-native"),
"languageeng": dbot.t("en"),
"languageprog": dbot.t("langhead-progress"),
"languagetrans": dbot.t("langhead-translations"),
"languagetranshead": dbot.t("translations"),
"pullreqs": dbot.t("outstanding-pullreq")
});
});
}
};
};

View File

@ -14,30 +14,30 @@ var project = function(dbot) {
var list = [];
if(_.has(dbot.modules,'dent')){
list.push(dbot.t("dent-account", {
"username": dbot.config.dent.username
"username": dbot.config.modules.dent.username
}));
if(_.has(dbot.config.dent.dentQuotes)) {
if(_.has(dbot.config.modules.dent.dentQuotes)) {
list.push(dbot.t("dent-push"));
}
}
if(_.has(dbot.modules,'link')){
if(dbot.config.link.autoTitle){
if(dbot.config.modules.link.autoTitle){
list.push(dbot.t("link-autotitle"));
}
}
if(_.has(dbot.modules,'quotes')){
list.push(dbot.t("quote-rmlimit", {
"limit": dbot.config.quotes.rmLimit
"limit": dbot.config.modules.quotes.rmLimit
}));
}
if(_.has(dbot.modules,'report')){
if(dbot.config.report.notifyVoice){
if(dbot.config.modules.report.notifyVoice){
list.push(dbot.t("report-notifyvoice"));
}
}
if(_.has(dbot.modules,'web')){
list.push(dbot.t("web-port", {
"port": dbot.config.web.webPort
"port": dbot.config.modules.web.webPort
}));
}
return list;

View File

@ -28,7 +28,7 @@
},
"en": {
"en": "English",
"fr": "anglais",
"fr": "Anglais",
"de": "Englisch",
"it": "inglese",
"cy": "saesneg",
@ -36,7 +36,7 @@
},
"fr": {
"en": "French",
"fr": "français",
"fr": "Français",
"de": "Französisch",
"it": "francese",
"cy": "Ffrangeg",
@ -44,7 +44,7 @@
},
"de": {
"en": "German",
"fr": "allemande",
"fr": "Allemand",
"de": "Deutsch",
"it": "tedesco",
"cy": "Almaeneg",
@ -52,7 +52,7 @@
},
"it": {
"en": "Italian",
"fr": "italien",
"fr": "Italien",
"de": "Italiener",
"it": "italiano",
"cy": "Eidaleg",
@ -60,7 +60,7 @@
},
"cy": {
"en": "Welsh",
"fr": "gallois",
"fr": "Gallois",
"de": "Walisisch",
"it": "gallese",
"cy": "cymraeg",
@ -77,7 +77,7 @@
},
"es": {
"en": "Spanish",
"fr": "espagnole",
"fr": "Espagnol",
"de": "Spanisch",
"it": "spagnolo",
"cy": "Sbaeneg",
@ -87,11 +87,12 @@
"nl": {
"en": "Dutch",
"cy": "Iseldireg",
"nl": "Nederlands"
"nl": "Nederlands",
"fr": "Néerlandais"
},
"revnum": {
"en": "{name} is at revision {rev}",
"fr": "{name} est à révision {rev}",
"fr": "{name} est à la révision {rev}",
"de": "{name} ist in der Revision {rev}",
"it": "{name} il numero di revisione {rev}",
"cy": "{name} ar diwygiad {rev}",
@ -115,7 +116,7 @@
},
"debugmode-true": {
"en": "Debug mode active",
"fr": "debug actif",
"fr": "Mode de débogage activé",
"de": "Debug-Modus aktiviert",
"it": "debug attivo",
"cy": "Modd dadfygio gweithredol",
@ -123,7 +124,7 @@
},
"debugmode-false": {
"en": "Debug off",
"fr": "debug éteint",
"fr": "Mode de débogage arrêté",
"de": "Debug-Modus ausgeschaltet",
"it": "debug spento",
"cy": "Modd dadfygio wedi'i ddiffodd",
@ -131,7 +132,7 @@
},
"milestones": {
"en": "Milestones",
"fr": "étapes",
"fr": "Etapes",
"cy": "Cerrig milltir",
"de": "Zwischenziel"
},
@ -177,36 +178,43 @@
},
"dent-account": {
"en": "Submitting dents to @{username}",
"cy": "Cyflwyno 'dents' i'w @{username}"
"cy": "Cyflwyno 'dents' i'w @{username}",
"fr": "Envoi de 'dents' à @{username}"
},
"dent-push": {
"en": "Pushing quotes to identi.ca",
"cy": "Gwthio dyfyniadau i identi.ca"
"cy": "Gwthio dyfyniadau i identi.ca",
"fr": "Envoi des citations vers identi.ca"
},
"quote-rmlimit": {
"en": "Quote removal throttle set to {limit}",
"cy": "Trothwy ar dileu dyfyniadau gosod i {limit}"
"cy": "Trothwy ar dileu dyfyniadau gosod i {limit}",
"fr": "Commande d'enlèvement de citations réglée à {limit}"
},
"report-notifyvoice": {
"en": "Voiced users are being notified of reports",
"cy": "Defnyddwyr leisiwyd yn cael eu hysbysu o adroddiadau"
"cy": "Defnyddwyr leisiwyd yn cael eu hysbysu o adroddiadau",
"fr": "Les utilisateurs ayant la parole sont notifiés des rapports"
},
"web-port": {
"en": "Web is listening on {port}",
"cy": "We yn gwrando ar {port}"
"cy": "We yn gwrando ar {port}",
"fr": "Le web écoute sur {port}"
},
"propaganda": {
"en": "Contribute to the code on Github!",
"cy": "Cyfrannu at ay cod ar Github!"
"cy": "Cyfrannu at ay cod ar Github!",
"fr": "Contribuez au code sur GitHub!"
},
"branch": {
"en": "{branch}"
"en": "{branch}",
"fr": "{branch}"
},
"git": {
"en": "version control",
"de": "Versionsverwaltung",
"es": "Control de versiones",
"fr": "Festion de versions",
"fr": "Gestion de versions",
"it": "Controllo versione",
"cy": "Rheoli fersiwn",
"nl": "Versiebeheersysteem"
@ -230,7 +238,7 @@
},
"langhead-progress": {
"en": "Translation Progress",
"fr": "Avancement de la Traduction",
"fr": "Avancement de la traduction",
"cy": "Cynnydd Cyfieithu",
"de": "Übersetzungsfortschritt"
},
@ -241,13 +249,16 @@
},
"outstanding-pullreq": {
"en": "There are outstanding pull requests",
"cy": "Mae disgwyl ceisiadau tynnu"
"cy": "Mae disgwyl ceisiadau tynnu",
"fr": "Il a des requêtes pull en attente"
},
"oaosidl": {
"en": "The Official Aberystwyth Open Source International Development League"
"en": "The Official Aberystwyth Open Source International Development League",
"fr": "La Ligue Officielle de Développement International Open Source d'Aberystwyth"
},
"link-autotitle": {
"en": "Automatically titling links in channels",
"cy": "Teitlo awtomatig cysylltiadau mewn sianelau"
"cy": "Teitlo awtomatig cysylltiadau mewn sianelau",
"fr": "Titre automatiquement les liens dans les channels"
}
}

View File

@ -11,42 +11,31 @@ This is the original reason that DBot was created, stores and displays quotes.
#### rmLimit: 10
Amount of quotes which can be removed before admin approval is required.
### Commands
#### quotesOnJoin: false
Show quotes for users when they enter channels.
### Commands: Quote Addition
#### ~qadd [category] = [quote]
Add a new quote to the database under the given category.
### Commands: Quote Retrieval
#### ~[category]
Alias for ~q.
#### ~q [category]
Display a random quote from a given category.
#### ~qadd [category] = [quote]
Add a new quote to the database.
#### ~rq
Show a random quote from the database.
#### ~qstats
Show a list of the biggest quote categories.
#### ~qsearch [category] = [needle]
Search a category for quotes including the given text.
#### ~rmlast [category]
Remove the last quote added to a given category.
### Commands: Quote Removal
#### ~rmstatus
Show how many quotes are currently in the removal cache, and whether they will
be randomly removed.
#### ~rm [category] = [quote]
Remove a given quote from the given category.
#### ~qcount [category]
Show the number of quotes stored in the given category, or if called without a
category it will show the total number of quotes in the database.
#### ~rq
Show a random quote from the database.
#### ~link [category]
Show a link to the page on the web interface which shows this category's quotes.
### Admin-only Commands
#### ~rmconfirm
Confirm that the quotes currently in the removal cache are okay to be removed,
and permanently delete them.
@ -55,10 +44,48 @@ and permanently delete them.
Re-instate the quotes that are currently in the removal cache back into the main
quote database.
#### ~rmlast [category]
Remove the last quote added to a given category.
#### ~rm [category] = [quote]
Remove a given quote from the given category.
### Commands: Stats & Searching
#### ~qstats
Show a list of the biggest quote categories.
#### ~qsearch [category] = [needle]
Search a category for quotes including the given text. If category is given as
_*_, then all categories will be searched for the given text.
#### ~qcount [category]
Show the number of quotes stored in the given category, or if called without a
category it will show the total number of quotes in the database.
#### ~qrename [oldName] [newName]
Rename a quote category.
#### ~qmerge [primary] [secondary]
Merge two quote categories into one.
#### ~link [category]
Show a link to the page on the web interface which shows this category's quotes.
### API
#### getQuote(event, category)
Returns a random quote from the given category.
#### addQuote(key, quote, user, callback)
Add the given quote to the category stored under the given key. If it's a new
quote category, then the owner of the category will be recorded as the given
user. The callback is called with one argument, the new quote count of the given
category.
#### getQuote (key, callback)
Get a random quote from the given category. Callback is called with one
argument, either a random quote under the given key, or false if the category
doesn't exist. This getQuote function does not require a server argument, and as
such does not run the quote through the interpolation function (does not parse
~~category~~ etc).
### Removal Spam Protection

View File

@ -1,13 +1,76 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
databank = require('databank'),
uuid = require('node-uuid');
var commands = function(dbot) {
var quotes = dbot.db.quoteArrs;
var commands = {
// Alternative syntax to ~q
/*** Quote Addition ***/
// Add a quote to a category
'~qadd': function(event) {
var key = event.input[1].toLowerCase().trim(),
quote = event.input[2];
this.api.addQuote(key, quote, event.user, function(newCount) {
if(newCount) {
dbot.api.event.emit('~qadd', [ key, quote ]);
event.reply(dbot.t('quote_saved', {
'category': key,
'count': newCount
}));
} else {
event.reply(dbot.t('quote_exists'));
}
});
},
/*** Quote Retrieval ***/
// Alternative ~q syntax
'~': function(event) {
commands['~q'].bind(this)(event);
},
// Retrieve quote from a category in the database.
'~q': function(event) {
var key = event.input[1];
this.api.getInterpolatedQuote(event.server, event.channel, key, function(quote) {
if(quote) {
event.reply(key + ': ' + quote);
} else {
if(this.config.udFallback === true && _.has(dbot.modules, 'link')) {
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 {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
}
}.bind(this));
},
// Choose a random quote category and a random quote from that
// TODO: This is quite inefficient, but databank must evolve to do otherwise.
'~rq': function(event) {
var categories = [];
this.db.scan('quote_category', function(result) {
if(result) {
categories.push(result);
}
}, function(err) {
var cIndex = _.random(0, _.size(categories) -1);
var qIndex = _.random(0, categories[cIndex].quotes.length - 1);
event.reply(categories[cIndex].name + ': ' + categories[cIndex].quotes[qIndex]);
});
},
/*** Quote Removal ***/
// Show number of quotes in removal cache
'~rmstatus': function(event) {
var rmCacheCount = this.rmCache.length;
if(rmCacheCount < dbot.config.quotes.rmLimit) {
@ -19,6 +82,7 @@ var commands = function(dbot) {
}
},
// Confirm removal of quote cache
'~rmconfirm': function(event) {
var rmCacheCount = this.rmCache.length;
this.rmCache.length = 0;
@ -26,213 +90,306 @@ var commands = function(dbot) {
{ 'count': rmCacheCount }));
},
// Reinstate all quotes in removal cache
'~rmdeny': function(event) {
var rmCache = this.rmCache;
var rmCacheCount = rmCache.length;
for(var i=0;i<rmCacheCount;i++) {
if(!_.has(quotes, rmCache[i].key)) {
quotes[rmCache[i].key] = [];
}
quotes[rmCache[i].key].push(rmCache[i].quote);
}
rmCache.length = 0;
_.each(rmCache, function(quote, index) {
this.api.addQuote(quote.key, quote.quote, event.user, function(newCount) { });
}.bind(this));
rmCache.length = 0;
event.reply(dbot.t('quote_cache_reinstated',
{ 'count': rmCacheCount }));
},
'~rmlast': function(event) {
if(this.rmAllowed === true || _.include(dbot.config.admins, event.user)) {
var key = event.input[1].trim().toLowerCase(),
category = false,
removedQuote;
var quoteRemoved = function(err) {
this.internalAPI.resetRemoveTimer(event, key, removedQuote);
event.reply(dbot.t('removed_from', {
'quote': removedQuote,
'category': key
}));
}.bind(this);
// Retrieve quote from a category in the database.
'~q': function(event) {
var key = event.input[1].trim().toLowerCase();
var quote = this.api.getQuote(event, event.input[1]);
if(quote) {
event.reply(key + ': ' + quote);
} else {
if(this.config.udFallback === true && _.has(dbot.modules, 'link')) {
dbot.api.link.udLookup(key, function(word, definition) {
if(word) {
event.reply(key + '[UD]: ' + definition);
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
if(category) {
removedQuote = category.quotes.pop();
if(category.quotes.length == 0) {
this.db.del('quote_category', category.id, quoteRemoved);
} else {
event.reply(dbot.t('category_not_found', {'category': key}));
this.db.save('quote_category', category.id, category, quoteRemoved);
}
});
} else {
event.reply(dbot.t('category_not_found', {'category': key}));
}
} else {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
}.bind(this));
} else {
event.reply(dbot.t('rmlast_spam'));
}
},
// Remove specific quote from category
'~rm': function(event) {
if(this.rmAllowed == true || _.include(dbot.config.admins, event.user)) {
var key = event.input[1].trim().toLowerCase();
quote = event.input[2],
category = false;
var quoteRemoved = function(err) {
this.internalAPI.resetRemoveTimer(event, key, quote);
event.reply(dbot.t('removed_from', {
'category': key,
'quote': quote
}));
}.bind(this);
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
if(category) {
if(category.quotes.indexOf(quote) != -1) {
category.quotes = _.without(category.quotes, quote);
if(category.quotes.length == 0) {
this.db.del('quote_category', category.id, quoteRemoved);
} else {
this.db.save('quote_category', category.id, category, quoteRemoved);
}
} else {
event.reply(dbot.t('q_not_exist_under', {
'category': key,
'quote': quote
}));
}
} else {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
}.bind(this));
} else {
event.reply(dbot.t('rmlast_spam'));
}
},
/*** Quote Statistics and Searching ***/
// Shows a list of the biggest categories
'~qstats': function(event) {
var qSizes = _.chain(quotes)
.pairs()
.sortBy(function(category) { return category[1].length })
.reverse()
.first(10)
.value();
var quoteSizes = {};
this.db.scan('quote_category', function(category) {
if(category) {
quoteSizes[category.name] = category.quotes.length;
}
}.bind(this), function(err) {
var qSizes = _.chain(quoteSizes)
.pairs()
.sortBy(function(category) { return category[1] })
.reverse()
.first(10)
.value();
var qString = dbot.t('large_categories');
for(var i=0;i<qSizes.length;i++) {
qString += qSizes[i][0] + " (" + qSizes[i][1].length + "), ";
}
var qString = dbot.t('large_categories');
for(var i=0;i<qSizes.length;i++) {
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
}
event.reply(qString.slice(0, -2));
event.reply(qString.slice(0, -2));
});
},
// Search a given category for some text.
'~qsearch': function(event) {
var haystack = event.input[1].trim().toLowerCase();
var needle = event.input[2];
if(_.has(quotes, haystack)) {
var matches = _.filter(quotes[haystack], function(quote) {
return quote.indexOf(needle) != -1;
}, this);
if(matches.length == 0) {
event.reply(dbot.t('no_results'));
} else {
event.reply(dbot.t('search_results', {
'category': haystack,
'needle': needle,
'quote': matches[0],
'matches': matches.length
}));
}
} else {
event.reply(dbot.t('empty_category'));
}
},
'~rmlast': function(event) {
if(this.rmAllowed === true || _.include(dbot.config.admins, event.user)) {
var key = event.input[1].trim().toLowerCase();
if(_.has(quotes, key)) {
var quote = quotes[key].pop();
if(quotes[key].length == 0) {
delete quotes[key];
var haystack = event.input[1].trim().toLowerCase(),
needle = event.input[2],
category = false;
if(haystack == '*') {
var matches = [];
this.db.scan('quote_category', function(category) {
if(category) {
var theseMatches =_.each(category.quotes, function(quote) {
if(quote.indexOf(needle) != -1) {
matches.push({
'category': category.name,
'quote': quote
});
}
});
}
this.internalAPI.resetRemoveTimer(event, key, quote);
event.reply(dbot.t('removed_from', {
'quote': quote,
'category': key
}));
} else {
event.reply(dbot.t('no_quotes', {'category': key}));
}
} else {
event.reply(dbot.t('rmlast_spam'));
}
},
'~rm': function(event) {
if(this.rmAllowed == true || _.include(dbot.config.admins, event.user)) {
var key = event.input[1].trim().toLowerCase();
var quote = event.input[2];
if(_.has(quotes, key)) {
var category = quotes[key];
var index = category.indexOf(quote);
if(index !== -1) {
category.splice(index, 1);
if(category.length === 0) {
delete quotes[key];
}
this.internalAPI.resetRemoveTimer(event, key, quote);
event.reply(dbot.t('removed_from', {'category': key, 'quote': quote}));
}, function(err) {
if(matches.length > 0) {
event.reply(dbot.t('search_results', {
'category': matches[0].category,
'needle': needle,
'quote': matches[0].quote,
'matches': matches.length
}));
} else {
event.reply(dbot.t('q_not_exist_under', {'category': key, 'quote': quote}));
event.reply(dbot.t('no_results'));
}
} else {
event.reply(dbot.t('category_not_found', {'category': key}));
}
});
} else {
event.reply(dbot.t('rmlast_spam'));
this.db.search('quote_category', { 'name': haystack }, function(result) {
category = result;
}, function(err) {
if(category) {
var matches = _.filter(category.quotes, function(quote) {
return quote.indexOf(needle) != -1;
});
if(matches.length == 0) {
event.reply(dbot.t('no_results'));
} else {
event.reply(dbot.t('search_results', {
'category': haystack,
'needle': needle,
'quote': matches[0],
'matches': matches.length
}));
}
} else {
event.reply(dbot.t('empty_category'));
}
});
}
},
// Count quotes in a given category or total quotes overall
'~qcount': function(event) {
var input = event.message.valMatch(/^~qcount ([\d\w\s-]*)/, 2);
if(input) { // Give quote count for named category
var key = input[1].trim().toLowerCase();
if(_.has(quotes, key)) {
event.reply(dbot.t('quote_count', {
'category': key,
'count': quotes[key].length
}));
var key = input[1].trim().toLowerCase(),
category = false;
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
if(category) {
event.reply(dbot.t('quote_count', {
'category': key,
'count': category.quotes.length
}));
} else {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
});
} else {
var quoteCount = 0;
this.db.scan('quote_category', function(category) {
if(category) {
quoteCount += category.quotes.length;
}
}, function(err) {
event.reply(dbot.t('total_quotes', { 'count': quoteCount }));
});
}
},
// Rename a quote category
'~qrename': function(event) {
var oldName = event.input[1],
newName = event.input[2],
oldCategory = false,
newCategory = false;
this.db.search('quote_category', { 'name': newName }, function(result) {
newCategory = result;
}, function(err) {
if(!newCategory) {
this.db.search('quote_category', { 'name': oldName }, function(result) {
oldCategory = result;
}, function(err) {
oldCategory.name = newName;
this.db.save('quote_category', oldCategory.id, oldCategory, function(err) {
event.reply(dbot.t('category_renamed', {
'oldName': oldName,
'newName': newName
}));
});
}.bind(this));
} else {
event.reply(dbot.t('no_quotes', { 'category': key }));
event.reply(dbot.t('newcat_exists', { 'newcat': newName }));
}
} else { // Give total quote count
var totalQuoteCount = _.reduce(quotes, function(memo, category) {
return memo + category.length;
}, 0);
event.reply(dbot.t('total_quotes', { 'count': totalQuoteCount }));
}
}.bind(this));
},
'~qadd': function(event) {
var key = event.input[1].toLowerCase().trim();
var text = event.input[2].trim();
if(!_.isArray(quotes[key])) {
quotes[key] = [];
}
// Merge a quote category insto another
'~qmerge': function(event) {
var primaryName = event.input[1],
secondName = event.input[2],
primary = false,
secondary = false;
if(_.include(quotes[key], text)) {
event.reply(dbot.t('quote_exists'));
} else {
quotes[key].push(text);
this.rmAllowed = true;
event.reply(dbot.t('quote_saved', {
'category': key,
'count': quotes[key].length
}));
return { 'key': key, 'text': text };
}
return false;
this.db.search('quote_category', { 'name': primaryName }, function(result) {
primary = result;
}, function(err) {
if(primary) {
this.db.search('quote_category', { 'name': secondName }, function(result) {
secondary = result;
}, function(err) {
if(secondary) {
primary.quotes = _.union(primary.quotes, secondary.quotes);
this.db.save('quote_category', primary.id, primary, function(err) {
this.db.del('quote_category', secondary.id, function(err) {
event.reply(dbot.t('categories_merged', {
'from': secondName,
'into': primaryName
}));
});
}.bind(this));
} else {
event.reply(dbot.t('category_not_found', { 'category': secondName }));
}
}.bind(this));
} else {
event.reply(dbot.t('category_not_found', { 'category': primaryName }));
}
}.bind(this));
},
'~rq': function(event) {
if(_.keys(quotes).length > 0) {
var category = _.keys(quotes)[_.random(0, _.size(quotes) -1)];
event.reply(category + ': ' +
this.internalAPI.interpolatedQuote(event.server, event.channel.name, event.user, category));
} else {
event.reply(dbot.t('no_results'));
}
},
// Link to quote web page
'~link': function(event) {
var key = event.input[1].toLowerCase();
if(_.has(quotes, key)) {
if(_.has(dbot.config, 'web') && _.has(dbot.config.web, 'webHost') &&
_.has(dbot.config.web, 'webPort')) {
event.reply(dbot.t('quote_link', {
'category': key,
'url': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key))
}));
var key = event.input[1].toLowerCase(),
category = false;
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
if(category) {
if(_.has(dbot.modules, 'web')) {
event.reply(dbot.t('quote_link', {
'category': key,
'url': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key))
}));
} else {
event.reply(dbot.t('web_not_configured'));
}
} else {
event.reply(dbot.t('web_not_configured'));
event.reply(dbot.t('category_not_found', { 'category': key }));
}
} else {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
},
});
}
};
commands['~'].regex = [/^~([\d\w\s-]*)/, 2];
commands['~q'].regex = [/^~q ([\d\w\s-]*)/, 2];
commands['~qsearch'].regex = [/^~qsearch ([\d\w\s-]+?)[ ]?=[ ]?(.+)$/, 3];
commands['~qsearch'].regex = [/^~qsearch ([\d\w\s*-]+?)[ ]?=[ ]?(.+)$/, 3];
commands['~rm'].regex = [/^~rm ([\d\w\s-]+?)[ ]?=[ ]?(.+)$/, 3];
commands['~rmlast'].regex = [/^~rmlast ([\d\w\s-]*)/, 2];
commands['~qadd'].regex = [/^~qadd ([\d\w-]+[\d\w\s-]*)[ ]?=[ ]?(.+)$/, 3];
commands['~qrename'].regex = [/^~qrename ([\d\w\s-]+?)[ ]?=[ ]?(.+)$/, 3];
commands['~qmerge'].regex = [/^~qmerge ([\d\w\s-]+?)[ ]?=[ ]?(.+)$/, 3];
commands['~link'].regex = [/^~link ([\d\w\s-]*)/, 2];
commands['~rmconfirm'].access = 'moderator';
commands['~rmdeny'].access = 'moderator';
commands['~qrename'].access = 'moderator';
commands['~qmerge'].access = 'moderator';
return commands;
};

View File

@ -1,9 +1,8 @@
{
"dbKeys": [ "quoteArrs" ],
"dependencies": [ "command", "users", "event" ],
"dbType": "redis",
"dependencies": [ "users", "event" ],
"rmLimit": 10,
"ignorable": true,
"quotesOnJoin": false,
"udFallback": false,
"help": "http://github.com/reality/depressionbot/blob/master/modules/quotes/README.md"
"udFallback": false
}

View File

@ -3,23 +3,39 @@ var pages = function(dbot) {
return {
// Lists quotes in a category
'/quotes/:key': function(req, res) {
var key = req.params.key.toLowerCase();
if(_.has(dbot.db.quoteArrs, key)) {
res.render('quotes', { 'name': dbot.config.name, 'quotes': dbot.db.quoteArrs[key], locals: { 'url_regex': RegExp.prototype.url_regex() } });
} else {
res.render('error', { 'name': dbot.config.name, 'message': 'No quotes under that key.' });
}
this.api.getQuoteCategory(req.params.key, function(category) {
if(category) {
res.render('quotes', {
'name': dbot.config.name,
'quotes': category.quotes,
'locals': {
'url_regex': RegExp.prototype.url_regex()
}
});
} else {
res.render('error', {
'name': dbot.config.name,
'message': 'No quotes under that key.'
});
}
});
},
// Show quote list.
'/quotes': function(req, res) {
res.render('quotelist', { 'name': dbot.config.name, 'quotelist': Object.keys(dbot.db.quoteArrs) });
this.api.getCategoryKeys(function(keys) {
res.render('quotelist', {
'name': dbot.config.name,
'quotelist': keys
});
});
},
// Load random quote category page
'/rq': function(req, res) {
var rCategory = Object.keys(dbot.db.quoteArrs).random();
res.render('quotes', { 'name': dbot.config.name, 'quotes': dbot.db.quoteArrs[rCategory], locals: { 'url_regex': RegExp.prototype.url_regex() } });
'/quoteremovals': function(req, res) {
res.render('quotes', {
'name': dbot.config.name,
'quotes': _.pluck(this.rmCache, 'quote')
});
}
}
};

View File

@ -1,50 +1,46 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
uuid = require('node-uuid');
var quotes = function(dbot) {
dbot.sessionData.rmCache = [];
this.quotes = dbot.db.quoteArrs,
this.addStack = [],
this.rmAllowed = true,
this.rmCache = dbot.sessionData.rmCache,
this.rmCache = dbot.sessionData.rmCache;
this.quotes = dbot.db.quoteArrs;
this.rmAllowed = true;
this.rmTimer;
this.internalAPI = {
// Retrieve a random quote from a given category, interpolating any quote
// references (~~QUOTE CATEGORY~~) within it
'interpolatedQuote': function(server, channel, user, key, quoteTree) {
if(!_.isUndefined(quoteTree) && quoteTree.indexOf(key) != -1) {
return '';
} else if(_.isUndefined(quoteTree)) {
quoteTree = [];
}
var index = _.random(0, this.quotes[key].length - 1);
var quoteString = this.quotes[key][index];
// Parse quote interpolations
var quoteRefs = quoteString.match(/~~([\d\w\s-]*)~~/g);
var thisRef;
while(quoteRefs && (thisRef = quoteRefs.shift()) !== undefined) {
var cleanRef = dbot.cleanNick(thisRef.replace(/^~~/,'').replace(/~~$/,'').trim());
if(cleanRef === '-nicks-') {
var randomNick = dbot.api.users.getRandomChannelUser(server, channel);
quoteString = quoteString.replace("~~" + cleanRef + "~~", randomNick);
quoteTree.pop();
} else if(cleanRef === '-user-') {
quoteString = quoteString.replace("~~" + cleanRef + "~~", user);
quoteTree.pop();
} else if(_.has(this.quotes, cleanRef)) {
quoteTree.push(key);
quoteString = quoteString.replace("~~" + cleanRef + "~~",
this.internalAPI.interpolatedQuote(server, channel, user, cleanRef, quoteTree.slice()));
quoteTree.pop();
// Parse quote interpolations
'interpolatedQuote': function(server, channel, key, quote, callback) {
var quoteRefs = quote.match(/~~([\d\w\s-]*)~~/g);
if(quoteRefs) {
var ref = this.internalAPI.cleanRef(quoteRefs[0].replace(/^~~/,'').replace(/~~$/,'').trim());
if(ref === '-nicks-') {
dbot.api.users.getRandomChannelUser(server, channel, function(user) {
quote = quote.replace('~~' + ref + '~~', randomNick);
this.internalAPI.interpolatedQuote(server, channel, key, quote, callback);
}.bind(this));
} else {
this.api.getQuote(ref, function(interQuote) {
if(!interQuote || ref == key) {
interQuote = '';
}
quote = quote.replace('~~' + ref + '~~', interQuote);
this.internalAPI.interpolatedQuote(server, channel, key, quote, callback);
}.bind(this));
}
} else {
callback(quote);
}
return quoteString;
}.bind(this),
'cleanRef': function(key) {
key = key.toLowerCase();
while(key.slice(-1) == '_') {
key = key.substring(0, key.length-1);
}
return key;
},
'resetRemoveTimer': function(event, key, quote) {
this.rmAllowed = false;
setTimeout(function() {
@ -57,7 +53,7 @@ var quotes = function(dbot) {
});
clearTimeout(this.rmTimer);
if(this.rmCache.length < dbot.config.quotes.rmLimit) {
if(this.rmCache.length < this.config.rmLimit) {
this.rmTimer = setTimeout(function() {
this.rmCache.length = 0; // lol what
}.bind(this), 600000);
@ -70,37 +66,81 @@ var quotes = function(dbot) {
};
this.api = {
'getQuote': function(event, category) {
var key = category.trim().toLowerCase();
var altKey;
if(key.split(' ').length > 0) {
altKey = key.replace(/ /g, '_');
}
'addQuote': function(key, quote, user, callback) {
var key = key.toLowerCase().trim(),
newCount,
category = false;
if(key.charAt(0) !== '_') { // lol
if(_.has(this.quotes, key)) {
return this.internalAPI.interpolatedQuote(event.server, event.channel.name, event.user, key);
} else if(_.has(this.quotes, altKey)) {
return this.internalAPI.interpolatedQuote(event.server, event.channel.name, event.user, altKey);
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
if(!category) {
var id = uuid.v4();
category = {
'id': id,
'name': key,
'quotes': [],
'owner': user
};
}
if(_.include(category.quotes, quote)) {
callback(false);
} else {
return false;
newCount = category.quotes.push(quote);
this.db.save('quote_category', category.id, category, function(err) {
this.rmAllowed = true;
callback(newCount);
}.bind(this));
}
}
}.bind(this));
},
'getQuote': function(key, callback) {
this.api.getQuoteCategory(key, function(category) {
if(category) {
var quotes = category.quotes;
var index = _.random(0, quotes.length - 1);
callback(quotes[index]);
} else {
callback(false);
}
});
},
'getQuoteCategory': function(name) {
console.log(name);
var key = name.trim().toLowerCase();
if(_.has(this.quotes, key)) {
return this.quotes[key];
} else {
return false;
}
'getInterpolatedQuote': function(server, channel, key, callback) {
key = key.trim().toLowerCase(),
this.api.getQuote(key, function(quote) {
if(quote) {
this.internalAPI.interpolatedQuote(server, channel, key, quote, callback);
} else {
callback(quote);
}
}.bind(this));
},
'getQuoteCategory': function(key, callback) {
var category = false,
key = key.trim().toLowerCase();
this.db.search('quote_category', { 'name': key }, function(result) {
category = result;
}, function(err) {
callback(category);
});
},
'getCategoryKeys': function(callback) {
var keys = [];
this.db.scan('quote_category', function(result) {
if(result) keys.push(result.name);
}, function(err) {
callback(keys);
});
}
};
this.api['getQuoteCategory'].external = true;
this.api['getQuoteCategory'].extMap = [ 'name' ];
this.listener = function(event) {
if(event.action == 'PRIVMSG') {
@ -111,17 +151,17 @@ var quotes = function(dbot) {
}
if(once) {
event.message = '~qadd realityonce=reality ' + once[1];
event.action = 'PRIVMSG';
event.params = event.message.split(' ');
dbot.instance.emit(event);
}
this.api.addQuote('realityonce', 'reality' + once[1], event.user, function(newCount) {
event.reply('\'reality ' + once[1] + '\' saved (' + newCount + ').');
});
}
} else if(event.action == 'JOIN') {
if(this.config.quotesOnJoin == true) {
var userQuote = this.api.getQuote(event, event.user)
if(userQuote) {
event.reply(event.user + ': ' + this.api.getQuote(event, event.user));
}
this.api.getQuote(event.user, function(quote) {
if(quote) {
event.reply(event.user + ': ' + quote);
}
});
}
}
}.bind(this);

View File

@ -5,7 +5,8 @@
"na'vi": "{category} yawne ke lu kawturu.",
"cy": "Does neb yn caru {category}",
"nl": "Niemand houdt van {category}",
"de": "Niemand hat {category} gerne"
"de": "Niemand hat {category} gerne",
"fr": "Personne n'aime {category}"
},
"large_categories": {
"en": "Largest categories: ",
@ -13,7 +14,8 @@
"na'vi": "U atsawl: ",
"cy": "Categoriau mwyaf: ",
"nl": "Grootste categoriën: ",
"de": "Größte Kategorien: "
"de": "Größte Kategorien: ",
"fr": "Les plus grandes catégories :"
},
"empty_category": {
"en": "That category has no quotes in. Commence incineration.",
@ -21,7 +23,8 @@
"na'vi": "Tsauru upxare lu. Nga skxawng lu.",
"cy": "Nid yw'r categori yna efo dyfyniadau. Cychwyn orfflosgiad",
"nl": "Die categorie heeft geen quotes. Bereid het verbrandingsritueel voor.",
"de": "In dieser Kategorie befinden sich keine Zitate. Verbennung einleiten."
"de": "In dieser Kategorie befinden sich keine Zitate. Verbennung einleiten.",
"fr": "Cette catégorie ne contient aucune citation. Commencer l'incinération."
},
"no_results": {
"en": "No results found.",
@ -29,7 +32,8 @@
"na'vi": "Oel kea humit rìmun",
"cy": "Dim canlyniadau ar gael",
"nl": "Geen resultaten gevonden.",
"de": "Keine Ergebnisse gefunden."
"de": "Keine Ergebnisse gefunden.",
"fr": "Aucun résultat trouvé."
},
"locked_category": {
"en": "{category} is locked. Commence incineration.",
@ -37,7 +41,8 @@
"na'vi": "{category} ke fkeytok set. Nga skxawng lu nafì'u",
"cy": "Mae {category} wedi cloi. Cychwyn orfflosgiad",
"nl": "{category} is op slot. Bereid het verbrandingsritueel voor.",
"de": "{category} ist geschlossen. Verbrennung einleiten."
"de": "{category} ist geschlossen. Verbrennung einleiten.",
"fr": "{category} est verrouillé(e). Commencer l'incinération."
},
"no_quotes": {
"en": "No quotes exist under {category}",
@ -45,7 +50,8 @@
"na'vi": "Kea upxare fkeytok {category}mì",
"cy": "Does dim dyfyniadau gan {category}",
"nl": "Er zijn geen quotes in {category}",
"de": "Es existieren keine Ziatate in {category}"
"de": "Es existieren keine Ziatate in {category}",
"fr": "Il n'existe aucune citation dans {category}"
},
"last_removed": {
"en": "Last quote removed from {category}.",
@ -53,7 +59,8 @@
"na'vi": "Oel 'upxareti aham 'aku {category}ta",
"cy": "Dyfyniad olaf wedi ei ddileu o {category}",
"nl": "De laatste quote is verwijderd uit {category}.",
"de": "Das letzte Zitat wurde aus {category} entfernt."
"de": "Das letzte Zitat wurde aus {category} entfernt.",
"fr": "Dernière citation enlevée de {category}."
},
"no_recent_adds": {
"en": "No quotes were added recently.",
@ -61,7 +68,8 @@
"na'vi": "Kea upxareti samung nìfkrr",
"cy": "Nid oes unrhyw dyfyniadau wedi ei ychwwanegu'n ddiweddar",
"nl": "Er zijn recentelijk geen quotes toegevoegd.",
"de": "Es wurden neulich keine Zitate hinzugefügt."
"de": "Es wurden neulich keine Zitate hinzugefügt.",
"fr": "Aucune citation n'a été ajoutée récemment."
},
"rmlast_spam": {
"en": "No spamming that shit. Try again in a few minutes...",
@ -69,7 +77,8 @@
"na'vi": "Nga Tsasngelit ke zene fpivere'. Sweylu nga fmivi ye'rìn...",
"cy": "Peidiwch a sbamio hwna. Triwch eto mewn ychydyg funudau...",
"nl": "Dat spammen kun je ook lekker laten. Probeer het zometeen nog eens...",
"de": "Hör auf zu spammen. Versuche es in einigen Minuten erneut..."
"de": "Hör auf zu spammen. Versuche es in einigen Minuten erneut...",
"fr": "Pas de spam de cette merde. Réessaye dans quelques minutes..."
},
"removed_from": {
"en": "'{quote}' removed from {category}",
@ -77,7 +86,8 @@
"na'vi": "'{quote}'(it/ti) 'ìyaku {category}",
"cy": "'{quote}' wedi ei ddileu o {category}",
"nl": "{quote} verwijderd uit {category}",
"de": "'{quote} aus {category} gelöscht"
"de": "'{quote} aus {category} gelöscht",
"fr": "'{quote}' enlevé(e) {category}"
},
"q_not_exist_under": {
"en": "'{quote}' doesn't exist under '{category}'.",
@ -85,7 +95,8 @@
"na'vi": "'{quote}' ke fkeytok '{category}'ta.",
"cy": "Nid yw '{quote}' yn bodoli yn '{category}'",
"nl": "{quote} bestaat niet in '{category}'.",
"de": "'{quote} existiert nicht in '{category}'."
"de": "'{quote} existiert nicht in '{category}'.",
"fr": "'{quote}' ne semble pas exister dans '{category}'."
},
"total_quotes": {
"en": "Total quote count: {count}.",
@ -93,7 +104,8 @@
"na'vi": "'upxareri holpxay: {count}.",
"cy": "Cyfanswm dyfyniadau: {count}.",
"nl": "Totaal aantal quotes: {count}.",
"de": "Anzahl Zitate insgesamt: {count}."
"de": "Anzahl Zitate insgesamt: {count}.",
"fr": "Nombre total de citations: {count}."
},
"quote_exists": {
"en": "Quote already in DB. Initiate incineration.",
@ -101,7 +113,8 @@
"na'vi": "'Upxarel säomumit fìtsengit tok srekrr. Nga skxawng lu.",
"cy": "Dyfyniad yn y gronfa ddata yn barod. Cychwyn orfflosgiad",
"nl": "De quote bestaat al. Bereid het verbrandingsritueel voor.",
"de": "Zitat ist bereits in der Datenbank. Verbennung einleiten."
"de": "Zitat ist bereits in der Datenbank. Verbennung einleiten.",
"fr": "La citation existe déjà dans la base de données. Initier l'incinération."
},
"quote_saved": {
"en": "Quote saved in '{category}' ({count}).",
@ -109,7 +122,8 @@
"na'vi": "Oe zayerok '{category}'mì ({count}).",
"cy": "Dyfyniad wedi ei gadw yn '{category}' ({count}).",
"nl": "Quote opgeslagen in '{category}' ({count}).",
"de": "Zitat in '{category}' gespeichert ({count})."
"de": "Zitat in '{category}' gespeichert ({count}).",
"fr": "Citation sauvegardée dans '{category}' ({count})."
},
"quote_replace": {
"en": "No replacing arrays, you whore.",
@ -117,7 +131,8 @@
"na'vi": "Ngal fìsäomumit ke tsun rivawn. Nga muntxa sayi suteo hrh.",
"cy": "Peidiwch a newid rhestrau, y cachgi",
"nl": "Geen arrays vervangen, slet.",
"de": "Ersetze keine Arrays, du Schlampe."
"de": "Ersetze keine Arrays, du Schlampe.",
"fr": "On ne remplace pas les arrays, espèce de salope."
},
"quote_count": {
"en": "{category} has {count} quotes.",
@ -125,7 +140,8 @@
"na'vi": "{count}a upxare {category}ur lu.",
"cy": "{count} dyfyniad yn {category}",
"nl": "{category} heeft {count} quotes.",
"de": "{count} Zitate befinden sich in {category}."
"de": "{count} Zitate befinden sich in {category}.",
"fr": "{category} contient {count} citations."
},
"quote_link": {
"en": "Link to {category} - {url}",
@ -133,7 +149,8 @@
"na'vi": "Fya'o {category}ne - {url}",
"cy": "Dolen i {category} - {url}",
"nl": "Link naar {category} - {url}",
"de": "Link zu {category} - {url}"
"de": "Link zu {category} - {url}",
"fr": "Lien vers {category} - {url}"
},
"search_results": {
"en": "{category} ({needle}): '{quote}' [{matches} results]",
@ -141,47 +158,66 @@
"na'vi": "{category} ({needle}): '{quote}' [kum a{matches}]",
"cy": "{category} ({needle}): '{quote}' [{matches} canlyniad]",
"nl": "{category} ({needle}): '{quote}' [{matches} resultaten]",
"de": "{category} ({needle}): '{quote}' [{matches} Ergebnisse]"
"de": "{category} ({needle}): '{quote}' [{matches} Ergebnisse]",
"fr": "{category} ({needle}): '{quote}' [{matches} résultats]"
},
"quote_cache_auto_remove": {
"en": "There are {count} quotes in the removal cache, which will be automatically cleared.",
"na'vi": "{count}a 'upxarel sngelit tok, Oel 'ayku sngelit lukenga.",
"cy": "Mae {count} dyfyniadau yn y celc dileu a fydd yn cael ei clirio yn awtomatig.",
"nl": "Er staan {count} quotes klaar om verwijderd te worden, wat automatisch zal gebeuren.",
"de": "Es befinden sich {count} im Cache, die automatisch entfernt werden."
"de": "Es befinden sich {count} im Cache, die automatisch entfernt werden.",
"fr": "Il y a {count} citations dans le cache de suppression, qui va être automatiquement vidé."
},
"quote_cache_manual_remove": {
"en": "There are {count} quotes in the removal cache, which must be manually cleared.",
"na'vi": "{count}a 'upxarel sngelit tok slä oel ke 'ayku sngelit tafral nga zene 'aivku",
"cy": "Mae {count} dyfyniadau yn y celc dileu a fydd yn cael ei clirio â llaw.",
"nl": "Er staan {count} quotes klaar om verwijderd te worden, wat handmatig zal moeten gebeuren.",
"de": "Es befinden sich {count} im Cache, die manuell entfernt werden müssen."
"de": "Es befinden sich {count} im Cache, die manuell entfernt werden müssen.",
"fr": "Il y a {count} citations dans le cache de suppression, qui doit être manuellement vidé."
},
"quote_cache_cleared": {
"en": "{count} quotes cleared from the removal cache.",
"na'vi": "Oel 'aìmku {count}a 'upxareti ta sngel.",
"cy": "{count} dyfyniadau wedi ei clirio o'r celc dileu",
"nl": "{count} quotes verwijderd uit de verwijderlijst.",
"de": "{count} Zitate wurden aus dem Cache entfernt."
"de": "{count} Zitate wurden aus dem Cache entfernt.",
"fr": "{count} citation(s) supprimée(s) du cache de suppression."
},
"quote_cache_reinstated": {
"en": "{count} quotes reinstated from the removal cache.",
"na'vi": "{count}a 'upxare tolätxaw ta sngel.",
"cy": "{count} dyfyniadau wedi ei adfer o'r celc dileu",
"nl": "{count} quotes hersteld van de verwijderlijst.",
"de": "{count} Zitate wurden aus dem Cache gerettet."
"de": "{count} Zitate wurden aus dem Cache gerettet.",
"fr": "{count} citation(s) restaurée(s) du cache de suppression."
},
"rm_cache_limit": {
"en": "Attention: Too many quotes removed, rmCache must be cleared or reinstated manually with ~rmconfirm or ~rmdeny.",
"na'vi": "Oel zerok 'upxareti apxay set, sweylu txo nga 'aivku upxareti ìlä ~rmconfirm fu ~rmdeny.",
"cy": "Sylw: Gormod o dyfyniadau wedi ei clirio, rhaid i'r celc dileu yn cael ei glirio neu adfer â llaw gyda ~rmconfirm neu ~rmdeny.",
"nl": "Waarschuwing: Er zijn te veel quotes verwijderd. rmCache moet opgeschoond of herinstalleerd worden met ~rmconfirm of ~rmdeny",
"de": "Achtung: Zu viele Zitate gelöscht, rmCache muss geleert werden oder manuell via ~rmconfirm oder ~rmdeny wiedereingesetzt werden."
"de": "Achtung: Zu viele Zitate gelöscht, rmCache muss geleert werden oder manuell via ~rmconfirm oder ~rmdeny wiedereingesetzt werden.",
"fr": "Attention : Trop de citations enlevées, rmCache doit être vidé ou restauré manuellement avec ~rmconfirm ou ~rmdeny."
},
"web_not_configured": {
"en": "Cannot link to category. Web module is either not loaded or misconfigured.",
"cy": "Ni all gysylltu â gategori. Modiwl we yn naill ai nid lwytho neu wedi ei camffurfweddu.",
"nl": "De categorie kan niet gelinkt worden. De web module is niet geladen of onjuist geconfigureerd.",
"de": "Diese Kategorie kann nicht gelinkt werden. Das Web-Modul ist entweder nicht geladen oder falsch konfiguriert."
"de": "Diese Kategorie kann nicht gelinkt werden. Das Web-Modul ist entweder nicht geladen oder falsch konfiguriert.",
"fr": "Impossible de donner le lien vers la catégorie. Le module web n'est pas chargé ou est mal configuré."
},
"category_renamed": {
"en": "Category ~{oldName} renamed to ~{newName}.",
"fr": "Catégorie ~{oldName} renommée ~{newName}."
},
"newcat_exists": {
"en": "Category ~{newcat} already exists. Delete that first or use ~qmerge.",
"fr": "La catégorie ~{newcat} existe déjà. Supprimez-la en premier ou utilisez ~qmerge."
},
"categories_merged": {
"en": "Category ~{from} and its quotes merged into ~{into}.",
"fr": "La catégorie ~{from} et ses citations ont été fusionnée dans ~{into}."
}
}

View File

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

View File

@ -4,96 +4,110 @@
"na'vi": "tompa ke zup {place}ur.",
"cy": "Dyw hi ddim yn bwrw glaw yn {place}.",
"nl": "Het regent niet in {place}.",
"de": "Es ist trocken in {place}."
"de": "Es ist trocken in {place}.",
"fr": "Il ne pleut pas à {place}."
},
"rain-1": {
"en" : "It's raining in {place}.",
"na'vi":"tompa zup {place}ur.",
"cy": "Mae'n bwrw glaw yn {place}.",
"nl": "Het regent in {place}.",
"de": "Es regnet in {place}."
"de": "Es regnet in {place}.",
"fr": "Il pleut à {place}."
},
"rain-2": {
"en" : "It's raining rather a lot in {place}.",
"na'vi":"tompa zup {place}ur nìhawng.",
"cy": "Mae'n bwrw glaw cryn dipyn yn {place}.",
"nl": "Het regent tamelijk hard in {place}.",
"de": "Es regnet relativ viel in {place}."
"de": "Es regnet relativ viel in {place}.",
"fr": "Il pleut assez fortement à {place}."
},
"rain-3": {
"en" : "It's raining shitloads.",
"na'vi":"tompa zup {place}ur nìhawng.",
"cy": "Mae'n bwrw glaw yn uffern o lawer yn {place}.",
"nl": "Het regent nu toch wel erg hard.",
"de": "Es regnet stark."
"de": "Es regnet stark.",
"fr": "Il pleut à mort."
},
"rain-4": {
"en" : "It's raining fucktons.",
"na'vi": "tompa zup {place}ur nìhawng.",
"cy": "Mae'n bwrw glaw yn drwm iawn yn {place}.",
"nl": "De regen komt verdorie met bakken uit de lucht.",
"de": "Es regnet extrem stark."
"de": "Es regnet extrem stark.",
"fr": "Il pleut vraiment des putains de tonnes."
},
"rain-5": {
"en" : "It's raining whales (in {place})!",
"na'vi": "payoang zup {place}ur",
"cy": "Mae'n bwrw glaw cathod a chŵn yn {place}.",
"nl": "Een punker krijgt z'n hanekam niet meer omhoog gehouden.",
"de": "Es regnet unglaublich stark (in {place})!"
"de": "Es regnet unglaublich stark (in {place})!",
"fr": "Il pleut des baleines (à {place}) !"
},
"rain-6": {
"en" : "IT'S SO FUCKING WET OUT HERE MAN",
"na'vi" : "pey lu oeru nìhawng taluna tok fìtengeti",
"cy": "MAE'N WIR GWLYB TU ALLAN.",
"nl": "Dit is dus een inloopdouche.",
"de": "ES IST SO VERDAMMT NASS HIER MANN."
"de": "ES IST SO VERDAMMT NASS HIER MANN.",
"fr": "C'EST TELLEMENT MOUILLE DEHORS MEC"
},
"rain-7": {
"en" : "I AM SO MOIST RIGHT NOW",
"na'vi":"pey lu oeru nìhawng taluna tok fìtengeti",
"cy": "RYDW I'N LLAITH IAWN AR HYN O BRYD",
"nl": "IK VOEL ME NU TOCH ANDERS WEL BEHOORLIJK ONDER GEZEKEN.",
"de": "ICH BIN SO EINGENÄSST."
"de": "ICH BIN SO EINGENÄSST.",
"fr": "JE SUIS TELLEMENT MOITE LA"
},
"rain-8": {
"en" : "You used a #3 pencil instead of #2, didn't you?",
"na'vi": "Nga pamrel sami hu pencìl a#3 ulte ke pamrel sami pencìl a#2 kefyak?",
"cy": "Rydych yn defnyddio #2 pensil yn hytrach na #3 pensil, ie?",
"nl": "PROOST.",
"de": "Jemand muss dich hassen"
"de": "Jemand muss dich hassen",
"fr": "T'as utilisé un crayon #3 au lieu d'un #2, n'est-ce pas ?"
},
"rain-9": {
"en" : "WELCOME TO ATLANTIS",
"na'vi": "ziva'u nìprrte atlantisftu.",
"cy": "CROESO I ATLANTIS!",
"nl": "HAD IK GEVRAAGD OM EEN OMMETJE ATLANTIS?!",
"de": "WILLKOMMEN IN ATLANTIS"
"de": "WILLKOMMEN IN ATLANTIS",
"fr": "BIENVENUE A ATLANTIS"
},
"rain-10": {
"en" : "GET INSIDE",
"na'vi": "Sweylu txo nga livatam futa ngal fìtsengeti tok",
"nl": "SCHUIL, BITCH! SCHUIL!",
"de": "REIN MIT DIR"
"de": "REIN MIT DIR",
"fr": "RENTRE CHEZ TOI"
},
"rain-11": {
"en" : "LOL U PROBABLY DIED",
"na'vi": "hrh kxawm nga tolerkup.",
"cy": "EWCH I MEWN",
"nl": "HA, NU BEN JE TOCH ZEKER VERZOPEN?!",
"de": "LOL DU MIST VERMUTLICH TOT"
"de": "LOL DU MIST VERMUTLICH TOT",
"fr": "LOL T'ES PROBABLEMENT MORT"
},
"rain-e": {
"en" : "Unable to fetch weather data.",
"na'vi":"Oel ke tsun rivum sänumeti teriyafkeyk.",
"cy": "Nid gallu nôl data tywydd.",
"nl": "Geen weerinformatie beschikbaar.",
"de": "Keine Wetterdaten vorhanden."
"de": "Keine Wetterdaten vorhanden.",
"fr": "Impossible de récupérer les données météo."
},
"rain-u": {
"en": "Location too ambiguous to process.",
"na'vi":"tsengeri, yayayr lu oeru.",
"cy": "Lleoliad yn rhy amwys i brosesu.",
"nl": "Deze locatie is te onduidelijk.",
"de": "Wähle den nächst größeren Ort."
"de": "Wähle den nächst größeren Ort.",
"fr": "Location trop ambiguë à traiter."
}
}

View File

@ -1,3 +1,4 @@
{
"outputPrefix": "\u000312reddit\u000f"
"outputPrefix": "\u000312reddit\u000f",
"dependencies": [ "link" ]
}

View File

@ -2,20 +2,24 @@
"about_subreddit": {
"en": "[{display_name} has {subscribers} subscribers ({active} active)]",
"cy": "[Mae {display_name} wedi {subscribers} subscribers ({active} active)]",
"de": "[{display_name} hat {subscribers} Abonnenten ({active} active)]"
"de": "[{display_name} hat {subscribers} Abonnenten ({active} active)]",
"fr": "[{display_name} a {subscribers} abonnés ({active} actifs)]"
},
"about_post": {
"en": "[Post by {poster} in {subreddit} — Comments: {comments}, Score: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)] — {url}",
"cy": "[Postio gan {poster} yn {subreddit} - Sylwadau: {comments}, Sgôr: {score} (\u00039]▲{up}\u000f|\u000312{down}▼\u000f)] — {url}",
"de": "[Beitrag von {poster} in {subreddit} — Kommentare: {comments}, Bewertung: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)] — {url}"
"de": "[Beitrag von {poster} in {subreddit} — Kommentare: {comments}, Bewertung: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)] — {url}",
"fr": "[Message de {poster} dans {subreddit} — Commentaires: {comments}, Score: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)] — {url}"
},
"about_comment": {
"en": "[Comment by {poster} in {subreddit} — Score: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)]",
"cy": "[Sylw gan {poster} yn {subreddit} - Sgôr: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)]",
"de": "[Kommentar von {poster} in {subreddit} — Bewertung: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)]"
"de": "[Kommentar von {poster} in {subreddit} — Bewertung: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)]",
"fr": "[Commentaire de {poster} dans {subreddit} — Score: {score} (\u00039▲{up}\u000f|\u000312{down}▼\u000f)]"
},
"nsfw": {
"en": "[NSFW]",
"cy": "[Anniogel Ar Gwaith (AAG)]"
"cy": "[Anniogel Ar Gwaith (AAG)]",
"fr": "[Risqué Pour le Travail (NSFW)]"
}
}

View File

@ -1,4 +1,3 @@
{
"ignorable": true,
"help": "http://github.com/reality/depressionbot/blob/master/modules/regex/README.md"
"ignorable": true
}

View File

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

44
modules/report/pages.js Normal file
View File

@ -0,0 +1,44 @@
var _ = require('underscore')._;
var pages = function(dbot) {
return {
'/notify': function(req, res) {
res.render('servers', {
'name': dbot.config.name,
'servers': _.keys(dbot.config.servers)
});
},
'/notify/:server': function(req, res) {
var server = req.params.server;
res.render('channels', {
'name': dbot.config.name,
'server': server,
'channels': _.keys(dbot.instance.connections[server].channels)
});
},
'/notify/:server/:channel': function(req, res) {
var server = req.params.server,
channel = req.params.channel,
notifies = [];
this.db.search('notifies', {
'server': server,
'channel': channel
}, function(notify) {
notifies.push(notify);
}, function(err) {
res.render('notifies', {
'name': dbot.config.name,
'server': server,
'notifies': notifies
});
});
}
};
};
exports.fetch = function(dbot) {
return pages(dbot);
};

View File

@ -1,6 +1,11 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
uuid = require('node-uuid'),
async = require('async');
var report = function(dbot) {
if(!dbot.db.pending) dbot.db.pending = {};
this.pending = dbot.db.pending;
this.api = {
'notify': function(server, channel, message) {
var channel = dbot.instance.connections[server].channels[channel];
@ -12,42 +17,78 @@ var report = function(dbot) {
}
}, this);
_.each(ops, function(user) {
dbot.say(server, user.name, message);
}, this);
dbot.api.users.getChannel(server, channel, function(channel) {
if(channel) {
var perOps = channel.op;
if(this.config.notifyVoice) pOps = _.union(perOps, channel.voice);
async.eachSeries(ops, function(nick, next) {
dbot.api.users.resolveUser(server, nick, function(user) {
perOps = _.without(perOps, user.id); next();
});
}, function() {
offlineUsers = perOps;
_.each(offlineUsers, function(id) {
if(!this.pending[id]) this.pending[id] = [];
this.pending[id].push(message);
}.bind(this));
}.bind(this));
}
}.bind(this));
var i = 0;
var notifyChannel = function(ops) {
if(i >= ops.length) return;
dbot.say(server, ops[i].name, message);
setTimeout(function() {
i++; notifyChannel(ops);
}, 1000);
};
notifyChannel(ops);
}
};
this.listener = function(event) {
if(_.has(this.pending, event.rUser.id)) {
_.each(this.pending[event.rUser.id], function(message) {
dbot.say(event.server, event.rUser.currentNick, message);
});
delete this.pending[event.rUser.id];
}
}.bind(this);
this.on = 'JOIN';
var commands = {
'~report': function(event) {
var channelName = event.input[1];
var nick = event.input[2];
var reason = event.input[3];
var channelName = event.input[1],
nick = event.input[2],
reason = event.input[3];
if(_.has(event.allChannels, channelName)) {
if(dbot.api.users.isChannelUser(event.server, nick, channelName, true)) {
nick = dbot.api.users.resolveUser(event.server, nick, true);
this.api.notify(event.server, channelName, dbot.t('report', {
'reporter': event.user,
'reported': nick,
'channel': channelName,
'reason': reason
}));
event.reply(dbot.t('reported', { 'reported': nick }));
dbot.api.users.resolveUser(event.server, nick, function(reportee) {
if(_.has(event.allChannels, channelName)) {
if(reportee) {
this.api.notify(event.server, channelName, dbot.t('report', {
'reporter': event.user,
'reported': nick,
'channel': channelName,
'reason': reason
}));
event.reply(dbot.t('reported', { 'reported': nick }));
} else {
event.reply(dbot.t('user_not_found', {
'reported': nick,
'channel': channelName
}));
}
} else {
event.reply(dbot.t('user_not_found', {
'reported': nick,
'channel': channelName
}));
}
} else {
event.reply(dbot.t('not_in_channel', { 'channel': channelName }));
}
event.reply(dbot.t('not_in_channel', { 'channel': channelName }));
}
}.bind(this));
},
'~notify': function(event) {
var channelName = event.input[1];
var message = event.input[2];
var channelName = event.input[1],
message = event.input[2];
if(_.has(event.allChannels, channelName)) {
this.api.notify(event.server, channelName, dbot.t('notify', {
@ -55,6 +96,17 @@ var report = function(dbot) {
'notifier': event.user,
'message': message
}));
var id = uuid.v4();
this.db.save('notifies', id, {
'id': id,
'server': event.server,
'channel': channelName,
'user': event.user,
'time': new Date().getTime(),
'message': message
}, function() {});
event.reply(dbot.t('notified', {
'user': event.user,
'channel': channelName

View File

@ -4,33 +4,39 @@
"na'vi": "{reporter}ìl fpìl futa {reported} fe' lu taluna {reason}.",
"cy": "Sylw! {reporter} wedi adrodd {reported} yn {channel}. Y rheswm a rhoddwyd oedd: \"{reason}.\"",
"nl": "Waarschuwing: {reporter} heeft {reported} aangegeven in {channel}. De reden die gegeven werd was: \"{reason}.\"",
"de": "Achtung: {reporter} hat {reported} in {channel} gemeldet. Grund: \"{reason}.\""
"de": "Achtung: {reporter} hat {reported} in {channel} gemeldet. Grund: \"{reason}.\"",
"fr": "Attention : {reporter} a reporté {reported} dans {channel}. Raison donnée : \"{reason}.\""
},
"reported": {
"en": "Thank you, {reported} has been reported to the channel administrators.",
"na'vi": "Irayo si ngari, fìtsengìri ayeyktan omum teri {reported}it set.",
"cy": "Diolch, {reported} wedi cael ei adrodd i'r gweinyddwyr sianel.",
"nl": "Bedankt, {reported} is aangegeven bij de administrators.",
"de": "Danke, {reported} wurde bei den Administratoren dieses Kanals gemeldet."
"de": "Danke, {reported} wurde bei den Administratoren dieses Kanals gemeldet.",
"fr": "Merci, {reported} a été reporté aux administrateurs du canal."
},
"user_not_found": {
"en": "{reported} isn't a known user in {channel}.",
"na'vi": "Oel ke omum {reported}it mì {channel}.",
"cy": "Nid yw {reported} yn ddefnyddiwr hysbys yn {channel}.",
"nl": "{reported} is geen bestaande gebruiker in {channel}.",
"de": "{reported} ist kein bekannter Benutzer in {channel}."
"de": "{reported} ist kein bekannter Benutzer in {channel}.",
"fr": "{reported} n'est pas un utilisateur connu dans {channel}."
},
"not_in_channel": {
"en": "I am not present in {channel}.",
"na'vi": "Oel {channel}it ke tok.",
"cy": "Dydw i ddim yn bresennol yn {channel}.",
"nl": "Ik ben niet aanwezig in {channel}.",
"de": "Ich bin nicht in {channel} aktiv."
"de": "Ich bin nicht in {channel} aktiv.",
"fr": "Je ne suis pas présent dans {channel}."
},
"notify": {
"en": "Attention {channel}: {notifier}: {message}"
"en": "Attention {channel}: {notifier}: {message}",
"fr": "Attention {channel}: {notifier}: {message}"
},
"notified": {
"en": "{user}: {channel} staff notified."
"en": "{user}: {channel} staff notified.",
"fr": "{user}: l'équipe de {channel} a été notifiée."
}
}

View File

@ -0,0 +1,5 @@
{
"client_id": "391bcf67b5ad70fd64c6a4f79374f2f4",
"outputPrefix": "\u000307soundcloud\u000f",
"dependencies": [ "link" ]
}

View File

@ -0,0 +1,70 @@
/**
* Module Name: soundcloud
* Description: Various SoundCloud functionality
*/
var _ = require('underscore')._,
request = require('request');
var soundcloud = function(dbot) {
this.ApiRoot = 'http://api.soundcloud.com';
this.commands = {
'~soundcloud': function(event) {
var query = event.input[1];
request.get(this.ApiRoot + '/tracks.json', {
'qs': {
'client_id': this.config.client_id,
'q': query
},
'json': true
}, function(error, response, body) {
if(body.length != 0) {
body = body[0];
if(!body.genre) body.genre = '';
event.reply(dbot.t('sc_track', {
'title': body.title,
'artist': body.user.username,
'genre': body.genre.trim(),
'plays': body.playback_count,
'favs': body.favoritings_count
}) + ' — ' + body.permalink_url);
} else {
event.reply(dbot.t('sc_notrack'));
}
});
}
};
this.commands['~soundcloud'].regex = [/^~soundcloud (.+)$/, 2];
this.onLoad = function() {
dbot.api.link.addHandler(this.name,
/https?:\/\/(www\.)?soundcloud\.com\//,
function(event, match, name) {
var url = match.input;
request.get(this.ApiRoot + '/resolve.json', {
'qs': {
'client_id': this.config.client_id,
'url': url
},
'json': true
}, function(error, response, body) {
if(response.statusCode == 200) {
if(body.kind == 'track') {
if(!body.genre) body.genre = '';
event.reply(dbot.t('sc_track', {
'title': body.title,
'artist': body.user.username,
'genre': body.genre.trim(),
'plays': body.playback_count,
'favs': body.favoritings_count
}));
}
}
});
}.bind(this));
}.bind(this);
};
exports.fetch = function(dbot) {
return new soundcloud(dbot);
};

View File

@ -0,0 +1,10 @@
{
"sc_track": {
"en": "[{title} by {artist} — {genre} — \u000312▶\u000f{plays} \u000304♥\u000f{favs}]",
"fr": "[{title} par {artist} — {genre} — \u000312▶\u000f{plays} \u000304♥\u000f{favs}]"
},
"sc_notrack": {
"en": "No results found.",
"fr": "Aucun résultat trouvé."
}
}

View File

@ -1,4 +1,3 @@
{
"ignorable": true,
"help": "http://github.com/reality/depressionbot/blob/master/modules/spelling/README.md"
"ignorable": true
}

View File

@ -5,7 +5,8 @@
"na'vi": "Sweylu {correcter} pamrel sivi: {fix}",
"cy": "Oedd {correcter} yn feddwl: {fix}",
"nl": "{correcter} bedoelde: {fix}",
"de": "{correcter} meinte: {fix}"
"de": "{correcter} meinte: {fix}",
"fr": "{correcter} voulait dire : {fix}"
},
"spelling_other": {
"en": "{correcter} thinks {candidate} meant: {fix}",
@ -13,6 +14,7 @@
"na'vi": "{correcter} fpìl futa sweylu {candiate} pamrel sivi: {fix}",
"cy": "Mae {correcter} yn meddwl bod {candidate} yn feddwl: {fix}",
"nl": "{correcter} denkt dat {candidate} bedoelde: {fix}",
"de": "{corrector} denkt, dass {canditate} meinte: {fix}"
"de": "{corrector} denkt, dass {canditate} meinte: {fix}",
"fr": "{correcter} pense que {candidate} voulait dire : {fix}"
}
}

View File

@ -1,2 +1,28 @@
#spotify
dbot spotify module
## spotify
Various Spotify functionality.
### Description
This module posts information on Spotify links, as well as providing Spotify
search functionality.
## Commands
### ~spotify [query]
Search spotify for a song.
### ~syt [youtube link]
Attempt to get a Spotify link from a YouTube link. If no link is provided with
the commands, it will attempt to use the last link posted in the channel.
## API
#### spotifySearch(query, callback)
Run a search query on Spotify. If no results are found, the callback will pass
false. If a result is found, the callback takes two arguments: data about the
found track, and a link to the track.
### Hooks
#### link
Posts information about a Spotify link when one is posted in a channel.

View File

@ -1,5 +1,5 @@
{
"dependencies": [ "command" ],
"dependencies": [ "link" ],
"ignorable": true,
"outputPrefix": "\u00039spotify\u000f"
}

View File

@ -2,28 +2,33 @@
"artist": {
"en": "[artist] {artist}",
"cy": "[cyfansoddwr] {artist}",
"de": "[Interpret] {artist}"
"de": "[Interpret] {artist}",
"fr": "[artiste] {artist}"
},
"album": {
"en": "[album] {artist} {album}",
"cy": "[albwm] {artist} - {album}",
"de": "[Album] {artist} {album}"
"cy": "[albwm] {artist} {album}",
"de": "[Album] {artist} {album}",
"fr": "[album] {artist} {album}"
},
"track": {
"en": "[track] {artist} {track} (from {album})",
"cy": "[trac] {artist} - {track} (o'r albwm: {album})",
"de": "[Track] {artist} {track} (aus dem Album {album})"
"cy": "[trac] {artist} {track} (o'r albwm: {album})",
"de": "[Track] {artist} {track} (aus dem Album {album})",
"fr": "[piste] {artist} {track} (tiré de {album})"
},
"found": {
"en": "[{url}] {artist} {track} (from {album})",
"cy": "[{url}] {artist} - {track} (o'r albwm: {album})",
"de": "[{url}] {artist} {track} (aus dem Album {album})"
"cy": "[{url}] {artist} {track} (o'r albwm: {album})",
"de": "[{url}] {artist} {track} (aus dem Album {album})",
"fr": "[{url}] {artist} {track} (tiré de {album})"
},
"not-found": {
"en": "No results.",
"na'vi": "{s}, Oel ke tsun rivum ayuoti.",
"na'vi": "Oel ke tsun rivum ayuoti.",
"cy": "Dim canlyniadau.",
"nl": "Geen resultaten.",
"de": "Kein Ergebnis."
"de": "Kein Ergebnis.",
"fr": "Aucun résultat."
}
}

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

View File

@ -1,3 +0,0 @@
{
"help": "https://github.com/reality/depressionbot/blob/master/modules/timers/README.md"
}

View File

@ -5,8 +5,7 @@ Track users.
### Description
This module tracks users and their aliases through nick changes and all that
kind of thing. It's mainly a utility module for other modules to use. It's
also totally !insaned.
kind of thing. It's mainly a utility module for other modules to use.
### Commands
@ -27,30 +26,67 @@ Requires moderator level access by default.
### API
#### resolveUser(server, nick, [useLowerCase])
This resolves a given nick to its primary user and returns it.
#### resolveUser(server, nick, callback)
This resolves a given nick to its primary user, returning false if no user
record is found in the store associated with the given nickname (either as a
primary nick or an alias). The callback is called with one argument, a _user_
object or false if no user was found.
Note that if the useLowerCase argument is set to true, it will do a lower-case
search, however it will return the username in its properly capitalised form, so
remember to lower case the return value if you are using lower case values as
keys.
#### getUser(uuid, callback)
Get a user by its uuid. Callback is called with one argument, a _user_ object or
false if no user was found by that uuid.
#### resolveUser(server, user)
Return whether a user is known either as an alias or a primary user.
#### getChannel(server, channelName, callback)
This resolves a given server and channel name to a particular channel. The
callback is called with one argument, a _channel_ object or false if no channel
was found with the given name on the given server.
#### isPrimaryUser(server, nick)
Return whether a nick is known as a primary user.
#### getRandomChannelUser(server, channel, callback)
Given a server name and a channel name, this gets a random user from that
channel. Callback is called with one argument, a _user_ object or false if no
channel was found from the given parameters.
#### getAliases(server, user)
Return a list of aliases for a given primary user.
#### getAllusers(callback)
Get a list of all users the bot currently knows about. Callback is called with
one argument, a list of user records.
#### isOnline(server, user, channel, useLowerCase)
Return whether a user is online in a given channel on the given server.
#### isOnline
Is the given nick on the given server currently in the given channel. Callback
is called with one argument, a boolean as to whether the nick is currently in
the specified place.
### Data
#### User Object
{
id: user uuid,
primaryNick,
currentNick: Current or last used nickname,
server,
channels: A list of names for channels this user has been in,
aliases: A list of known aliases for this user
}
#### Channel Object
{
id: channel uuid,
server,
name,
users: A list of the uuids of users who are in this channel
}
### Events
#### nick_changed(server, newNick)
This is executed when a new alias is added for a user.
#### new_user(user)
This is executed when a new user is added to the known users DB.
#### new_user(server, nick)
This is executed when a new primary user is added to the known users DB.
#### new_user_alias(user, newNick)
When a new user alias is added.
#### new_channel(channel)
Executed when DBot creates a new channel object.
#### new_channel_user(user, channel)
Executed when DBot creates a new channel object.

View File

@ -1,4 +1,9 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
uuid = require('node-uuid'),
databank = require('databank'),
AlreadyExistsError = databank.AlreadyExistsError,
NoSuchThingError = databank.NoSuchThingError,
NotImplementedError = databank.NotImplementedError;
var api = function(dbot) {
var escapeRegexen = function(str) {
@ -6,100 +11,124 @@ var api = function(dbot) {
};
var api = {
'resolveUser': function(server, nick, useLowerCase) {
var knownUsers = this.getServerUsers(server);
var user = nick;
if(!useLowerCase) {
if(!_.include(knownUsers.users, nick) && _.has(knownUsers.aliases, nick)) {
user = knownUsers.aliases[nick];
// Return a user record given a primary nick or an alias
'resolveUser': function(server, nick, callback) {
var user = false;
this.db.search('users', { 'server': server }, function(result) {
if(result.primaryNick == nick || _.include(result.aliases, nick)) {
user = result;
}
} else {
// this is retarded
user = user.toLowerCase();
var toMatch = new RegExp("^" + escapeRegexen(user) + "$", "i");
}, function(err) {
if(!err) {
callback(user);
}
});
},
var resolvedUser = _.find(knownUsers.users, function(nick) {
return nick.match(toMatch) !== null;
}, this);
// Return a user record given a UUID
'getUser': function(uuid, callback) {
this.db.read('users', uuid, function(err, user) {
if(err) user = false;
callback(user);
});
},
if(!resolvedUser) {
resolvedUser = _.find(knownUsers.aliases, function(nick, alias) {
if(alias.match(toMatch) !== null) return nick;
'getChannel': function(server, channelName, callback) {
var channel = false;
this.db.search('channel_users', {
'server': server,
'name': channelName
}, function(result) {
channel = result;
}, function(err) {
if(!err) {
callback(channel);
}
});
},
'getRandomChannelUser': function(server, channel, callback) {
var channel;
this.db.search('channel_users', {
'server': server,
'channel': channel
}, function(result) {
channel = result;
}, function(err) {
if(!err) {
if(!_.isUndefined(channel.users)) {
var randomUser = channel.users[_.random(0, channel.users.length - 1)];
this.api.resolveUser(server, randomUser, function(user) {
callback(user);
});
} else {
callback(false);
}
}
});
},
'getAllUsers': function(callback) {
var users = [];
this.db.scan('users', function(user) {
users.push(user);
}, function(err) {
if(!err) {
callback(users);
}
});
},
'getAllChannels': function(callback) {
var channels = [];
this.db.scan('channel_users', function(channel) {
channels.push(channel);
}, function(err) {
if(!err) {
callback(channels);
}
});
},
'isOnline': function(server, nick, channel, callback) {
this.api.resolveUser(server, nick, function(user) {
var possiNicks = [user].concat(user.aliases);
if(_.has(dbot.instance.connections[server].channels, channel)) {
var onlineNicks = dbot.instance.connections[server].channels[channel].nicks;
var isOnline = _.any(onlineNicks, function(nick) {
nick = nick.name;
return _.include(possiNicks, nick);
}, this);
if(!_.isUndefined(resolvedUser)) user = resolvedUser;
callback(isOnline);
}
else{
user = resolvedUser;
});
},
'isKnownUser': function(server, nick, callback) {
this.api.resolveUser(server, nick, function(isKnown) {
if(isKnown == false) {
callback(false);
} else {
callback(true);
}
}
return user;
},
'getRandomChannelUser': function(server, channel) {
var channelUsers = this.getServerUsers(server).channelUsers[channel];
if(!_.isUndefined(channelUsers)) {
return channelUsers[_.random(0, channelUsers.length - 1)];
} else {
return false;
}
},
'getServerUsers': function(server) {
return dbot.db.knownUsers[server].users;
},
'getAllUsers': function() {
return _.reduce(dbot.db.knownUsers, function(memo, server, name) {
memo[name] = server.users;
return memo;
}, {}, this);
},
'isKnownUser': function(server, nick) {
var knownUsers = this.getServerUsers(server);
return (_.include(knownUsers.users, nick) || _.has(knownUsers.aliases, nick));
},
'isPrimaryUser': function(server, nick) {
var knownUsers = this.getServerUsers(server);
return _.include(knownUsers.users, nick);
},
'getAliases': function(server, nick) {
var knownUsers = this.getServerUsers(server);
return _.chain(knownUsers.aliases)
.keys()
.filter(function(user) {
return knownUsers.aliases[user] == nick;
}, this)
.value();
},
'isOnline': function(server, user, channel, useLowerCase) {
var user = this.api.resolveUser(server, user, useLowerCase);
var possiNicks = [user].concat(this.api.getAliases(server, user));
if(!_.has(dbot.instance.connections[server].channels, channel)) return false;
var onlineNicks = dbot.instance.connections[server].channels[channel].nicks;
return _.any(onlineNicks, function(nick) {
nick = nick.name;
return _.include(possiNicks, nick);
}, this);
},
'isChannelUser': function(server, user, channel, useLowerCase) {
var knownUsers = this.getServerUsers(server);
var user = this.api.resolveUser(server, user, useLowerCase);
if(!_.has(knownUsers.channelUsers, channel)) {
return false;
}
return _.include(knownUsers.channelUsers[channel], user);
});
}
};
api['resolveUser'].external = true;
api['resolveUser'].extMap = [ 'server', 'nick', 'callback' ];
api['getChannel'].external = true;
api['getChannel'].extMap = [ 'server', 'channel', 'callback' ];
api['getAllUsers'].external = true;
api['getAllUsers'].extMap = [ 'callback' ];
api['getAllChannels'].external = true;
api['getAllChannels'].extMap = [ 'callback' ];
return api;
};

View File

@ -3,106 +3,139 @@ var _ = require('underscore')._;
var commands = function(dbot) {
var commands = {
'~alias': function(event) {
var knownUsers = this.getServerUsers(event.server),
alias = event.params[1].trim();
var nick = event.params[1].trim() || event.user;
this.api.resolveUser(event.server, nick, function(user) {
if(user) {
if(nick == user.primaryNick) {
var aliases = _.first(user.aliases, 10);
var including = 'including: ' + aliases.join(', ') + '.';
if(_.include(knownUsers.users, alias)) {
var aliases = this.api.getAliases(event.server, alias);
var aliasCount = aliases.length;
if(aliasCount != 0) {
var aliases = _.first(aliases, 10);
var including = 'including: ' + aliases.join(', ') + '.';
event.reply(dbot.t('primary', {
'user': alias,
'count': aliasCount
}) + including);
if(user.aliases.length != 0) {
event.reply(dbot.t('primary', {
'user': nick,
'currentNick': user.currentNick,
'count': user.aliases.length,
}) + including);
} else {
event.reply(dbot.t('primary', {
'user': nick,
'currentNick': user.currentNick,
'count': user.aliases.length
}).slice(0, -2) + ".");
}
} else {
event.reply(dbot.t('alias', {
'alias': nick,
'user': user.primaryNick
}));
}
} else {
event.reply(dbot.t('primary', {
'user': alias,
'count': aliasCount
}).slice(0, -2) + ".");
event.reply(dbot.t('unknown_alias', { 'alias': nick }));
}
} else if(_.has(knownUsers.aliases, alias)) {
event.reply(dbot.t('alias', {
'alias': alias,
'user': knownUsers.aliases[alias]
}));
} else {
event.reply(dbot.t('unknown_alias', { 'alias': alias }));
}
});
},
'~addalias': function(event) {
var nick = event.input[1],
alias = event.input[2];
this.api.resolveUser(event.server, nick, function(user) {
if(user) {
if(!_.include(user.aliases, alias)) {
user.aliases.push(alias);
this.db.save('users', user.id, user, function(err) {
if(!err) {
event.reply(dbot.t('alias_added', {
'user': user.primaryNick,
'alias': alias
}));
}
});
} else {
event.reply(dbot.t('alias_exists', { 'alias': alias }));
}
} else {
event.reply(dbot.t('unknown_alias', { 'alias': nick }));
}
}.bind(this));
},
'~setaliasparent': function(event) {
var knownUsers = this.getServerUsers(event.server);
var newParent = event.params[1];
var newPrimary = event.params[1].trim();
this.api.resolveUser(event.server, newPrimary, function(user) {
if(user && user.primaryNick != newPrimary) {
var newAlias = user.primaryNick;
user.primaryNick = newPrimary;
user.aliases = _.without(user.aliases, newPrimary);
user.aliases.push(newAlias);
this.internalAPI.updateChannelPrimaryUser(event.server, newAlias, newPrimary);
if(_.has(knownUsers.aliases, newParent)) {
var newAlias = knownUsers.aliases[newParent];
// Replace user entry
knownUsers.users = _.without(knownUsers.users, newAlias);
knownUsers.users.push(newParent);
// Replace channels entries with new primary user
this.updateChannels(event, newAlias, newParent);
// Remove alias for new parent & add alias for new alias
delete knownUsers.aliases[newParent];
knownUsers.aliases[newAlias] = newParent;
// Update aliases to point to new primary user
this.updateAliases(event, newAlias, newParent);
event.reply(dbot.t('aliasparentset', {
'newParent': newParent,
'newAlias': newAlias
}));
return {
'server': event.server,
'alias': newAlias
};
} else {
event.reply(dbot.t('unknown_alias', { 'alias': newParent }));
}
return false;
this.db.save('users', user.id, user, function(err) {
if(!err) {
event.reply(dbot.t('aliasparentset', {
'newParent': newPrimary,
'newAlias': newAlias
}));
dbot.api.event.emit('~setaliasparent', {
'server': event.server,
'alias': newAlias
});
}
});
} else {
event.reply(dbot.t('unknown_alias', { 'alias': newPrimarj }));
}
}.bind(this));
},
'~mergeusers': function(event) {
var knownUsers = this.getServerUsers(event.server);
var primaryUser = event.params[1];
var secondaryUser = event.params[2];
if(_.include(knownUsers.users, primaryUser) && _.include(knownUsers.users, secondaryUser)) {
knownUsers.users = _.without(knownUsers.users, secondaryUser);
knownUsers.aliases[secondaryUser] = primaryUser;
this.updateAliases(event, secondaryUser, primaryUser);
this.updateChannels(event, secondaryUser, primaryUser);
event.reply(dbot.t('merged_users', {
'old_user': secondaryUser,
'new_user': primaryUser
}));
return {
'server': event.server,
'secondary': secondaryUser
};
} else {
event.reply(dbot.t('unprimary_error'));
}
return false;
}
this.api.resolveUser(event.server, primaryUser, function(user) {
if(user) {
this.api.resolveUser(event.server, secondaryUser, function(oldUser) {
if(oldUser) {
user.aliases.push(oldUser.primaryNick);
user.aliases = _.union(user.aliases, oldUser.aliases);
this.internalAPI.mergeChannelUsers(event.server, oldUser, user);
this.db.del('users', oldUser.id, function(err) {
if(!err) {
this.db.save('users', user.id, user, function(err) {
if(!err) {
this.internalAPI.mergeChannelUsers(event.server, secondaryUser, primaryUser);
event.reply(dbot.t('merged_users', {
'old_user': secondaryUser,
'new_user': primaryUser
}));
dbot.api.event.emit('~mergeusers', [
event.server,
oldUser,
user
]);
}
}.bind(this));
}
}.bind(this));
} else {
event.reply(dbot.t('unprimary_error', { 'nick': secondaryUser }));
}
}.bind(this));
} else {
event.reply(dbot.t('unprimary_error', { 'nick': primaryUser }));
}
}.bind(this));
}
};
commands['~alias'].regex = [/^~alias ([\d\w[\]{}^|\\`_-]+?)/, 2];
commands['~setaliasparent'].regex = [/^~setaliasparent ([\d\w[\]{}^|\\`_-]+?)/, 2];
commands['~mergeusers'].regex = [/^~mergeusers ([\d\w[\]{}^|\\`_-]+?)\s*?([\d\w[\]{}^|\\`_-]+?)/, 3];
commands['~addalias'].regex = [/^~addalias ([\d\w[\]{}^|\\`_-]+?) ([\d\w[\]{}^|\\`_-]+?)$/, 3];
commands['~setaliasparent'].access = 'moderator';
commands['~mergeusers'].access = 'moderator';
commands['~addalias'].access = 'moderator';
return commands;
};

View File

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

View File

@ -4,41 +4,55 @@
"na'vi": "ayfko syaw {user} {alias} nìteng",
"cy": "{alias} ydy enw arall o {user}",
"nl": "{alias} is een alias van {user}",
"de": "{alias} ist ein Nickname von {user}"
"de": "{alias} ist ein Nickname von {user}",
"fr": "{alias} est un alias de {user}"
},
"primary": {
"en": "{user} is a primary user with {count} aliases, ",
"na'vi": "{user} lu txin ulte {count}a stxo lu poru, ",
"cy": "Mae {user} yn ddefnyddiwr gynradd gyda {count} enwau eraill, ",
"nl": "{user} is een primaire gebruiker met {count} aliassen, ",
"de": "{user} ist ein Benutzer mit {count} Nicknamen, "
"en": "{user} (currently {currentNick}) is a primary user with {count} aliases, ",
"na'vi": "{user} ({currentNick}) lu txin ulte {count}a stxo lu poru, ",
"nl": "{user} ({currentNick}) is een primaire gebruiker met {count} aliassen, ",
"cy": "Mae {user} ({currentNick}) yn ddefnyddiwr gynradd gyda {count} enwau eraill, ",
"de": "{user} ({currentNick}) ist ein Benutzer mit {count} Nicknamen, ",
"fr": "{user} (actuellement {currentNick}) est un utilisateur primaire avec {count} alias, "
},
"unknown_alias": {
"en": "{alias} does not currently exist as an alias or known user.",
"na'vi": "{alias} ke fkeytok nìfkrr",
"cy": "Nid yw {alias} yn bodoli fel enw arall neu defnyddiwr yn hysbys.",
"nl": "{alias} staat momenteel niet bekend als een bestaande gebruiker of alias.",
"de": "{alias} existiert nicht oder ist kein Nickname eines Benutzers."
"de": "{alias} existiert nicht oder ist kein Nickname eines Benutzers.",
"fr": "{alias} n'existe pas actuellement en tant qu'alias ou utilisateur connu."
},
"aliasparentset": {
"en": "{newParent} is now the parent user, and {newAlias} is an alias.",
"na'vi": "{newParent} lu sa'sem set ulte {newAlias} lu stxo set nìteng.",
"cy": "Mae {newParent} ydy defnyddiwr rhiant nawr, a {alias} ydy enw arall.",
"nl": "{newParent} is nu de bovenliggende gebruiker, en {newAlias} is een alias.",
"de": "{newParent} ist nun der Hauptname und {newAlias} ist ein Nickname."
"de": "{newParent} ist nun der Hauptname und {newAlias} ist ein Nickname.",
"fr": "{newParent} est maintenant le même utilisateur parent, et {newAlias} est un alias."
},
"unprimary_error": {
"en": "One of those users isn't currently recorded as a primary user.",
"en": "{nick} isn't recorded as a primary user.",
"na'vi": "fo sute txin ke lu.",
"cy": "Nid yw un o'r defnyddwyr hynny yn cael ei gofnodi ar hyn o bryd fel defnyddiwr gynradd.",
"nl": "Een van deze gebruikers is nog niet bekend als een primaire gebruiker.",
"de": "Einer dieser Benutzer ist nicht als Hauptbenutzer gespeichert."
"de": "Einer dieser Benutzer ist nicht als Hauptbenutzer gespeichert.",
"fr": "{nick} n'est pas enregistré en tant qu'utilisateur primaire."
},
"merged_users": {
"en": "{old_user} and their aliases have been merged into {new_user}.",
"na'vi": "{old_user} ulte stxo alahe {new_user} lu set.",
"cy": "{old_user} a'u enwau eraill wedi cael eu cyfuno i mewn i {new_user}.",
"nl": "{old_user} en zijn aliassen zijn samengevoegd met {new_user}.",
"de": "{old_user} und seine Nicknamen wurden zusammengelegt in {new_user}."
"de": "{old_user} und seine Nicknamen wurden zusammengelegt in {new_user}.",
"fr": "{old_user} et ses alias ont été fusionnés dans {new_user}."
},
"alias_exists": {
"en": "Alias {alias} already exists.",
"fr": "L'alias {alias} existe déjà."
},
"alias_added": {
"en": "Alias {alias} added to {user}.",
"fr": "Alias {alias} ajouté à {user}."
}
}

View File

@ -2,93 +2,228 @@
* Name: Users
* Description: Track known users
*/
var _ = require('underscore')._;
var _ = require('underscore')._,
uuid = require('node-uuid'),
async = require('async');
var users = function(dbot) {
this.knownUsers = dbot.db.knownUsers;
this.getServerUsers = function(server) {
var knownUsers = this.knownUsers;
if(!_.has(knownUsers, server)) {
knownUsers[server] = { 'users': [], 'aliases': {}, 'channelUsers': {} };
}
if(!_.has(knownUsers[server], 'channelUsers')) {
knownUsers[server].channelUsers = {};
}
return knownUsers[server];
};
this.updateAliases = function(event, oldUser, newUser) {
var knownUsers = this.getServerUsers(event.server);
_.each(knownUsers.aliases, function(user, alias) {
if(user == oldUser) {
knownUsers.aliases[alias] = newUser;
/*** Internal API ***/
this.internalAPI = {
'createUser': function(server, nick, callback) {
var id = uuid.v4();
this.db.create('users', id, {
'id': id,
'primaryNick': nick,
'currentNick': nick,
'server': server,
'channels': [],
'aliases': []
}, function(err, result) {
if(!err) {
dbot.api.event.emit('new_user', [ result ]);
callback(result);
}
});
}.bind(this),
'createChannel': function(server, name, callback) {
var id = uuid.v4();
this.db.create('channel_users', id, {
'id': id,
'server': server,
'name': name,
'users': [],
'op': [],
'voice': []
}, function(err, result) {
if(!err) {
dbot.api.event.emit('new_channel', [ result ]);
callback(result);
}
});
}.bind(this),
'addChannelUser': function(channel, user, staff, callback) {
if(!_.include(channel.users, user.id)) {
channel.users.push(user.id);
}
}, this);
};
this.updateChannels = function(event, oldUser, newUser) {
var channelUsers = this.getServerUsers(event.server).channelUsers;
channelUsers = _.each(channelUsers, function(channel, channelName) {
channelUsers[channelName] = _.without(channel, oldUser);
channelUsers[channelName].push(newUser);
}, this);
};
this.listener = function(event) {
var knownUsers = this.getServerUsers(event.server);
var nick = event.user;
if(event.action == 'JOIN' && nick != dbot.config.name) {
if(!_.has(knownUsers.channelUsers, event.channel.name)) {
knownUsers.channelUsers[event.channel.name] = [];
if(!_.include(user.channels, channel.id)) {
user.channels.push(channel.id);
}
var channelUsers = knownUsers.channelUsers[event.channel.name];
if(this.api.isKnownUser(event.server, nick)) {
nick = this.api.resolveUser(event.server, nick);
if(!channel.op) channel.op = [];
if(!channel.voice) channel.voice = [];
if(staff.op) {
channel.op.push(user.id);
} else if(staff.voice) {
channel.voice.push(user.id);
}
this.db.save('users', user.id, user, function(err) {
this.db.save('channel_users', channel.id, channel, function(err) {
dbot.api.event.emit('new_channel_user', [ user, channel ]);
callback(err);
});
}.bind(this));
}.bind(this),
'modChannelStaff': function(channel, user, staff, callback) {
var needsUpdating = false;
if(!channel.op) {
channel.op = [];
needsUpdating = true;
}
if(!channel.voice) {
channel.voice = [];
needsUpdating = true;
}
if(!_.include(channel.op, user.id) && staff.op) {
channel.op.push(user.id);
needsUpdating = true;
} else if(!_.include(channel.voice, user.id) && staff.voice) {
channel.voice.push(user.id);
needsUpdating = true;
} else if(_.include(channel.op, user.id) && !staff.op) {
channel.op = _.without(channel.op, user.id);
needsUpdating = true;
} else if(_.include(channel.voice, user.id) && !staff.voice) {
channel.voice = _.without(channel.voice, user.id);
needsUpdating = true;
}
if(needsUpdating) {
this.db.save('channel_users', channel.id, channel, function(err) {
callback(err);
});
} else {
knownUsers.users.push(nick);
dbot.api.event.emit('new_user', [ event.server, nick ]);
callback();
}
}.bind(this),
'updateChannelPrimaryUser': function(server, oldUser, newUser) {
this.db.search('channel_users', { 'server': server }, function(channel) {
channel.users = _.without(channel.users, oldUser);
if(!_.include(channel.users, newUser)) channel.users.push(newUser);
this.db.save('channel_users', channel.id, channel, function(err) {
if(err) {
// QQ
}
});
}.bind(this), function(err) {
if(err) {
// QQ
}
});
}.bind(this),
'mergeChannelUsers': function(server, oldUser, newUser) {
newUser.channels = _.union(oldUser.channels, newUser.channels);
_.each(newUser.channels, function(name) {
this.api.getChannel(server, name, function(channel) {
if(_.include(channel.users, oldUser.id)) {
channel.users = _.without(channel.users, oldUser.id);
}
if(!_.include(channel.users, newUser.id)) {
channel.users.push(newUser.id);
}
this.db.save('channel_users', channel.id, channel, function(err) {
if(err) {
// QQ
}
});
}.bind(this));
}, this);
}.bind(this)
};
this.listener = function(event) {
if(event.action == 'JOIN' && event.user != dbot.config.name) {
if(!event.rUser) {
this.internalAPI.createUser(event.server, event.user, function(user) {
this.internalAPI.addChannelUser(event.rChannel, user, {}, function() {});
}.bind(this));
} else if(!_.include(event.rUser.channels, event.rChannel.id)) {
this.internalAPI.addChannelUser(event.rChannel, event.rUser, {}, function() {});
}
if(!_.include(channelUsers, nick)) {
channelUsers.push(nick);
if(event.rUser.currentNick != event.user) {
event.rUser.currentNick = event.user;
this.db.save('users', event.rUser.id, event.rUser, function() {});
}
} else if(event.action == 'NICK') {
var newNick = event.newNick;
if(!this.api.isKnownUser(newNick)) {
knownUsers.aliases[newNick] = this.api.resolveUser(event.server, event.user);
dbot.api.event.emit('nick_change', [ event.server, newNick ]);
}
this.api.isKnownUser(event.server, event.newNick, function(isKnown) {
event.rUser.currentNick = event.newNick;
if(!isKnown) {
event.rUser.aliases.push(event.newNick);
}
this.db.save('users', event.rUser.id, event.rUser, function(err) {
dbot.api.event.emit('new_user_alias', [ event.rUser, event.newNick ]);
});
}.bind(this));
}
}.bind(this);
this.on = ['JOIN', 'NICK'];
this.onLoad = function() {
// Trigger updateNickLists to stat current users in channel
dbot.instance.addListener('366', 'users', function(event) {
var knownUsers = this.getServerUsers(event.server);
if(!_.has(knownUsers.channelUsers, event.channel.name)) {
knownUsers.channelUsers[event.channel.name] = [];
}
var channelUsers = knownUsers.channelUsers[event.channel.name];
this.on = ['JOIN', 'NICK'];
_.each(event.channel.nicks, function(nick) {
nick = nick.name;
if(this.api.isKnownUser(event.server, nick)) {
nick = this.api.resolveUser(event.server, nick);
} else {
knownUsers.users.push(nick);
dbot.api.event.emit('new_user', [ event.server, nick ]);
}
if(!_.include(channelUsers, nick)) {
channelUsers.push(nick);
}
}, this);
this.onLoad = function() {
dbot.instance.addPreEmitHook(function(event, callback) {
if(event.user) {
this.api.resolveUser(event.server, event.user, function(user) {
event.rUser = user;
callback(false);
});
} else {
callback(false);
}
}.bind(this));
dbot.instance.addPreEmitHook(function(event, callback) {
if(event.channel) {
this.api.getChannel(event.server, event.channel.name, function(channel) {
event.rChannel = channel;
callback(false);
});
} else {
callback(false);
}
}.bind(this));
var connections = dbot.instance.connections;
_.each(connections, function(connection) {
dbot.instance.addListener('366', 'users', function(event) {
var checkChannel = function(channel) {
async.eachSeries(_.keys(event.channel.nicks), function(nick, next) {
var staff = event.channel.nicks[nick];
this.api.resolveUser(event.server, nick, function(user) {
var checkChannelUser = function(user) {
if(!_.include(channel.users, user.id)) {
this.internalAPI.addChannelUser(channel, user, staff, next);
} else {
this.internalAPI.modChannelStaff(channel, user, staff, next);
}
}.bind(this);
if(user) {
checkChannelUser(user);
} else {
this.internalAPI.createUser(event.server, nick, checkChannelUser);
}
}.bind(this));
}.bind(this), function(err) {});
}.bind(this);
if(!event.rChannel) {
this.internalAPI.createChannel(event.server, event.channel.name, checkChannel);
} else {
checkChannel(event.rChannel);
}
}.bind(this));
_.each(dbot.instance.connections, function(connection) {
connection.updateNickLists();
});
};

View File

@ -1,4 +1,4 @@
{
"dependencies": [ "report", "users", "web" ],
"dbKeys": [ "warnings" ]
"dbType": "redis"
}

View File

@ -1,4 +1,5 @@
var _ = require('underscore')._;
var _ = require('underscore')._,
async = require('async');
var pages = function(dbot) {
this.warnings = dbot.db.warnings;
@ -7,37 +8,59 @@ var pages = function(dbot) {
'/warning': function(req, res) {
res.render('servers', {
'name': dbot.config.name,
'servers': _.keys(this.warnings)
'servers': _.keys(dbot.config.servers)
});
},
'/warning/:server': function(req, res) {
var server = req.params.server;
var server = req.params.server,
userIds = [],
userNicks = [];
if(_.has(this.warnings, server)) {
res.render('users', {
'name': dbot.config.name,
'server': server,
'users': _.keys(this.warnings[server])
this.db.search('warnings', { 'server': server }, function(warning) {
if(!_.include(userIds, warning.warnee)) userIds.push(warning.warnee);
}, function(err) {
async.eachSeries(userIds, function(id, callback) {
dbot.api.users.getUser(id, function(user) {
userNicks.push(user.primaryNick);
callback(false);
});
}, function(err) {
res.render('users', {
'name': dbot.config.name,
'server': server,
'users': userNicks
});
});
} else {
res.render('error');
}
});
},
'/warning/:server/:user': function(req, res) {
var server = req.params.server,
user = req.params.user;
if(_.has(this.warnings, server) && _.has(this.warnings[server], user)) {
res.render('warnings', {
'name': dbot.config.name,
dbot.api.users.resolveUser(server, user, function(user) {
var warnings = [];
this.db.search('warnings', {
'server': server,
'warnings': this.warnings[server][user]
'warnee': user.id
}, function(warning) {
warnings.push(warning);
}, function(err) {
async.eachSeries(warnings, function(warning, callback) {
dbot.api.users.getUser(warning.warner, function(user) {
warning.warner = user.primaryNick;
callback(false);
});
}, function(err) {
res.render('warnings', {
'name': dbot.config.name,
'server': server,
'warnings': warnings
});
});
});
} else {
res.render('error');
}
}.bind(this));
}
};
};

View File

@ -1,11 +1,14 @@
{
"no_warnings": {
"en": "No warnings found for {user}."
"en": "No warnings found for {user}.",
"fr": "Aucun avertissement trouvé pour {user}."
},
"warning_info": {
"en": "{user} has {num} warnings. More info can be found at {url}"
"en": "{user} has {num} warnings. More info can be found at {url}",
"fr": "{user} a {num} avertissements. Plus d'informations peuvent être trouvées ici : {url}"
},
"warn_notify": {
"en": "Attention: {warner} has issued a warning to {warnee} for \"{reason}.\" More info can be found at {url}"
"en": "Attention: {warner} has issued a warning to {warnee} for \"{reason}.\" More info can be found at {url}",
"fr": "Attention : {warner} a donné un avertissement à {warnee} pour \"{reason}.\". Plus d'informations peuvent être trouvées ici : {url}"
}
}

View File

@ -1,54 +1,69 @@
var _ = require('underscore')._;
uuid = require('node-uuid');
var warning = function(dbot) {
this.warnings = dbot.db.warnings;
this.commands = {
'~warn': function(event) {
var warner = event.user,
var warner = event.rUser,
server = event.server,
warnee = dbot.api.users.resolveUser(server, event.input[1]),
reason = event.input[2],
adminChannel = dbot.config.servers[server].admin_channel;
// Store the warn
if(!_.has(this.warnings, server)) this.warnings[server] = {};
if(!_.has(this.warnings[server], warnee)) this.warnings[server][warnee] = [];
this.warnings[server][warnee].push({
'warner': warner,
'reason': reason,
'time': new Date().getTime()
});
// Notify interested parties
var notifyString = dbot.t('warn_notify', {
'warner': warner,
'warnee': warnee,
'reason': reason,
'url': dbot.api.web.getUrl('warning/' + server + '/' + warnee)
});
if(!_.isUndefined(adminChannel)) {
adminChannel = event.channel.name;
}
dbot.api.report.notify(server, adminChannel, notifyString);
dbot.say(server, adminChannel, notifyString);
dbot.say(server, warnee, notifyString);
dbot.api.users.resolveUser(server, event.input[1], function(warnee) {
if(warnee) {
var id = uuid.v4();
this.db.save('warnings', id, {
'id': id,
'server': event.server,
'warnee': warnee.id,
'warner': warner.id,
'reason': reason,
'time': new Date().getTime()
}, function(err) {
var notifyString = dbot.t('warn_notify', {
'warner': warner.primaryNick,
'warnee': warnee.primaryNick,
'reason': reason,
'url': dbot.api.web.getUrl('warning/' + server + '/'
+ warnee.primaryNick)
});
if(_.isUndefined(adminChannel)) {
adminChannel = event.channel.name;
}
dbot.api.report.notify(server, adminChannel, notifyString);
dbot.say(server, adminChannel, notifyString);
dbot.say(server, warnee.currentNick, notifyString);
});
} else {
event.reply(dbot.t('warnee_not_found', { 'user': event.input[1] }));
}
}.bind(this));
},
'~warnings': function(event) {
var warnee = event.params[1],
server = event.server;
if(_.has(this.warnings, server) && _.has(this.warnings[server], warnee)) {
event.reply(dbot.t('warning_info', {
'user': warnee,
'num': this.warnings[server][warnee].length,
'url': dbot.api.web.getUrl('warning/' + server + '/' + warnee)
}));
} else {
event.reply(dbot.t('no_warnings', { 'user': warnee }));
}
dbot.api.users.resolveUser(server, warnee, function(warnee) {
var warnings = 0;
this.db.search('warnings', {
'server': server,
'warnee': warnee.id
}, function(warning) {
warnings++;
}, function(err) {
if(warnings > 0) {
event.reply(dbot.t('warning_info', {
'user': warnee.primaryNick,
'num': warnings,
'url': dbot.api.web.getUrl('warning/' + server + '/'
+ warnee.primaryNick)
}));
} else {
event.reply(dbot.t('no_warnings', { 'user': warnee }));
}
});
}.bind(this));
}
};

View File

@ -7,31 +7,21 @@ Web interface
It's a web interface for DBot. What of it?
## Requirements
###Express and Jade@0.25
### Express and Jade
```
npm install express
npm install jade@0.25
npm install express jade
```
###Express Patch
Express currently needs to be patched, edit ~/node_modules/express/lib/express.js as thus;
```
52 for (var key in connect.middleware) {
**53 if( !Object.prototype.hasOwnProperty(key) ) {
54 Object.defineProperty(
55 exports
56 , key
57 , Object.getOwnPropertyDescriptor(connect.middleware, key));
**58 }
59 }
```
###Twitter Bootstrap
### Twitter Bootstrap
```
cd depressionbot/public/
wget http://twitter.github.com/bootstrap/assets/bootstrap.zip
unzip bootstrap.zip
rm bootstrap.zip
```
###d3.js
### d3.js
```
cd depressionbot/public/
mkdir d3

View File

@ -1,6 +1,5 @@
{
"webHost": "nourishedcloud.com",
"webPort": 8080,
"externalPath": false,
"help": "https://github.com/reality/depressionbot/blob/master/modules/web/README.md"
"externalPath": false
}

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