mirror of
https://github.com/AsgardEternal/SquadJS.git
synced 2024-09-28 11:54:23 -05:00
297 lines
10 KiB
JavaScript
297 lines
10 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import {fileURLToPath} from 'url';
|
|
|
|
import Discord from 'discord.js';
|
|
import sequelize from 'sequelize';
|
|
import AwnAPI from './utils/awn-api.js';
|
|
import ConfigTools from './utils/config-tools.js';
|
|
|
|
import Logger from 'core/logger';
|
|
|
|
import SquadServer from './index.js';
|
|
import Plugins from './plugins/index.js';
|
|
|
|
const { Sequelize } = sequelize;
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
export default class SquadServerFactory {
|
|
static async buildFromConfig(config) {
|
|
Logger.verbose('SquadServerFactory', 4, `Logging config:\n${JSON.stringify(config)}`);
|
|
Logger.setTimeStamps(config.logger.timestamps ? config.logger.timestamps : false);
|
|
|
|
const plugins = await Plugins.getPlugins();
|
|
|
|
for (const plugin of Object.keys(plugins)) {
|
|
Logger.setColor(plugin, 'magentaBright');
|
|
}
|
|
|
|
// setup logging levels
|
|
for (const [module, verboseness] of Object.entries(config.logger.verboseness)) {
|
|
Logger.setVerboseness(module, verboseness);
|
|
}
|
|
|
|
for (const [module, color] of Object.entries(config.logger.colors)) {
|
|
Logger.setColor(module, color);
|
|
}
|
|
|
|
// create SquadServer
|
|
Logger.verbose('SquadServerFactory', 1, 'Creating SquadServer...');
|
|
const server = new SquadServer(config.server);
|
|
|
|
// initialise connectors
|
|
Logger.verbose('SquadServerFactory', 1, 'Preparing connectors...');
|
|
const connectors = {};
|
|
for (const pluginConfig of config.plugins) {
|
|
if (!pluginConfig.enabled) continue;
|
|
|
|
if (!plugins[pluginConfig.plugin])
|
|
throw new Error(`Plugin ${pluginConfig.plugin} does not exist.`);
|
|
|
|
const Plugin = plugins[pluginConfig.plugin];
|
|
|
|
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
|
|
// ignore non connectors
|
|
if (!option.connector) continue;
|
|
|
|
// check the connector is listed in the options
|
|
if (!(optionName in pluginConfig))
|
|
throw new Error(
|
|
`${Plugin.name}: ${optionName} (${option.connector} connector) is missing.`
|
|
);
|
|
|
|
// get the name of the connector
|
|
const connectorName = pluginConfig[optionName];
|
|
|
|
// skip already created connectors
|
|
if (connectors[connectorName]) continue;
|
|
|
|
// create the connector
|
|
connectors[connectorName] = await SquadServerFactory.createConnector(
|
|
server,
|
|
option.connector,
|
|
connectorName,
|
|
config.connectors[connectorName]
|
|
);
|
|
}
|
|
}
|
|
|
|
// initialise plugins
|
|
Logger.verbose('SquadServerFactory', 1, 'Initialising plugins...');
|
|
|
|
for (const pluginConfig of config.plugins) {
|
|
if (!pluginConfig.enabled) continue;
|
|
|
|
if (!plugins[pluginConfig.plugin])
|
|
throw new Error(`Plugin ${pluginConfig.plugin} does not exist.`);
|
|
|
|
const Plugin = plugins[pluginConfig.plugin];
|
|
|
|
Logger.verbose('SquadServerFactory', 1, `Initialising ${Plugin.name}...`);
|
|
|
|
const plugin = new Plugin(server, pluginConfig, connectors);
|
|
|
|
// allow the plugin to do any asynchronous work needed before it can be mounted
|
|
await plugin.prepareToMount();
|
|
|
|
server.plugins.push(plugin);
|
|
}
|
|
|
|
return server;
|
|
}
|
|
|
|
static async createConnector(server, type, connectorName, connectorConfig) {
|
|
Logger.verbose('SquadServerFactory', 1, `Starting ${type} connector ${connectorName}...`);
|
|
|
|
if (type === 'discord') {
|
|
Logger.verbose('SquadServerFactory', 1, `Connector is Discord Type`);
|
|
const connector = new Discord.Client();
|
|
Logger.verbose('SquadServerFactory', 1, `Connector created Discord client`);
|
|
await connector.login(connectorConfig);
|
|
Logger.verbose('SquadServerFactory', 1, `Connector Logged into Discord`);
|
|
return connector;
|
|
}
|
|
|
|
if (type === 'sequelize') {
|
|
Logger.verbose('SquadServerFactory', 1, `Connector is SQL Type`);
|
|
let connector;
|
|
|
|
if (typeof connectorConfig === 'string') {
|
|
connector = new Sequelize(connectorConfig, {
|
|
define: {
|
|
charset: 'utf8mb4',
|
|
collate: 'utf8mb4_unicode_ci'
|
|
},
|
|
logging: (msg) => Logger.verbose('Sequelize', 3, msg)
|
|
});
|
|
} else if (typeof connectorConfig === 'object') {
|
|
connector = new Sequelize({
|
|
...connectorConfig,
|
|
logging: (msg) => Logger.verbose('Sequelize', 3, msg)
|
|
});
|
|
} else {
|
|
throw new Error('Unknown sequelize connector config type.');
|
|
}
|
|
Logger.verbose('SquadServerFactory', 1, `Connector created SQL client`);
|
|
|
|
await connector.authenticate();
|
|
Logger.verbose('SquadServerFactory', 1, `Connector Logged into SQL`);
|
|
return connector;
|
|
}
|
|
|
|
if (type === 'awnAPI') {
|
|
const awn = new AwnAPI(connectorConfig);
|
|
await awn.auth(connectorConfig);
|
|
return awn;
|
|
}
|
|
|
|
throw new Error(`${type.connector} is an unsupported connector type.`);
|
|
}
|
|
|
|
static parseConfig(configString, configfile = "", previousconfigfiles = null) {
|
|
configfile = path.resolve(configfile);
|
|
if (!previousconfigfiles) previousconfigfiles = []
|
|
if (previousconfigfiles.indexOf(configfile) > -1){
|
|
Logger.verbose('SquadServerFactory', 1, `Found circular config with ${previousconfigfiles.slice(1)}`);
|
|
throw new Error(`Config has circular dependency at ${configfile} in with previous configs ${previousconfigfiles}!`);
|
|
}
|
|
else previousconfigfiles.push(configfile)
|
|
try {
|
|
const jsonconfig = JSON.parse(configString);
|
|
const beforeincludes = jsonconfig.baseincludes ? jsonconfig.baseincludes.flatMap((filename) => {
|
|
try {
|
|
return SquadServerFactory.parseConfig(
|
|
SquadServerFactory.readConfigFile(
|
|
filename,
|
|
path.dirname(configfile)
|
|
),
|
|
filename,
|
|
previousconfigfiles
|
|
);
|
|
} catch (err) {
|
|
throw new Error(`Config file ${filename} from ${configfile} did not load. ${err}`)
|
|
}
|
|
}) : [];
|
|
const overwrites = jsonconfig.overwrites ? jsonconfig.overwrites.flatMap((filename) => SquadServerFactory.parseConfig(SquadServerFactory.readConfigFile(filename), filename, previousconfigfiles)) : [];
|
|
const retconfig = [beforeincludes, jsonconfig, overwrites].flat(1);
|
|
return retconfig;
|
|
} catch (err) {
|
|
throw new Error(`Unable to parse config file. ${err}`);
|
|
}
|
|
}
|
|
|
|
static buildFromConfigString(configString) {
|
|
Logger.verbose('SquadServerFactory', 1, 'Parsing config string...');
|
|
return SquadServerFactory.buildFromConfig(SquadServerFactory.parseConfig(configString));
|
|
}
|
|
|
|
static readConfigFile(configPath = './config.json', dirlocation = null) {
|
|
const configLoc = dirlocation ? path.resolve(__dirname, '../', dirlocation, configPath) : path.resolve(__dirname, '../', configPath);
|
|
if (!fs.existsSync(configLoc)) throw new Error(`Config file does not exist. ${configLoc}`);
|
|
|
|
Logger.verbose('SquadServerFactory', 1, `Reading config file ${configLoc}`);
|
|
return fs.readFileSync(configLoc, 'utf8');
|
|
}
|
|
|
|
static buildFromConfigFile(configPath) {
|
|
Logger.verbose('SquadServerFactory', 1, 'Reading config file...');
|
|
Logger.verbose('SquadServerFactory', 4, JSON.stringify(configPath));
|
|
let configs = SquadServerFactory.parseConfig(SquadServerFactory.readConfigFile(configPath));
|
|
|
|
const mergedconfig = ConfigTools.mergeConfigs({}, ...configs);
|
|
Logger.verbose('SquadServerFactory', 4, `Merged config: ${JSON.stringify(mergedconfig)}`)
|
|
return SquadServerFactory.buildFromConfig(mergedconfig);
|
|
}
|
|
|
|
static async buildConfig() {
|
|
const plugins = await Plugins.getPlugins();
|
|
|
|
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;
|
|
}
|
|
|
|
static async buildConfigFile() {
|
|
const configPath = path.resolve(__dirname, '../config.example.json');
|
|
const config = await SquadServerFactory.buildConfig();
|
|
|
|
const configString = JSON.stringify(config, null, 2);
|
|
fs.writeFileSync(configPath, configString);
|
|
}
|
|
|
|
static async buildReadmeFile() {
|
|
const plugins = await Plugins.getPlugins();
|
|
|
|
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)) {
|
|
let optionInfo = `<li><h4>${optionName}${option.required ? ' (Required)' : ''}</h4>
|
|
<h6>Description</h6>
|
|
<p>${option.description}</p>
|
|
<h6>Default</h6>
|
|
<pre><code>${
|
|
typeof option.default === 'object'
|
|
? JSON.stringify(option.default, null, 2)
|
|
: option.default
|
|
}</code></pre></li>`;
|
|
|
|
if (option.example)
|
|
optionInfo += `<h6>Example</h6>
|
|
<pre><code>${
|
|
typeof option.example === 'object'
|
|
? JSON.stringify(option.example, null, 2)
|
|
: option.example
|
|
}</code></pre>`;
|
|
|
|
options.push(optionInfo);
|
|
}
|
|
|
|
pluginInfo.push(
|
|
`<details>
|
|
<summary>${Plugin.name}</summary>
|
|
<h2>${Plugin.name}</h2>
|
|
<p>${Plugin.description}</p>
|
|
<h3>Options</h3>
|
|
<ul>${options.join('\n')}</ul>
|
|
</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);
|
|
}
|
|
}
|