Add support for command plugins

This commit is contained in:
Alistair McKinlay 2019-07-02 17:02:02 +01:00
parent 0d3fc7b4fb
commit 4cbe72f635
9 changed files with 108 additions and 104 deletions

View file

@ -19,62 +19,6 @@ const colorCodeMap = [
["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 = [
"away",
"back",
@ -95,7 +39,7 @@ const timeFormats = {
module.exports = {
colorCodeMap,
commands,
commands: [],
condensedTypes,
condensedTypesQuery,
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("./change_password");
require("./commands");
require("./init");
require("./join");
require("./more");

View file

@ -11,6 +11,7 @@ const Helper = require("./helper");
const UAParser = require("ua-parser-js");
const uuidv4 = require("uuid/v4");
const escapeRegExp = require("lodash/escapeRegExp");
const inputs = require("./plugins/inputs");
const MessageStorage = require("./plugins/messageStorage/sqlite");
const TextFileMessageStorage = require("./plugins/messageStorage/text");
@ -40,33 +41,6 @@ const events = [
"list",
"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 = {}) {
_.merge(this, {
@ -362,12 +336,20 @@ Client.prototype.inputLine = function(data) {
const irc = target.network.irc;
let connected = irc && irc.connection && irc.connection.connected;
if (Object.prototype.hasOwnProperty.call(inputs, cmd) && typeof inputs[cmd].input === "function") {
const plugin = inputs[cmd];
if (Object.prototype.hasOwnProperty.call(inputs.userInputs, cmd) && typeof inputs.userInputs[cmd].input === "function") {
const plugin = inputs.userInputs[cmd];
if (connected || plugin.allowDisconnected) {
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) {
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 themes = require("./themes");
const packageMap = new Map();
const inputs = require("../inputs");
const stylesheets = [];
@ -15,11 +16,15 @@ module.exports = {
loadPackages,
};
const packageApis = function(packageName) {
const packageApis = function(clientManager, 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}),
},
Config: {
getConfig: () => Helper.config,
},
@ -38,7 +43,7 @@ function getPackage(name) {
return packageMap.get(name);
}
function loadPackages() {
function loadPackages(clientManager) {
const packageJson = path.join(Helper.getPackagesPath(), "package.json");
let packages;
@ -75,7 +80,7 @@ function loadPackages() {
}
if (packageFile.onServerStart) {
packageFile.onServerStart(packageApis(packageName));
packageFile.onServerStart(packageApis(clientManager, packageName));
}
log.info(`Package ${colors.bold(packageName)} loaded`);

View file

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