BetterDiscordApp-rauenzi/src/modules/pluginmanager2.js

165 lines
7.0 KiB
JavaScript

import {Config} from "data";
import ContentManager from "./contentmanager2";
import Utilities from "./utilities";
import {Toasts, Modals} from "ui";
import ContentError from "../structs/contenterror";
const path = require("path");
const electronRemote = require("electron").remote;
export default new class PluginManager extends ContentManager {
get name() {return "PluginManager";}
get moduleExtension() {return ".js";}
get extension() {return ".plugin.js";}
get contentFolder() {return path.resolve(Config.dataPath, "plugins");}
get prefix() {return "plugin";}
constructor() {
super();
this.onSwitch = this.onSwitch.bind(this);
this.observer = new MutationObserver((mutations) => {
for (let i = 0, mlen = mutations.length; i < mlen; i++) {
this.onMutation(mutations[i]);
}
});
}
/* Aliases */
updatePluginList() {return this.updateList();}
loadAllPlugins() {return this.loadAllContent();}
enablePlugin(idOrContent, fromWatcher = false) {return this.enableContent(idOrContent, fromWatcher);}
disablePlugin(idOrContent, fromWatcher = false) {return this.disableContent(idOrContent, fromWatcher);}
togglePlugin(id) {return this.toggleContent(id);}
loadContent(filename, fromWatcher) {
const error = this.loadPlugin(filename, fromWatcher);
if (!fromWatcher) return error;
if (error) Modals.showContentErrors({plugins: [error]});
}
unloadContent(idOrFileOrContent, fromWatcher) {return this.unloadPlugin(idOrFileOrContent, fromWatcher);}
/* Overrides */
getContentModification(module, content, meta) {
module._compile(content, module.filename);
const didExport = !Utilities.isEmpty(module.exports);
if (didExport) {
meta.type = module.exports;
module.exports = meta;
return "";
}
content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`;
return content;
}
loadPlugin(filename) {
const content = super.loadContent(filename);
if (content instanceof ContentError) return content;
console.log(content);
if (!content.type) return new ContentError(filename, filename, "Plugin had no exports", {message: "Plugin had no exports or no name property.", stack: ""});
try {
const thePlugin = new content.type();
if (this.contentList.find(c => c.id == content.id)) return new ContentError(content.id, filename, `There is already a plugin with name ${content.id}`);
content.plugin = thePlugin;
content.name = content.name || thePlugin.getName();
content.author = content.author || thePlugin.getAuthor();
content.description = content.description || thePlugin.getDescription();
content.version = content.version || thePlugin.getVersion();
this.contentList.push(content);
try {
if (typeof(content.plugin.load) == "function") content.plugin.load();
}
catch (error) {
this.state[content.id] = false;
return new ContentError(content.name, filename, "load() could not be fired.", {message: error.message, stack: error.stack});
}
this.emit("loaded", content.id);
Toasts.success(`${content.name} was loaded.`);
if (!this.state[content.id]) return this.state[content.id] = false;
return this.startPlugin(content);
}
catch (error) {return new ContentError(filename, filename, "Could not be constructed.", {message: error.message, stack: error.stack});}
}
unloadPlugin(idOrFileOrContent, fromWatcher) {
const content = typeof(idOrFileOrContent) == "string" ? this.contentList.find(c => c.id == idOrFileOrContent || c.filename == idOrFileOrContent) : idOrFileOrContent;
if (!content) return false;
if (this.state[content.id]) this.disablePlugin(content, fromWatcher);
super.unloadContent(content);
Toasts.success(`${content.name} was unloaded.`);
this.emit("unloaded", content.id);
return true;
}
reloadPlugin(filename) {
this.reloadContent(filename);
}
startContent(id) {return this.startPlugin(id);}
stopContent(id) {return this.stopPlugin(id);}
startPlugin(idOrContent) {
const content = typeof(idOrContent) == "string" ? this.contentList.find(p => p.id == idOrContent) : idOrContent;
if (!content) return;
const plugin = content.plugin;
try {
plugin.start();
this.emit("started", content.id);
Toasts.show(`${content.name} v${content.version} has started.`);
}
catch (err) {
this.state[content.id] = false;
Toasts.error(`${content.name} v${content.version} could not be started.`);
Utilities.err("Plugins", content.name + " could not be started.", err);
return new ContentError(content.name, content.filename, "start() could not be fired.", {message: err.message, stack: err.stack});
}
}
stopPlugin(idOrContent) {
const content = typeof(idOrContent) == "string" ? this.contentList.find(p => p.id == idOrContent) : idOrContent;
if (!content) return;
const plugin = content.plugin;
try {
plugin.stop();
this.emit("stopped", content.id);
Toasts.show(`${content.name} v${content.version} has stopped.`);
}
catch (err) {
this.state[content.id] = false;
Toasts.error(`${content.name} v${content.version} could not be stopped.`);
Utilities.err("Plugins", content.name + " could not be stopped.", err);
return new ContentError(content.name, content.filename, "stop() could not be fired.", {message: err.message, stack: err.stack});
}
}
setupFunctions() {
electronRemote.getCurrentWebContents().on("did-navigate-in-page", this.onSwitch.bind(this));
this.observer.observe(document, {
childList: true,
subtree: true
});
}
onSwitch() {
this.emit("page-switch");
for (let i = 0; i < this.contentList.length; i++) {
const plugin = this.contentList[i].plugin;
if (!this.state[this.contentList[i].id]) continue;
if (typeof(plugin.onSwitch) === "function") {
try { plugin.onSwitch(); }
catch (err) { Utilities.err("Plugins", "Unable to fire onSwitch for " + this.contentList[i].name + ".", err); }
}
}
}
onMutation(mutation) {
for (let i = 0; i < this.contentList.length; i++) {
const plugin = this.contentList[i].plugin;
if (!this.state[this.contentList[i].id]) continue;
if (typeof plugin.observer === "function") {
try { plugin.observer(mutation); }
catch (err) { Utilities.err("Plugins", "Unable to fire observer for " + this.contentList[i].name + ".", err); }
}
}
}
};