diff --git a/core/bbs.js b/core/bbs.js index ea351480..4fb50c3a 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -11,6 +11,7 @@ const logger = require('./logger.js'); const database = require('./database.js'); const clientConns = require('./client_connections.js'); const resolvePath = require('./misc_util.js').resolvePath; +const events = require('./events.js'); // deps const async = require('async'); @@ -27,7 +28,7 @@ exports.main = main; const initServices = {}; const ENIGMA_COPYRIGHT = 'ENiGMA½ Copyright (c) 2014-2017 Bryan Ashby'; -const HELP = +const HELP = `${ENIGMA_COPYRIGHT} usage: main.js @@ -60,7 +61,7 @@ function main() { conf.init(resolvePath(configPath), function configInit(err) { // - // If the user supplied a path and we can't read/parse it + // If the user supplied a path and we can't read/parse it // then it's a fatal error // if(err) { @@ -84,15 +85,17 @@ function main() { } return callback(err); }); - }, + } ], function complete(err) { + events.registerModules(); + // note this is escaped: fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => { console.info(ENIGMA_COPYRIGHT); - if(!err) { + if(!err) { console.info(banner); - } + } console.info('System started!'); }); @@ -140,7 +143,7 @@ function shutdownSystem() { }, function stopMsgNetwork(callback) { require('./msg_network.js').shutdown(callback); - } + } ], () => { console.info('Goodbye!'); @@ -173,9 +176,9 @@ function initialize(cb) { process.on('SIGINT', shutdownSystem); require('later').date.localTime(); // use local times for later.js/scheduling - + return callback(null); - }, + }, function initDatabases(callback) { return database.initializeDatabases(callback); }, @@ -195,7 +198,7 @@ function initialize(cb) { // * Makes this accessible for MCI codes, easy non-blocking access, etc. // * We do this every time as the op is free to change this information just // like any other user - // + // const User = require('./user.js'); async.waterfall( @@ -223,7 +226,7 @@ function initialize(cb) { opProps.username = opUserName; _.each(opProps, (v, k) => { - StatLog.setNonPeristentSystemStat(`sysop_${k}`, v); + StatLog.setNonPeristentSystemStat(`sysop_${k}`, v); }); } @@ -235,7 +238,7 @@ function initialize(cb) { return require('./predefined_mci.js').init(callback); }, function readyMessageNetworkSupport(callback) { - return require('./msg_network.js').startup(callback); + return require('./msg_network.js').startup(callback); }, function listenConnections(callback) { return require('./listening_server.js').startup(callback); diff --git a/core/client_connections.js b/core/client_connections.js index 5cbeb3b9..471b1b79 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -3,6 +3,7 @@ // ENiGMA½ const logger = require('./logger.js'); +const events = require('./events.js'); // deps const _ = require('lodash'); @@ -22,7 +23,7 @@ function getActiveConnections() { return clientConnections; } function getActiveNodeList(authUsersOnly) { if(!_.isBoolean(authUsersOnly)) { - authUsersOnly = true; + authUsersOnly = true; } const now = moment(); @@ -30,7 +31,7 @@ function getActiveNodeList(authUsersOnly) { const activeConnections = getActiveConnections().filter(ac => { return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly); }); - + return _.map(activeConnections, ac => { const entry = { node : ac.node, @@ -41,7 +42,7 @@ function getActiveNodeList(authUsersOnly) { // // There may be a connection, but not a logged in user as of yet - // + // if(ac.user.isAuthenticated()) { entry.userName = ac.user.username; entry.realName = ac.user.properties.real_name; @@ -49,7 +50,7 @@ function getActiveNodeList(authUsersOnly) { entry.affils = ac.user.properties.affiliation; const diff = now.diff(moment(ac.user.properties.last_login_timestamp), 'minutes'); - entry.timeOn = moment.duration(diff, 'minutes'); + entry.timeOn = moment.duration(diff, 'minutes'); } return entry; }); @@ -59,7 +60,7 @@ function addNewClient(client, clientSock) { const id = client.session.id = clientConnections.push(client) - 1; const remoteAddress = client.remoteAddress = clientSock.remoteAddress; - // Create a client specific logger + // Create a client specific logger // Note that this will be updated @ login with additional information client.log = logger.log.child( { clientId : id } ); @@ -76,6 +77,8 @@ function addNewClient(client, clientSock) { client.log.info(connInfo, 'Client connected'); + events.emit('codes.l33t.enigma.system.connected', {'client': client}); + return id; } @@ -85,17 +88,19 @@ function removeClient(client) { const i = clientConnections.indexOf(client); if(i > -1) { clientConnections.splice(i, 1); - + logger.log.info( - { + { connectionCount : clientConnections.length, - clientId : client.session.id - }, + clientId : client.session.id + }, 'Client disconnected' ); + + events.emit('codes.l33t.enigma.system.disconnected', {'client': client}); } } function getConnectionByUserId(userId) { return getActiveConnections().find( ac => userId === ac.user.userId ); -} \ No newline at end of file +} diff --git a/core/connect.js b/core/connect.js index ffd28a0c..85273577 100644 --- a/core/connect.js +++ b/core/connect.js @@ -3,6 +3,7 @@ // ENiGMA½ const ansi = require('./ansi_term.js'); +const events = require('./events.js'); // deps const async = require('async'); @@ -82,7 +83,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { // if(h < 10 || w < 10) { client.log.warn( - { height : h, width : w }, + { height : h, width : w }, 'Ignoring ANSI CPR screen size query response due to very small values'); return done(new Error('Term size <= 10 considered invalid')); } @@ -91,11 +92,11 @@ function ansiQueryTermSizeIfNeeded(client, cb) { client.term.termWidth = w; client.log.debug( - { - termWidth : client.term.termWidth, - termHeight : client.term.termHeight, - source : 'ANSI CPR' - }, + { + termWidth : client.term.termWidth, + termHeight : client.term.termHeight, + source : 'ANSI CPR' + }, 'Window size updated' ); @@ -109,7 +110,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { return done(new Error('No term size established by CPR within timeout')); }, 2000); - // Start the process: Query for CPR + // Start the process: Query for CPR client.term.rawWrite(ansi.queryScreenSize()); } @@ -123,7 +124,7 @@ function displayBanner(term) { // note: intentional formatting: term.pipeWrite(` |06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN -|06Copyright (c) 2014-2017 Bryan Ashby |14- |12http://l33t.codes/ +|06Copyright (c) 2014-2017 Bryan Ashby |14- |12http://l33t.codes/ |06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/ |00` ); @@ -153,11 +154,11 @@ function connectEntry(client, nextMenu) { if(0 === term.termHeight || 0 === term.termWidth) { // // We still don't have something good for term height/width. - // Default to DOS size 80x25. + // Default to DOS size 80x25. // - // :TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing??? + // :TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing??? client.log.warn( { reason : err.message }, 'Failed to negotiate term size; Defaulting to 80x25!'); - + term.termHeight = 25; term.termWidth = 80; } @@ -165,8 +166,8 @@ function connectEntry(client, nextMenu) { return callback(null); }); - }, - ], + }, + ], () => { prepareTerminal(term); @@ -175,6 +176,9 @@ function connectEntry(client, nextMenu) { // displayBanner(term); + // fire event + events.emit('codes.l33t.enigma.system.term_detected', {'client': client}); + setTimeout( () => { return client.menuStack.goto(nextMenu); }, 500); diff --git a/core/events.js b/core/events.js new file mode 100644 index 00000000..79b41429 --- /dev/null +++ b/core/events.js @@ -0,0 +1,50 @@ +/* jslint node: true */ +'use strict'; + +const Config = require('./config.js'); +const fs = require('fs'); +const path = require('path'); +const events = require('events'); +const logger = require('./logger.js'); + +var eventEmitter = new events.EventEmitter(); + +var self = module.exports = { + emit: function(eventName, args) { + logger.log.debug("Emit "+eventName); + eventEmitter.emit(eventName, args); + }, + on: function(eventName, listener) { + logger.log.debug("Register listener for "+eventName); + eventEmitter.on(eventName, listener); + }, + remove: function(eventName, listener) { + logger.log.debug("Remove listener for "+eventName); + eventEmitter.removeListener(eventName, listener); + }, + registerModules: function() { + const moduleUtil = require('./module_util.js'); + + moduleUtil.getModulePaths().forEach(function(modulePath) { + var mods = fs.readdirSync(modulePath); + mods.forEach(function(item) { + var modPath = modulePath+item; + if (item.substr(item.length-3) != '.js') { + modPath += path.sep+item+'.js'; + } + if (fs.existsSync(modPath)) { + var module = require(modPath); + + if (module.registerEvents !== undefined) { + logger.log.debug(modPath+" calling registerEvents function"); + module.registerEvents(); + } else { + logger.log.debug(modPath+" has no registerEvents function"); + } + } else { + logger.log.debug(modPath+" - file not found"); + } + }); + }); + } +} diff --git a/core/module_util.js b/core/module_util.js index 1cd4bed3..194efca5 100644 --- a/core/module_util.js +++ b/core/module_util.js @@ -15,6 +15,7 @@ const async = require('async'); exports.loadModuleEx = loadModuleEx; exports.loadModule = loadModule; exports.loadModulesForCategory = loadModulesForCategory; +exports.getModulePaths = getModulePaths; function loadModuleEx(options, cb) { assert(_.isObject(options)); @@ -25,7 +26,7 @@ function loadModuleEx(options, cb) { if(_.isObject(modConfig) && false === modConfig.enabled) { const err = new Error(`Module "${options.name}" is disabled`); - err.code = 'EENIGMODDISABLED'; + err.code = 'EENIGMODDISABLED'; return cb(err); } @@ -36,7 +37,7 @@ function loadModuleEx(options, cb) { // let mod; let modPath = paths.join(options.path, `${options.name}.js`); // general case first - try { + try { mod = require(modPath); } catch(e) { if('MODULE_NOT_FOUND' === e.code) { @@ -48,7 +49,7 @@ function loadModuleEx(options, cb) { } } else { return cb(e); - } + } } if(!_.isObject(mod.moduleInfo)) { @@ -75,7 +76,7 @@ function loadModule(name, category, cb) { } function loadModulesForCategory(category, iterator, complete) { - + fs.readdir(Config.paths[category], (err, files) => { if(err) { return iterator(err); @@ -97,3 +98,9 @@ function loadModulesForCategory(category, iterator, complete) { }); }); } + +function getModulePaths() { + return [ + Config.paths.mods + ]; +} diff --git a/core/plugin_module.js b/core/plugin_module.js index aeac4950..31ba6f01 100644 --- a/core/plugin_module.js +++ b/core/plugin_module.js @@ -4,4 +4,4 @@ exports.PluginModule = PluginModule; function PluginModule(options) { -} \ No newline at end of file +}