Add support for command plugins (#2757)

This commit is contained in:
Al McKinlay 2019-07-02 18:25:43 +01:00 committed by GitHub
commit 1b38e6cc02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 108 additions and 104 deletions

View File

@ -19,62 +19,6 @@ const colorCodeMap = [
["15", "Light Grey"], ["15", "Light Grey"],
]; ];
const commands = [
"/as",
"/away",
"/back",
"/ban",
"/banlist",
"/bs",
"/close",
"/collapse",
"/connect",
"/cs",
"/ctcp",
"/cycle",
"/dehop",
"/deop",
"/devoice",
"/disconnect",
"/expand",
"/ho",
"/hop",
"/hs",
"/ignore",
"/ignorelist",
"/invite",
"/invitelist",
"/join",
"/kick",
"/leave",
"/list",
"/me",
"/mode",
"/ms",
"/msg",
"/nick",
"/notice",
"/ns",
"/op",
"/os",
"/part",
"/query",
"/quote",
"/quit",
"/raw",
"/rejoin",
"/rs",
"/say",
"/send",
"/server",
"/slap",
"/topic",
"/unban",
"/unignore",
"/voice",
"/whois",
];
const condensedTypes = [ const condensedTypes = [
"away", "away",
"back", "back",
@ -95,7 +39,7 @@ const timeFormats = {
module.exports = { module.exports = {
colorCodeMap, colorCodeMap,
commands, commands: [],
condensedTypes, condensedTypes,
condensedTypesQuery, condensedTypesQuery,
timeFormats, timeFormats,

View File

@ -0,0 +1,8 @@
const constants = require("../constants");
const socket = require("../socket");
socket.on("commands", function(commands) {
if (commands) {
constants.commands = commands;
}
});

View File

@ -2,6 +2,7 @@
require("./auth"); require("./auth");
require("./change_password"); require("./change_password");
require("./commands");
require("./init"); require("./init");
require("./join"); require("./join");
require("./more"); require("./more");

View File

@ -11,6 +11,7 @@ const Helper = require("./helper");
const UAParser = require("ua-parser-js"); const UAParser = require("ua-parser-js");
const uuidv4 = require("uuid/v4"); const uuidv4 = require("uuid/v4");
const escapeRegExp = require("lodash/escapeRegExp"); const escapeRegExp = require("lodash/escapeRegExp");
const inputs = require("./plugins/inputs");
const MessageStorage = require("./plugins/messageStorage/sqlite"); const MessageStorage = require("./plugins/messageStorage/sqlite");
const TextFileMessageStorage = require("./plugins/messageStorage/text"); const TextFileMessageStorage = require("./plugins/messageStorage/text");
@ -40,33 +41,6 @@ const events = [
"list", "list",
"whois", "whois",
]; ];
const inputs = [
"ban",
"ctcp",
"msg",
"part",
"rejoin",
"action",
"away",
"connect",
"disconnect",
"ignore",
"invite",
"kick",
"kill",
"mode",
"nick",
"notice",
"quit",
"raw",
"topic",
"list",
"whois",
].reduce(function(plugins, name) {
const plugin = require(`./plugins/inputs/${name}`);
plugin.commands.forEach((command) => plugins[command] = plugin);
return plugins;
}, {});
function Client(manager, name, config = {}) { function Client(manager, name, config = {}) {
_.merge(this, { _.merge(this, {
@ -362,12 +336,20 @@ Client.prototype.inputLine = function(data) {
const irc = target.network.irc; const irc = target.network.irc;
let connected = irc && irc.connection && irc.connection.connected; let connected = irc && irc.connection && irc.connection.connected;
if (Object.prototype.hasOwnProperty.call(inputs, cmd) && typeof inputs[cmd].input === "function") { if (Object.prototype.hasOwnProperty.call(inputs.userInputs, cmd) && typeof inputs.userInputs[cmd].input === "function") {
const plugin = inputs[cmd]; const plugin = inputs.userInputs[cmd];
if (connected || plugin.allowDisconnected) { if (connected || plugin.allowDisconnected) {
connected = true; connected = true;
plugin.input.apply(client, [target.network, target.chan, cmd, args]); plugin.input.apply(client,
[
target.network,
target.chan,
cmd,
args,
(command) => this.inputLine({target: data.target, text: command}),
]
);
} }
} else if (connected) { } else if (connected) {
irc.raw(text); irc.raw(text);

View File

@ -0,0 +1,54 @@
const clientSideCommands = ["/collapse", "/expand"];
const passThroughCommands = [
"/as",
"/bs",
"/cs",
"/ho",
"/hs",
"/join",
"/ms",
"/ns",
"/os",
"/rs",
];
const userInputs = [
"action",
"away",
"ban",
"connect",
"ctcp",
"disconnect",
"ignore",
"invite",
"kick",
"kill",
"list",
"mode",
"msg",
"nick",
"notice",
"part",
"quit",
"raw",
"rejoin",
"topic",
"whois",
].reduce(function(plugins, name) {
const plugin = require(`./${name}`);
plugin.commands.forEach((command) => (plugins[command] = plugin));
return plugins;
}, {});
const getCommands = () =>
Object.keys(userInputs)
.map((command) => `/${command}`)
.concat(clientSideCommands)
.concat(passThroughCommands)
.sort();
module.exports = {
getCommands,
userInputs,
};

View File

@ -6,6 +6,7 @@ const path = require("path");
const Helper = require("../../helper"); const Helper = require("../../helper");
const themes = require("./themes"); const themes = require("./themes");
const packageMap = new Map(); const packageMap = new Map();
const inputs = require("../inputs");
const stylesheets = []; const stylesheets = [];
@ -15,11 +16,15 @@ module.exports = {
loadPackages, loadPackages,
}; };
const packageApis = function(packageName) { const packageApis = function(clientManager, packageName) {
return { return {
Stylesheets: { Stylesheets: {
addFile: addStylesheet.bind(this, packageName), addFile: addStylesheet.bind(this, packageName),
}, },
Commands: {
add: (command, func) => inputs.userInputs[command] = func,
runAsUser: (line, userName, target) => clientManager.findClient(userName).inputLine({target, text: line}),
},
Config: { Config: {
getConfig: () => Helper.config, getConfig: () => Helper.config,
}, },
@ -38,7 +43,7 @@ function getPackage(name) {
return packageMap.get(name); return packageMap.get(name);
} }
function loadPackages() { function loadPackages(clientManager) {
const packageJson = path.join(Helper.getPackagesPath(), "package.json"); const packageJson = path.join(Helper.getPackagesPath(), "package.json");
let packages; let packages;
@ -75,7 +80,7 @@ function loadPackages() {
} }
if (packageFile.onServerStart) { if (packageFile.onServerStart) {
packageFile.onServerStart(packageApis(packageName)); packageFile.onServerStart(packageApis(clientManager, packageName));
} }
log.info(`Package ${colors.bold(packageName)} loaded`); log.info(`Package ${colors.bold(packageName)} loaded`);

View File

@ -16,12 +16,12 @@ const colors = require("chalk");
const net = require("net"); const net = require("net");
const Identification = require("./identification"); const Identification = require("./identification");
const changelog = require("./plugins/changelog"); const changelog = require("./plugins/changelog");
const inputs = require("./plugins/inputs");
const themes = require("./plugins/packages/themes"); const themes = require("./plugins/packages/themes");
themes.loadLocalThemes(); themes.loadLocalThemes();
const packages = require("./plugins/packages/index"); const packages = require("./plugins/packages/index");
packages.loadPackages();
// The order defined the priority: the first available plugin is used // The order defined the priority: the first available plugin is used
// ALways keep local auth in the end, which should always be enabled. // ALways keep local auth in the end, which should always be enabled.
@ -173,6 +173,7 @@ module.exports = function() {
}); });
manager = new ClientManager(); manager = new ClientManager();
packages.loadPackages(manager);
new Identification((identHandler) => { new Identification((identHandler) => {
manager.init(identHandler, sockets); manager.init(identHandler, sockets);
@ -582,6 +583,7 @@ function initializeClient(socket, client, token, lastMessage) {
networks: client.networks.map((network) => network.getFilteredClone(client.lastActiveChannel, lastMessage)), networks: client.networks.map((network) => network.getFilteredClone(client.lastActiveChannel, lastMessage)),
token: tokenToSend, token: tokenToSend,
}); });
socket.emit("commands", inputs.getCommands());
}; };
if (!Helper.config.public && token === null) { if (!Helper.config.public && token === null) {

View File

@ -17,19 +17,6 @@ describe("client-side constants", function() {
}); });
}); });
describe(".commands", function() {
it("should be a non-empty array", function() {
expect(constants.commands).to.be.an("array").that.is.not.empty;
});
it("should only contain strings with no whitespaces and starting with /", function() {
constants.commands.forEach((command) => {
expect(command).to.be.a("string").that.does.not.match(/\s/);
expect(command[0]).to.equal("/");
});
});
});
describe(".condensedTypes", function() { describe(".condensedTypes", function() {
it("should be a non-empty array", function() { it("should be a non-empty array", function() {
expect(constants.condensedTypes).to.be.an("array").that.is.not.empty; expect(constants.condensedTypes).to.be.an("array").that.is.not.empty;

View File

@ -0,0 +1,21 @@
"use strict";
const expect = require("chai").expect;
const inputs = require("../../../src/plugins/inputs");
describe("inputs", function() {
describe(".getCommands", function() {
it("should return a non-empty array", function() {
expect(inputs.getCommands()).to.be.an("array").that.is.not.empty;
});
it("should only return strings with no whitespaces and starting with /", function() {
inputs.getCommands().forEach((command) => {
expect(command)
.to.be.a("string")
.that.does.not.match(/\s/);
expect(command[0]).to.equal("/");
});
});
});
});