SquadJS/squad-server/factory.js

245 lines
7.5 KiB
JavaScript
Raw Normal View History

2020-11-07 06:03:25 -06:00
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import Discord from 'discord.js';
2020-12-06 15:21:41 -06:00
import sequelize from 'sequelize';
2020-11-07 06:03:25 -06:00
import Logger from 'core/logger';
import SquadServer from './index.js';
2021-01-30 08:39:56 -06:00
import Plugins from './plugins/index.js';
2020-11-07 06:03:25 -06:00
2020-12-06 15:21:41 -06:00
const { Sequelize } = sequelize;
2020-11-07 06:03:25 -06:00
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default class SquadServerFactory {
static async buildFromConfig(config) {
2021-01-30 08:39:56 -06:00
const plugins = await Plugins.getPlugins();
2020-12-08 10:40:56 -06:00
for (const plugin of Object.keys(plugins)) {
2020-12-08 11:16:32 -06:00
Logger.setColor(plugin, 'magentaBright');
2020-12-08 10:40:56 -06:00
}
2020-12-03 08:16:07 -06:00
// setup logging levels
2020-12-08 14:27:17 -06:00
for (const [module, verboseness] of Object.entries(config.logger.verboseness)) {
2020-11-07 06:03:25 -06:00
Logger.setVerboseness(module, verboseness);
}
2020-12-08 14:27:17 -06:00
for (const [module, color] of Object.entries(config.logger.colors)) {
2020-12-08 10:40:56 -06:00
Logger.setColor(module, color);
}
2020-12-03 08:16:07 -06:00
// create SquadServer
2020-11-07 06:03:25 -06:00
Logger.verbose('SquadServerFactory', 1, 'Creating SquadServer...');
const server = new SquadServer(config.server);
2020-12-03 08:16:07 -06:00
// initialise connectors
2020-11-07 06:03:25 -06:00
Logger.verbose('SquadServerFactory', 1, 'Preparing connectors...');
const connectors = {};
for (const pluginConfig of config.plugins) {
if (!pluginConfig.enabled) continue;
2020-12-08 10:52:44 -06:00
if (!plugins[pluginConfig.plugin])
throw new Error(`Plugin ${pluginConfig.plugin} does not exist.`);
2020-11-07 06:03:25 -06:00
const Plugin = plugins[pluginConfig.plugin];
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
// ignore non connectors
if (!option.connector) continue;
2020-12-03 08:16:07 -06:00
// check the connector is listed in the options
2020-12-06 15:23:05 -06:00
if (!(optionName in pluginConfig))
throw new Error(
`${Plugin.name}: ${optionName} (${option.connector} connector) is missing.`
);
2020-11-07 06:03:25 -06:00
2020-12-03 08:16:07 -06:00
// get the name of the connector
2020-11-07 06:03:25 -06:00
const connectorName = pluginConfig[optionName];
// skip already created connectors
if (connectors[connectorName]) continue;
2020-12-03 08:16:07 -06:00
// create the connector
2020-12-06 15:23:05 -06:00
connectors[connectorName] = await SquadServerFactory.createConnector(
server,
option.connector,
connectorName,
config.connectors[connectorName]
);
2020-11-07 06:03:25 -06:00
}
}
2020-12-03 08:16:07 -06:00
// initialise plugins
Logger.verbose('SquadServerFactory', 1, 'Initialising plugins...');
2020-12-06 15:23:05 -06:00
for (const pluginConfig of config.plugins) {
2020-11-07 06:03:25 -06:00
if (!pluginConfig.enabled) continue;
2020-12-06 15:23:05 -06:00
if (!plugins[pluginConfig.plugin])
throw new Error(`Plugin ${pluginConfig.plugin} does not exist.`);
2020-11-07 06:03:25 -06:00
const Plugin = plugins[pluginConfig.plugin];
Logger.verbose('SquadServerFactory', 1, `Initialising ${Plugin.name}...`);
2020-12-03 08:16:07 -06:00
const plugin = new Plugin(server, pluginConfig, connectors);
2020-11-07 06:03:25 -06:00
2020-12-03 08:16:07 -06:00
// allow the plugin to do any asynchronous work needed before it can be mounted
await plugin.prepareToMount();
2020-11-07 06:03:25 -06:00
2020-12-03 08:16:07 -06:00
server.plugins.push(plugin);
}
2020-11-07 06:03:25 -06:00
return server;
}
2020-12-03 08:16:07 -06:00
static async createConnector(server, type, connectorName, connectorConfig) {
Logger.verbose('SquadServerFactory', 1, `Starting ${type} connector ${connectorName}...`);
if (type === 'discord') {
const connector = new Discord.Client();
await connector.login(connectorConfig);
return connector;
}
2020-12-06 15:21:41 -06:00
if (type === 'sequelize') {
2020-12-09 08:33:56 -06:00
let connector;
2020-12-10 14:46:29 -06:00
if (typeof connectorConfig === 'string') {
connector = new Sequelize(connectorConfig, {
logging: (msg) => Logger.verbose('Sequelize', 3, msg)
});
2020-12-09 08:33:56 -06:00
} else if (typeof connectorConfig === 'object') {
2020-12-10 14:46:29 -06:00
connector = new Sequelize({
...connectorConfig,
logging: (msg) => Logger.verbose('Sequelize', 3, msg)
});
2020-12-09 08:33:56 -06:00
} else {
throw new Error('Unknown sequelize connector config type.');
}
2020-12-10 14:46:29 -06:00
2020-12-06 15:21:41 -06:00
await connector.authenticate();
return connector;
2020-12-03 08:16:07 -06:00
}
throw new Error(`${type.connector} is an unsupported connector type.`);
}
2020-11-07 06:03:25 -06:00
static parseConfig(configString) {
try {
return JSON.parse(configString);
} catch (err) {
throw new Error('Unable to parse config file.');
}
}
static buildFromConfigString(configString) {
Logger.verbose('SquadServerFactory', 1, 'Parsing config string...');
return SquadServerFactory.buildFromConfig(SquadServerFactory.parseConfig(configString));
}
static readConfigFile(configPath = './config.json') {
configPath = path.resolve(__dirname, '../', configPath);
if (!fs.existsSync(configPath)) throw new Error('Config file does not exist.');
return fs.readFileSync(configPath, 'utf8');
}
static buildFromConfigFile(configPath) {
Logger.verbose('SquadServerFactory', 1, 'Reading config file...');
return SquadServerFactory.buildFromConfigString(SquadServerFactory.readConfigFile(configPath));
}
2021-01-30 08:39:56 -06:00
static async buildConfig() {
const plugins = await Plugins.getPlugins();
2020-11-07 06:03:25 -06:00
const templatePath = path.resolve(__dirname, './templates/config-template.json');
const templateString = fs.readFileSync(templatePath, 'utf8');
const template = SquadServerFactory.parseConfig(templateString);
const pluginKeys = Object.keys(plugins).sort((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
);
for (const pluginKey of pluginKeys) {
const Plugin = plugins[pluginKey];
const pluginConfig = { plugin: Plugin.name, enabled: Plugin.defaultEnabled };
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
pluginConfig[optionName] = option.default;
}
template.plugins.push(pluginConfig);
}
return template;
}
2021-01-30 08:39:56 -06:00
static async buildConfigFile() {
2020-11-07 06:03:25 -06:00
const configPath = path.resolve(__dirname, '../config.json');
2021-01-30 08:39:56 -06:00
const config = await SquadServerFactory.buildConfig();
const configString = JSON.stringify(config, null, 2);
fs.writeFileSync(configPath, configString);
2020-11-07 06:03:25 -06:00
}
2021-01-30 08:39:56 -06:00
static async buildReadmeFile() {
const plugins = await Plugins.getPlugins();
2020-11-07 06:03:25 -06:00
const pluginKeys = Object.keys(plugins).sort((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
);
const pluginInfo = [];
for (const pluginName of pluginKeys) {
const Plugin = plugins[pluginName];
const options = [];
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
2020-12-16 14:17:55 -06:00
let optionInfo = `<li><h4>${optionName}${option.required ? ' (Required)' : ''}</h4>
2020-11-07 06:03:25 -06:00
<h6>Description</h6>
<p>${option.description}</p>
<h6>Default</h6>
<pre><code>${
2020-12-06 15:23:05 -06:00
typeof option.default === 'object'
? JSON.stringify(option.default, null, 2)
: option.default
2020-12-16 14:17:55 -06:00
}</code></pre></li>`;
2020-11-07 06:03:25 -06:00
if (option.example)
optionInfo += `<h6>Example</h6>
<pre><code>${
2020-12-06 15:23:05 -06:00
typeof option.example === 'object'
? JSON.stringify(option.example, null, 2)
: option.example
}</code></pre>`;
2020-11-07 06:03:25 -06:00
options.push(optionInfo);
}
pluginInfo.push(
`<details>
<summary>${Plugin.name}</summary>
<h2>${Plugin.name}</h2>
<p>${Plugin.description}</p>
<h3>Options</h3>
2020-12-16 14:17:55 -06:00
<ul>${options.join('\n')}</ul>
2020-11-07 06:03:25 -06:00
</details>`
);
}
const pluginInfoText = pluginInfo.join('\n\n');
const templatePath = path.resolve(__dirname, './templates/readme-template.md');
const template = fs.readFileSync(templatePath, 'utf8');
const readmePath = path.resolve(__dirname, '../README.md');
const readme = template.replace(/\/\/PLUGIN-INFO\/\//, pluginInfoText);
fs.writeFileSync(readmePath, readme);
}
}