2013-01-12 11:38:40 +01:00
|
|
|
var fs = require('fs'),
|
|
|
|
_ = require('underscore')._,
|
2013-02-12 20:15:32 +01:00
|
|
|
jsbot = require('./jsbot/jsbot'),
|
|
|
|
DatabaseDriver = require('./database').DatabaseDriver;
|
2011-09-04 16:39:03 +02:00
|
|
|
require('./snippets');
|
2011-08-23 01:04:40 +02:00
|
|
|
|
2013-01-27 21:52:11 +01:00
|
|
|
var DBot = function() {
|
2013-01-24 22:35:00 +01:00
|
|
|
|
|
|
|
/*** Load the DB ***/
|
2013-01-24 21:51:38 +01:00
|
|
|
if(fs.existsSync('db.json')) {
|
|
|
|
try {
|
|
|
|
this.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
|
|
|
|
} catch(err) {
|
|
|
|
console.log('Error loading db.json. Stopping: ' + err);
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.db = {};
|
2012-03-10 19:38:56 +01:00
|
|
|
}
|
2012-12-11 21:06:29 +01:00
|
|
|
|
2013-06-04 01:00:23 +02:00
|
|
|
this.reloadConfig();
|
2013-01-21 01:05:42 +01:00
|
|
|
|
2013-07-08 12:06:34 +02:00
|
|
|
/*** Load the fancy DB ***/
|
|
|
|
this.ddb = new DatabaseDriver(this.config);
|
|
|
|
|
2013-01-24 22:35:00 +01:00
|
|
|
/*** Load main strings ***/
|
2012-12-11 21:06:29 +01:00
|
|
|
try {
|
|
|
|
this.strings = JSON.parse(fs.readFileSync('strings.json', 'utf-8'));
|
|
|
|
} catch(err) {
|
2012-12-12 19:25:07 +01:00
|
|
|
console.log('Probably a syntax error in strings.json: ' + err);
|
2012-12-11 21:06:29 +01:00
|
|
|
this.strings = {};
|
|
|
|
}
|
2011-08-24 02:57:52 +02:00
|
|
|
|
2012-04-20 15:14:28 +02:00
|
|
|
// Initialise run-time resources
|
2012-12-11 21:06:29 +01:00
|
|
|
this.usage = {};
|
2013-01-23 23:32:17 +01:00
|
|
|
this.status = {};
|
2012-04-20 15:14:28 +02:00
|
|
|
this.sessionData = {};
|
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Populate bot properties with config data
|
2012-04-20 15:14:28 +02:00
|
|
|
// Create JSBot and connect to each server
|
2012-12-17 18:18:31 +01:00
|
|
|
this.instance = jsbot.createJSBot(this.config.name);
|
2013-01-12 22:40:16 +01:00
|
|
|
_.each(this.config.servers, function(server, name) {
|
|
|
|
this.instance.addConnection(name, server.server, server.port,
|
|
|
|
this.config.admin, function(event) {
|
|
|
|
var server = this.config.servers[event.server];
|
|
|
|
for(var i=0;i<server.channels.length;i++) {
|
|
|
|
this.instance.join(event, server.channels[i]);
|
|
|
|
}
|
|
|
|
}.bind(this), server.nickserv, server.password);
|
|
|
|
}, this);
|
2011-08-24 02:57:52 +02:00
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Load the modules and connect to the server
|
2011-08-24 02:57:52 +02:00
|
|
|
this.reloadModules();
|
2012-04-20 15:14:28 +02:00
|
|
|
this.instance.connectAll();
|
2011-08-24 02:57:52 +02:00
|
|
|
};
|
|
|
|
|
2013-06-04 01:00:23 +02:00
|
|
|
DBot.prototype.reloadConfig = function() {
|
|
|
|
this.config = {};
|
|
|
|
|
|
|
|
if(!fs.existsSync('config.json')) {
|
|
|
|
console.log('Error: config.json file does not exist. Stopping');
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2013-06-05 23:00:52 +02:00
|
|
|
var configFile = fs.readFileSync('config.json', 'utf-8');
|
|
|
|
this.config = JSON.parse(configFile);
|
|
|
|
this.customConfig = JSON.parse(configFile);
|
2013-06-04 01:00:23 +02:00
|
|
|
} catch(err) {
|
|
|
|
console.log('Config file is invalid. Stopping: ' + err);
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
var defaultConfig = JSON.parse(fs.readFileSync('config.json.sample', 'utf-8'));
|
|
|
|
} catch(err) {
|
|
|
|
console.log('Error loading sample config. Bugger off this should not even be edited. Stopping.');
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load missing config directives from sample file
|
2013-06-05 23:00:52 +02:00
|
|
|
if(!_.has(this.customConfig, 'modules')) {
|
|
|
|
this.customConfig.modules = {};
|
|
|
|
this.config.modules = {};
|
|
|
|
}
|
2013-06-04 01:00:23 +02:00
|
|
|
_.defaults(this.config, defaultConfig);
|
|
|
|
};
|
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Say something in a channel
|
2012-05-23 20:38:10 +02:00
|
|
|
DBot.prototype.say = function(server, channel, message) {
|
|
|
|
this.instance.say(server, channel, message);
|
2011-08-24 02:57:52 +02:00
|
|
|
};
|
|
|
|
|
2012-05-19 17:33:31 +02:00
|
|
|
// Format given stored string in config language
|
|
|
|
DBot.prototype.t = function(string, formatData) {
|
2013-03-19 03:33:15 +01:00
|
|
|
var formattedString = 'String not found. Something has gone screwy. Maybe.';
|
|
|
|
|
2013-01-12 17:14:17 +01:00
|
|
|
if(_.has(this.strings, string)) {
|
2012-12-17 18:18:31 +01:00
|
|
|
var lang = this.config.language;
|
2013-01-12 17:14:17 +01:00
|
|
|
if(!_.has(this.strings[string], lang)) {
|
2013-02-16 19:07:41 +01:00
|
|
|
lang = "en";
|
2012-12-11 21:06:29 +01:00
|
|
|
}
|
2012-05-19 17:33:31 +02:00
|
|
|
|
2013-03-19 03:33:15 +01:00
|
|
|
if(_.has(this.strings[string], lang)) {
|
2013-05-21 19:13:19 +02:00
|
|
|
var module = this.stringMap[string];
|
2013-08-25 18:10:43 +02:00
|
|
|
|
|
|
|
// TODO: Create per-server support
|
2013-08-25 18:13:07 +02:00
|
|
|
/*if(this.config.enableColours) {
|
2013-08-25 18:10:43 +02:00
|
|
|
var colours = this.config.colours;
|
|
|
|
_.each(formatData, function(str, key) {
|
|
|
|
if(_.has(colours, key)) {
|
|
|
|
if(key == 'channel' && _.has(colours['channel'], key)) {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
str = colours[key] + str + '\u000f';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-08-25 18:13:07 +02:00
|
|
|
}*/
|
2013-08-25 18:10:43 +02:00
|
|
|
|
2013-03-19 03:33:15 +01:00
|
|
|
formattedString = this.strings[string][lang].format(formatData);
|
2013-06-04 02:10:12 +02:00
|
|
|
if(this.config.modules[module] && this.config.modules[module].outputPrefix) {
|
|
|
|
formattedString = '[' + this.config.modules[module].outputPrefix + '] ' +
|
2013-05-21 19:13:19 +02:00
|
|
|
formattedString;
|
|
|
|
}
|
2013-03-19 03:33:15 +01:00
|
|
|
}
|
2012-12-11 21:06:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return formattedString;
|
2012-05-19 17:33:31 +02:00
|
|
|
};
|
|
|
|
|
2012-05-23 20:38:10 +02:00
|
|
|
/*DBot.prototype.act = function(channel, data) {
|
2012-03-09 22:44:05 +01:00
|
|
|
this.instance.send('PRIVMSG', channel, ':\001ACTION ' + data + '\001');
|
2012-05-23 20:38:10 +02:00
|
|
|
}*/
|
2012-02-27 17:43:47 +01:00
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Save the database file
|
2011-08-24 02:57:52 +02:00
|
|
|
DBot.prototype.save = function() {
|
2013-01-23 00:27:02 +01:00
|
|
|
fs.writeFileSync('db.json', JSON.stringify(this.db, null, ' '));
|
2011-08-24 02:57:52 +02:00
|
|
|
};
|
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Hot-reload module files.
|
2011-08-24 02:57:52 +02:00
|
|
|
DBot.prototype.reloadModules = function() {
|
2013-08-24 21:19:58 +02:00
|
|
|
this.save();
|
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
if(this.modules) { // Run 'onDestroy' code for each module if it exists.
|
2013-02-12 19:34:36 +01:00
|
|
|
_.each(this.modules, function(module) {
|
2011-09-14 18:31:26 +02:00
|
|
|
if(module.onDestroy) {
|
|
|
|
module.onDestroy();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2011-09-14 18:30:39 +02:00
|
|
|
|
2011-08-24 02:57:52 +02:00
|
|
|
this.rawModules = [];
|
2012-12-24 06:47:47 +01:00
|
|
|
this.pages = {};
|
2013-01-23 23:32:17 +01:00
|
|
|
this.status = {};
|
2012-12-30 01:45:25 +01:00
|
|
|
this.modules = {};
|
2011-08-28 16:00:22 +02:00
|
|
|
this.commands = {};
|
2013-01-03 20:56:30 +01:00
|
|
|
this.api = {};
|
2013-05-21 19:13:19 +02:00
|
|
|
this.stringMap = {};
|
2012-12-11 21:06:29 +01:00
|
|
|
this.usage = {};
|
2013-06-04 01:00:23 +02:00
|
|
|
this.reloadConfig();
|
2013-01-21 01:05:42 +01:00
|
|
|
|
2012-12-11 21:47:50 +01:00
|
|
|
try {
|
|
|
|
this.strings = JSON.parse(fs.readFileSync('strings.json', 'utf-8'));
|
|
|
|
} catch(err) {
|
|
|
|
this.strings = {};
|
|
|
|
}
|
|
|
|
|
2012-12-17 18:18:31 +01:00
|
|
|
var moduleNames = this.config.moduleNames;
|
|
|
|
|
2012-03-15 19:08:40 +01:00
|
|
|
// Enforce having command. it can still be reloaded, but dbot _will not_
|
|
|
|
// function without it, so not having it should be impossible
|
2013-05-17 15:32:41 +02:00
|
|
|
if(!_.include(moduleNames, 'command')) {
|
2012-12-17 18:18:31 +01:00
|
|
|
moduleNames.push("command");
|
2012-03-10 18:35:50 +01:00
|
|
|
}
|
|
|
|
|
2012-02-13 20:56:04 +01:00
|
|
|
// Reload Javascript snippets
|
2011-08-25 18:54:59 +02:00
|
|
|
var path = require.resolve('./snippets');
|
2012-02-08 19:42:38 +01:00
|
|
|
delete require.cache[path];
|
2011-08-25 18:54:59 +02:00
|
|
|
require('./snippets');
|
|
|
|
|
2012-04-15 21:42:23 +02:00
|
|
|
this.instance.removeListeners();
|
2013-08-19 19:57:02 +02:00
|
|
|
this.instance.clearHooks();
|
2013-04-21 16:51:59 +02:00
|
|
|
|
|
|
|
var name, moduleDir, config;
|
|
|
|
for(i=0;i<moduleNames.length;i++) {
|
|
|
|
name = moduleNames[i];
|
2013-03-21 00:34:55 +01:00
|
|
|
this.status[name] = true;
|
2013-04-21 16:51:59 +02:00
|
|
|
moduleDir = './modules/' + name + '/';
|
2013-03-21 00:34:55 +01:00
|
|
|
try {
|
|
|
|
var cacheKey = require.resolve(moduleDir + name);
|
|
|
|
delete require.cache[cacheKey];
|
|
|
|
} catch(err) {
|
|
|
|
this.status[name] = 'Error loading module: ' + err + ' ' + err.stack.split('\n')[2].trim();
|
2013-05-06 17:36:35 +02:00
|
|
|
continue;
|
2013-01-07 13:49:18 +01:00
|
|
|
}
|
|
|
|
|
2013-06-04 01:00:23 +02:00
|
|
|
if(!_.has(this.config.modules, name)) this.config.modules[name] = {};
|
|
|
|
if(fs.existsSync(moduleDir + 'config.json')) {
|
2012-12-18 12:07:39 +01:00
|
|
|
try {
|
2013-06-04 01:00:23 +02:00
|
|
|
var defaultConfig = JSON.parse(fs.readFileSync(moduleDir + 'config.json', 'utf-8'));
|
|
|
|
} catch(err) { // Syntax error
|
2013-04-10 02:19:56 +02:00
|
|
|
this.status[name] = 'Error parsing config: ' + err + ' ' + err.stack.split('\n')[2].trim();
|
2013-05-26 17:21:13 +02:00
|
|
|
continue;
|
2012-12-18 12:07:39 +01:00
|
|
|
}
|
2013-06-04 01:00:23 +02:00
|
|
|
this.config.modules[name] = _.defaults(this.config.modules[name], defaultConfig);
|
2013-04-10 02:19:56 +02:00
|
|
|
}
|
2013-06-04 01:00:23 +02:00
|
|
|
var config = this.config.modules[name];
|
2012-12-18 12:07:39 +01:00
|
|
|
|
2013-04-10 02:19:56 +02:00
|
|
|
// Don't shit out if dependencies not met
|
|
|
|
if(_.has(config, 'dependencies')) {
|
|
|
|
_.each(config.dependencies, function(dependency) {
|
|
|
|
if(!_.include(moduleNames, dependency)) {
|
|
|
|
console.log('Warning: Automatically loading ' + dependency);
|
|
|
|
moduleNames.push(dependency);
|
2013-01-24 22:35:00 +01:00
|
|
|
}
|
2013-04-10 02:19:56 +02:00
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
|
2013-04-20 18:08:34 +02:00
|
|
|
// Groovy funky database shit
|
|
|
|
if(!_.has(config, 'dbType') || config.dbType == 'olde') {
|
|
|
|
// Generate missing DB keys
|
|
|
|
_.each(config.dbKeys, function(dbKey) {
|
|
|
|
if(!_.has(this.db, dbKey)) {
|
|
|
|
this.db[dbKey] = {};
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
} else {
|
|
|
|
// Just use the name of the module for now, add dbKey iteration later
|
|
|
|
this.ddb.createDB(name, config.dbType, {}, function(db) {});
|
|
|
|
}
|
2013-04-21 16:51:59 +02:00
|
|
|
}
|
2013-04-20 18:08:34 +02:00
|
|
|
|
|
|
|
process.nextTick(function() {
|
|
|
|
_.each(moduleNames, function(name) {
|
2013-05-26 17:21:13 +02:00
|
|
|
if(this.status[name] === true) {
|
|
|
|
try {
|
|
|
|
var moduleDir = './modules/' + name + '/';
|
|
|
|
var rawModule = require(moduleDir + name);
|
|
|
|
var module = rawModule.fetch(this);
|
|
|
|
this.rawModules.push(rawModule);
|
|
|
|
} catch(err) {
|
|
|
|
var stack = err.stack.split('\n')[2].trim();
|
|
|
|
this.status[name] = 'Error loading module: ' + err + ' ' + stack;
|
|
|
|
console.log('Error loading module: ' + err + ' ' + stack);
|
|
|
|
return;
|
2013-04-10 03:07:29 +02:00
|
|
|
}
|
|
|
|
|
2013-05-26 17:21:13 +02:00
|
|
|
module.name = name;
|
|
|
|
module.db = this.ddb.databanks[name];
|
2013-06-04 01:00:23 +02:00
|
|
|
module.config = this.config.modules[name];
|
2013-05-26 17:21:13 +02:00
|
|
|
|
|
|
|
// Load the module data
|
|
|
|
_.each([ 'commands', 'pages', 'api' ], function(property) {
|
|
|
|
var propertyObj = {};
|
|
|
|
|
|
|
|
if(fs.existsSync(moduleDir + property + '.js')) {
|
|
|
|
try {
|
|
|
|
var propertyKey = require.resolve(moduleDir + property);
|
|
|
|
if(propertyKey) delete require.cache[propertyKey];
|
|
|
|
propertyObj = require(moduleDir + property).fetch(this);
|
|
|
|
} catch(err) {
|
|
|
|
console.log('Module error (' + module.name + ') in ' +
|
|
|
|
property + ': ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!_.has(module, property)) module[property] = {};
|
|
|
|
_.extend(module[property], propertyObj);
|
|
|
|
_.each(module[property], function(item, itemName) {
|
|
|
|
item.module = name;
|
|
|
|
if(_.has(module.config, property) && _.has(module.config[property], itemName)) {
|
|
|
|
_.extend(item, module.config[property][itemName]);
|
|
|
|
}
|
|
|
|
module[property][itemName] = _.bind(item, module);
|
|
|
|
_.extend(module[property][itemName], item);
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
if(property == 'api') {
|
|
|
|
this[property][name] = module[property];
|
|
|
|
} else {
|
|
|
|
_.extend(this[property], module[property]);
|
2013-04-10 03:07:29 +02:00
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
2013-05-26 17:21:13 +02:00
|
|
|
// Load the module listener
|
|
|
|
if(module.listener) {
|
|
|
|
if(!_.isArray(module.on)) {
|
|
|
|
module.on = [ module.on ];
|
|
|
|
}
|
|
|
|
_.each(module.on, function(on) {
|
|
|
|
this.instance.addListener(on, module.name, module.listener);
|
|
|
|
}, this);
|
2013-04-10 03:07:29 +02:00
|
|
|
}
|
|
|
|
|
2013-05-26 17:21:13 +02:00
|
|
|
// Load string data for the module
|
|
|
|
_.each([ 'usage', 'strings' ], function(property) {
|
|
|
|
var propertyData = {};
|
|
|
|
try {
|
|
|
|
propertyData = JSON.parse(fs.readFileSync(moduleDir + property + '.json', 'utf-8'));
|
2013-07-11 17:22:29 +02:00
|
|
|
} catch(err) {
|
|
|
|
console.log('Data error (' + module.name + ') in ' +
|
|
|
|
property + ': ' + err);
|
|
|
|
};
|
2013-05-26 17:21:13 +02:00
|
|
|
_.extend(this[property], propertyData);
|
|
|
|
if(property == 'strings') {
|
|
|
|
_.each(_.keys(propertyData), function(string) {
|
|
|
|
this.stringMap[string] = name;
|
|
|
|
}.bind(this));
|
|
|
|
}
|
2013-04-10 03:07:29 +02:00
|
|
|
}, this);
|
|
|
|
|
2013-05-26 17:21:13 +02:00
|
|
|
// Provide toString for module name
|
|
|
|
module.toString = function() {
|
|
|
|
return this.name;
|
2013-05-21 19:32:26 +02:00
|
|
|
}
|
2013-04-10 03:07:29 +02:00
|
|
|
|
2013-05-26 17:21:13 +02:00
|
|
|
this.modules[module.name] = module;
|
2013-04-10 03:07:29 +02:00
|
|
|
}
|
2013-04-20 18:08:34 +02:00
|
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
2013-04-10 03:07:29 +02:00
|
|
|
|
2013-04-20 18:08:34 +02:00
|
|
|
process.nextTick(function() {
|
|
|
|
if(_.has(this.modules, 'web')) this.modules.web.reloadPages();
|
|
|
|
_.each(this.modules, function(module, name) {
|
|
|
|
if(module.onLoad) {
|
|
|
|
try {
|
|
|
|
module.onLoad();
|
|
|
|
} catch(err) {
|
|
|
|
this.status[name] = 'Error in onLoad: ' + err + ' ' + err.stack.split('\n')[1].trim();
|
|
|
|
console.log('MODULE ONLOAD ERROR (' + name + '): ' + err );
|
2013-01-14 23:34:47 +01:00
|
|
|
}
|
2013-04-20 18:08:34 +02:00
|
|
|
}
|
|
|
|
}, this);
|
2013-04-10 02:19:56 +02:00
|
|
|
}.bind(this));
|
2013-04-20 18:08:34 +02:00
|
|
|
|
2013-04-10 02:19:56 +02:00
|
|
|
this.save();
|
|
|
|
};
|
2012-12-24 06:47:47 +01:00
|
|
|
|
2013-01-27 21:52:11 +01:00
|
|
|
new DBot();
|