diff --git a/src/client.js b/src/client.js index 86383ed7..a0fc70e0 100644 --- a/src/client.js +++ b/src/client.js @@ -12,6 +12,7 @@ const UAParser = require("ua-parser-js"); const uuidv4 = require("uuid/v4"); const escapeRegExp = require("lodash/escapeRegExp"); const inputs = require("./plugins/inputs"); +const PublicClient = require("./plugins/packages/publicClient"); const MessageStorage = require("./plugins/messageStorage/sqlite"); const TextFileMessageStorage = require("./plugins/messageStorage/text"); @@ -336,20 +337,19 @@ Client.prototype.inputLine = function(data) { const irc = target.network.irc; let connected = irc && irc.connection && irc.connection.connected; - if (Object.prototype.hasOwnProperty.call(inputs.userInputs, cmd) && typeof inputs.userInputs[cmd].input === "function") { - const plugin = inputs.userInputs[cmd]; + if (inputs.userInputs.has(cmd)) { + const plugin = inputs.userInputs.get(cmd); - if (connected || plugin.allowDisconnected) { + if (typeof plugin.input === "function" && (connected || plugin.allowDisconnected)) { connected = true; - plugin.input.apply(client, - [ - target.network, - target.chan, - cmd, - args, - (command) => this.inputLine({target: data.target, text: command}), - ] - ); + plugin.input.apply(client, [target.network, target.chan, cmd, args]); + } + } else if (inputs.pluginCommands.has(cmd)) { + const plugin = inputs.pluginCommands.get(cmd); + + if (typeof plugin.input === "function" && (connected || plugin.allowDisconnected)) { + connected = true; + plugin.input(new PublicClient(client), {network: target.network, chan: target.chan}, cmd, args); } } else if (connected) { irc.raw(text); diff --git a/src/plugins/inputs/index.js b/src/plugins/inputs/index.js index 12be924c..6ea9d8cc 100644 --- a/src/plugins/inputs/index.js +++ b/src/plugins/inputs/index.js @@ -37,18 +37,25 @@ const userInputs = [ "whois", ].reduce(function(plugins, name) { const plugin = require(`./${name}`); - plugin.commands.forEach((command) => (plugins[command] = plugin)); + plugin.commands.forEach((command) => plugins.set(command, plugin)); return plugins; -}, {}); +}, new Map()); + +const pluginCommands = new Map(); const getCommands = () => - Object.keys(userInputs) + Array.from(userInputs.keys()) + .concat(Array.from(pluginCommands.keys())) .map((command) => `/${command}`) .concat(clientSideCommands) .concat(passThroughCommands) .sort(); +const addPluginCommand = (command, func) => pluginCommands.set(command, func); + module.exports = { + addPluginCommand, getCommands, + pluginCommands, userInputs, }; diff --git a/src/plugins/packages/index.js b/src/plugins/packages/index.js index fece7bb8..4e738de5 100644 --- a/src/plugins/packages/index.js +++ b/src/plugins/packages/index.js @@ -16,14 +16,14 @@ module.exports = { loadPackages, }; -const packageApis = function(clientManager, packageName) { +const packageApis = function(packageName) { return { Stylesheets: { addFile: addStylesheet.bind(this, packageName), }, Commands: { - add: (command, func) => inputs.userInputs[command] = func, - runAsUser: (line, userName, target) => clientManager.findClient(userName).inputLine({target, text: line}), + add: inputs.addPluginCommand, + runAsUser: (command, targetId, client) => client.inputLine({target: targetId, text: command}), }, Config: { getConfig: () => Helper.config, @@ -43,7 +43,7 @@ function getPackage(name) { return packageMap.get(name); } -function loadPackages(clientManager) { +function loadPackages() { const packageJson = path.join(Helper.getPackagesPath(), "package.json"); let packages; let anyPlugins = false; @@ -83,7 +83,7 @@ function loadPackages(clientManager) { } if (packageFile.onServerStart) { - packageFile.onServerStart(packageApis(clientManager, packageName)); + packageFile.onServerStart(packageApis(packageName)); } log.info(`Package ${colors.bold(packageName)} loaded`); diff --git a/src/plugins/packages/publicClient.js b/src/plugins/packages/publicClient.js new file mode 100644 index 00000000..c18cf668 --- /dev/null +++ b/src/plugins/packages/publicClient.js @@ -0,0 +1,40 @@ +module.exports = class PublicClient { + constructor(client) { + this.client = client; + } + + /** + * + * @param {String} command - IRC command to run, this is in the same format that a client would send to the server (eg: JOIN #test) + * @param {String} targetId - The id of the channel to simulate the command coming from. Replies will go to this channel if appropriate + */ + runAsUser(command, targetId) { + this.client.inputLine({target: targetId, text: command}); + } + + /** + * + * @param {Object} attributes + */ + createChannel(attributes) { + return this.client.createChannel(attributes); + } + + /** + * Emits an `event` to the browser client, with `data` in the body of the event. + * + * @param {String} event - Name of the event, must be something the browser will recognise + * @param {Object} data - Body of the event, can be anything, but will need to be properly interpreted by the client + */ + sendToBrowser(event, data) { + this.client.emit(event, data); + } + + /** + * + * @param {Number} chanId + */ + getChannel(chanId) { + return this.client.find(chanId); + } +}; diff --git a/src/server.js b/src/server.js index 214bd35e..c0cfbbdf 100644 --- a/src/server.js +++ b/src/server.js @@ -173,7 +173,7 @@ module.exports = function() { }); manager = new ClientManager(); - packages.loadPackages(manager); + packages.loadPackages(); new Identification((identHandler) => { manager.init(identHandler, sockets);