BetterDiscordApp-v2/client/src/modules/pluginmanager.js

171 lines
5.8 KiB
JavaScript
Raw Normal View History

2018-01-30 14:20:24 +01:00
/**
* BetterDiscord Plugin Manager Module
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
2018-07-18 21:57:05 +02:00
import { Permissions } from 'modules';
import { Modals } from 'ui';
import { ErrorEvent } from 'structs';
import { ClientLogger as Logger } from 'common';
import Globals from './globals';
2018-01-30 16:59:27 +01:00
import ContentManager from './contentmanager';
2018-02-14 08:56:55 +01:00
import ExtModuleManager from './extmodulemanager';
2018-01-30 16:59:27 +01:00
import Plugin from './plugin';
import PluginApi from './pluginapi';
import Vendor from './vendor';
2018-01-30 14:20:24 +01:00
2018-01-30 16:59:27 +01:00
export default class extends ContentManager {
2018-01-30 14:20:24 +01:00
static get localPlugins() {
2018-01-30 16:59:27 +01:00
return this.localContent;
}
2018-02-07 17:02:27 +01:00
static get contentType() {
return 'plugin';
}
2018-01-30 16:59:27 +01:00
static get moduleName() {
2018-02-13 17:57:05 +01:00
return 'Plugin Manager';
2018-01-30 14:20:24 +01:00
}
2018-01-30 16:59:27 +01:00
static get pathId() {
return 'plugins';
2018-01-30 14:25:35 +01:00
}
static async loadAllPlugins(suppressErrors) {
2018-02-21 18:46:27 +01:00
this.loaded = false;
const loadAll = await this.loadAllContent(true);
2018-02-21 18:46:27 +01:00
this.loaded = true;
2018-08-15 08:01:47 +02:00
for (const plugin of this.localPlugins) {
2018-03-06 01:24:14 +01:00
if (!plugin.enabled) continue;
plugin.userConfig.enabled = false;
try {
2018-03-06 01:24:14 +01:00
plugin.start(false);
} catch (err) {
// Disable the plugin but don't save it - the next time BetterDiscord is started the plugin will attempt to start again
this.errors.push(new ErrorEvent({
module: this.moduleName,
message: `Failed to start ${plugin.name}`,
err
}));
Logger.err(this.moduleName, [`Failed to start plugin ${plugin.name}:`, err]);
}
}
if (this.errors.length && !suppressErrors) {
Modals.error({
header: `${this.moduleName} - ${this.errors.length} ${this.contentType}${this.errors.length !== 1 ? 's' : ''} failed to load`,
module: this.moduleName,
type: 'err',
content: this.errors
});
this._errors = [];
2018-02-21 18:46:27 +01:00
}
return loadAll;
}
2018-01-30 23:21:06 +01:00
static get refreshPlugins() { return this.refreshContent }
2018-01-30 16:59:27 +01:00
static get loadContent() { return this.loadPlugin }
2018-08-27 18:16:30 +02:00
static async loadPlugin(paths, configs, info, main, dependencies, permissions, mainExport, packed = false) {
2018-02-28 20:34:12 +01:00
if (permissions && permissions.length > 0) {
2018-08-15 08:01:47 +02:00
for (const perm of permissions) {
Logger.log(this.moduleName, `Permission: ${Permissions.permissionText(perm).HEADER} - ${Permissions.permissionText(perm).BODY}`);
2018-02-28 20:34:12 +01:00
}
try {
const allowed = await Modals.permissions(`${info.name} wants to:`, info.name, permissions).promise;
} catch (err) {
return;
2018-02-28 20:34:12 +01:00
}
}
const deps = {};
2018-02-14 08:56:55 +01:00
if (dependencies) {
for (const [key, value] of Object.entries(dependencies)) {
const extModule = ExtModuleManager.findModule(key);
if (!extModule) {
2018-03-22 03:13:32 +01:00
throw {message: `Dependency ${key}:${value} is not loaded.`};
2018-02-14 08:56:55 +01:00
}
deps[key] = extModule.__require;
2018-02-14 08:56:55 +01:00
}
}
2018-06-23 00:55:53 +02:00
const pluginExports = Globals.require(paths.mainPath);
const pluginFunction = mainExport ? pluginExports[mainExport]
: pluginExports.__esModule ? pluginExports.default : pluginExports;
if (typeof pluginFunction !== 'function')
throw {message: `Plugin ${info.name} did not export a function.`};
const plugin = pluginFunction.call(pluginExports, Plugin, new PluginApi(info, paths.contentPath), Vendor, deps);
if (!plugin || !(plugin.prototype instanceof Plugin))
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
2018-02-21 18:46:27 +01:00
const instance = new plugin({
2019-03-12 20:47:55 +01:00
configs, info, main, paths
2018-02-21 18:46:27 +01:00
});
2018-01-30 23:21:06 +01:00
2018-03-06 01:24:14 +01:00
if (instance.enabled && this.loaded) {
instance.userConfig.enabled = false;
instance.start(false);
}
2018-02-21 18:46:27 +01:00
return instance;
2018-01-30 23:21:06 +01:00
}
static get deletePlugin() { return this.deleteContent }
2018-02-21 18:46:27 +01:00
static get unloadPlugin() { return this.unloadContent }
static get reloadPlugin() { return this.reloadContent }
2018-01-31 09:17:15 +01:00
static unloadContentHook(content, reload) {
delete Globals.require.cache[Globals.require.resolve(content.paths.mainPath)];
const uncache = [];
for (const required in Globals.require.cache) {
if (!required.includes(content.paths.contentPath)) continue;
uncache.push(Globals.require.resolve(required));
}
for (const u of uncache) delete Globals.require.cache[u];
}
2018-03-22 03:13:32 +01:00
/**
* Stops a plugin.
* @param {Plugin|String} plugin
* @return {Promise}
*/
static stopPlugin(plugin) {
plugin = this.isPlugin(plugin) ? plugin : this.getPluginById(plugin);
return plugin.stop();
2018-01-31 09:32:20 +01:00
}
2018-03-22 03:13:32 +01:00
/**
* Starts a plugin.
* @param {Plugin|String} plugin
* @return {Promise}
*/
static startPlugin(plugin) {
plugin = this.isPlugin(plugin) ? plugin : this.getPluginById(plugin);
return plugin.start();
2018-01-31 09:32:20 +01:00
}
2018-02-21 18:46:27 +01:00
static get isPlugin() { return this.isThisContent }
static isThisContent(plugin) {
return plugin instanceof Plugin;
}
2018-01-30 23:21:06 +01:00
static get findPlugin() { return this.findContent }
static get getPluginIndex() { return this.getContentIndex }
static get getPluginByName() { return this.getContentByName }
static get getPluginById() { return this.getContentById }
static get getPluginByPath() { return this.getContentByPath }
static get getPluginByDirName() { return this.getContentByDirName }
2018-02-12 23:49:44 +01:00
static get waitForPlugin() { return this.waitForContent }
2018-01-30 14:20:24 +01:00
}