From e823b1c9d0a2a59fd75cee6009f348a6d5eb452d Mon Sep 17 00:00:00 2001 From: Jiiks Date: Mon, 15 Jan 2018 14:01:43 +0200 Subject: [PATCH 1/5] Test vs branch manager --- client/src/modules/pluginmanager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/modules/pluginmanager.js b/client/src/modules/pluginmanager.js index 6351ba33..4afba4dd 100644 --- a/client/src/modules/pluginmanager.js +++ b/client/src/modules/pluginmanager.js @@ -16,6 +16,7 @@ class PluginManager extends Module { this.setState({ plugins: [] }); +//Testpush } get plugins() { From bc0513803797124e54109a6be2edeff490231bb9 Mon Sep 17 00:00:00 2001 From: Jiiks Date: Mon, 15 Jan 2018 14:17:13 +0200 Subject: [PATCH 2/5] Tests base --- client/dist/betterdiscord.client.js | 233 +++++++++++++++++++++------- client/src/modules/pluginmanager.js | 124 ++++++++++++++- tests/plugins/Example/config.json | 6 + 3 files changed, 304 insertions(+), 59 deletions(-) create mode 100644 tests/plugins/Example/config.json diff --git a/client/dist/betterdiscord.client.js b/client/dist/betterdiscord.client.js index 77c05af6..1abbcb99 100644 --- a/client/dist/betterdiscord.client.js +++ b/client/dist/betterdiscord.client.js @@ -60,7 +60,7 @@ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 125); +/******/ return __webpack_require__(__webpack_require__.s = 126); /******/ }) /************************************************************************/ /******/ ([ @@ -1911,7 +1911,7 @@ function loadLocale(name) { try { oldLocale = globalLocale._abbr; var aliasedRequire = require; - __webpack_require__(128)("./" + name); + __webpack_require__(129)("./" + name); getSetGlobalLocale(oldLocale); } catch (e) {} } @@ -4603,7 +4603,7 @@ return hooks; }))); -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(127)(module))) +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(128)(module))) /***/ }), /* 1 */ @@ -16421,6 +16421,37 @@ return zhTw; /* 123 */ /***/ (function(module, exports, __webpack_require__) { +/** + * BetterDiscord Client IPC Module + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://github.com/JsSucks - 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. +*/ + +const { ipcRenderer } = __webpack_require__(131); + +class BDIpc { + + static async send(channel, message) { + channel = channel.startsWith('bd-') ? channel : `bd-${channel}`; + const __eid = Date.now().toString(); + ipcRenderer.send(channel, Object.assign(message ? message : {}, { __eid })); + return new Promise((resolve, reject) => { + ipcRenderer.once(__eid, (event, arg) => resolve(arg)); + }); + } + +} + +module.exports = { BDIpc }; + +/***/ }), +/* 124 */ +/***/ (function(module, exports, __webpack_require__) { + /** * BetterDiscord Client Globals * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks @@ -16469,7 +16500,7 @@ const _instance = new Global(); module.exports = { 'Global': _instance }; /***/ }), -/* 124 */ +/* 125 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -26729,7 +26760,7 @@ return jQuery; /***/ }), -/* 125 */ +/* 126 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26745,7 +26776,7 @@ return jQuery; -const { Logger, Utils, PluginManager, BDIpc, WebpackModules, SocketProxy, Global } = __webpack_require__(126); +const { Logger, Utils, PluginManager, BDIpc, WebpackModules, SocketProxy, Global } = __webpack_require__(127); class BetterDiscord { @@ -26763,15 +26794,15 @@ if (window.BetterDiscord) { let bdInstance = new BetterDiscord(); window.BetterDiscord = { 'vendor': { - jQuery: __webpack_require__(124), - $: __webpack_require__(124), + jQuery: __webpack_require__(125), + $: __webpack_require__(125), moment: __webpack_require__(0) } }; } /***/ }), -/* 126 */ +/* 127 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -26780,19 +26811,19 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__utils__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__utils__, "Logger")) __webpack_require__.d(__webpack_exports__, "Logger", function() { return __WEBPACK_IMPORTED_MODULE_0__utils__["Logger"]; }); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__utils__, "Utils")) __webpack_require__.d(__webpack_exports__, "Utils", function() { return __WEBPACK_IMPORTED_MODULE_0__utils__["Utils"]; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__pluginmanager__ = __webpack_require__(129); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__pluginmanager__ = __webpack_require__(130); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__pluginmanager___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__pluginmanager__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_1__pluginmanager__, "PluginManager")) __webpack_require__.d(__webpack_exports__, "PluginManager", function() { return __WEBPACK_IMPORTED_MODULE_1__pluginmanager__["PluginManager"]; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__plugin__ = __webpack_require__(130); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__plugin__ = __webpack_require__(132); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__plugin___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__plugin__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_2__plugin__, "Pluging")) __webpack_require__.d(__webpack_exports__, "Pluging", function() { return __WEBPACK_IMPORTED_MODULE_2__plugin__["Pluging"]; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__bdipc__ = __webpack_require__(131); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__bdipc__ = __webpack_require__(123); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__bdipc___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__bdipc__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_3__bdipc__, "BDIpc")) __webpack_require__.d(__webpack_exports__, "BDIpc", function() { return __WEBPACK_IMPORTED_MODULE_3__bdipc__["BDIpc"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__webpackmodules__ = __webpack_require__(133); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__webpackmodules___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__webpackmodules__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_4__webpackmodules__, "WebpackModules")) __webpack_require__.d(__webpack_exports__, "WebpackModules", function() { return __WEBPACK_IMPORTED_MODULE_4__webpackmodules__["WebpackModules"]; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__global__ = __webpack_require__(123); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__global__ = __webpack_require__(124); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__global___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__global__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_5__global__, "Global")) __webpack_require__.d(__webpack_exports__, "Global", function() { return __WEBPACK_IMPORTED_MODULE_5__global__["Global"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__events__ = __webpack_require__(2); @@ -26811,7 +26842,7 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /***/ }), -/* 127 */ +/* 128 */ /***/ (function(module, exports) { module.exports = function(module) { @@ -26839,7 +26870,7 @@ module.exports = function(module) { /***/ }), -/* 128 */ +/* 129 */ /***/ (function(module, exports, __webpack_require__) { var map = { @@ -27096,10 +27127,10 @@ webpackContext.keys = function webpackContextKeys() { }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; -webpackContext.id = 128; +webpackContext.id = 129; /***/ }), -/* 129 */ +/* 130 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -27113,6 +27144,102 @@ webpackContext.id = 128; */ const { Module } = __webpack_require__(1); +const { BDIpc } = __webpack_require__(123); +const fs = window.require('fs'); + +//TODO add these to actual utils +class Utils { + + static async tryParseJson(jsonString) { + try { + return JSON.parse(jsonString); + } catch (err) { + throw { + 'message': 'Failed to parse json', + err + }; + } + } + + static get timestamp() { + return 'Timestamp'; + } + +} + +class FileUtils { + + static async fileExists(path) { + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `No such file or directory: ${err.path}`, + err + }); + + if (!stats.isFile()) return reject({ + 'message': `Not a file: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async directoryExists(path) { + return new Promise(resolve => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `Directory does not exist: ${path}`, + err + }); + + if (!stats.isDirectory()) return reject({ + 'message': `Not a directory: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async readFile(path) { + try { + await this.fileExists(path); + } catch (err) { + throw err; + } + + return new Promise(resolve => { + fs.readFile(path, 'utf-8', (err, data) => { + if (err) reject({ + 'message': `Could not read file: ${path}`, + err + }); + + resolve(data); + }); + }); + } + + static async readJsonFromFile(path) { + let readFile; + try { + readFile = await this.readFile(path); + } catch (err) { + throw err; + } + + try { + const parsed = await Utils.tryParseJson(readFile); + return parsed; + } catch (err) { + throw Object.assign(err, { path }); + } + } +} class PluginManager extends Module { @@ -27120,6 +27247,7 @@ class PluginManager extends Module { this.setState({ plugins: [] }); + tests(); } get plugins() { @@ -27127,7 +27255,7 @@ class PluginManager extends Module { } loadPlugin(plugin) { - const { plugins } = this; + const { plugins } = this.state; plugins.push(plugin); this.setState({ plugins @@ -27142,14 +27270,42 @@ class PluginManager extends Module { return this.plugins.find(plugin => plugin.id === id); } + async readConfig(path) { + path = `${path}/config.json`; + return FileUtils.readJsonFromFile(path); + } + } const _instance = new PluginManager(); +async function tests() { + + const config = await BDIpc.send('getConfig'); + const pluginPath = config.paths.find(path => 'plugins' in path).plugins; + console.log(`Plugin Path: ${pluginPath}`); + + const examplePluginPath = `${pluginPath}/Example`; + + //Test read config + try { + const readConfig = await _instance.readConfig(examplePluginPath); + console.log(readConfig); + } catch (err) { + console.log(err); + } +} + module.exports = { PluginManager: _instance }; /***/ }), -/* 130 */ +/* 131 */ +/***/ (function(module, exports) { + +module.exports = window.require("electron"); + +/***/ }), +/* 132 */ /***/ (function(module, exports) { /** @@ -27170,43 +27326,6 @@ class Plugin { module.exports = { Plugin }; -/***/ }), -/* 131 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * BetterDiscord Client IPC Module - * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks - * All rights reserved. - * https://github.com/JsSucks - 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. -*/ - -const { ipcRenderer } = __webpack_require__(132); - -class BDIpc { - - static async send(channel, message) { - channel = channel.startsWith('bd-') ? channel : `bd-${channel}`; - const __eid = Date.now().toString(); - ipcRenderer.send(channel, Object.assign(message ? message : {}, { __eid })); - return new Promise((resolve, reject) => { - ipcRenderer.once(__eid, (event, arg) => resolve(arg)); - }); - } - -} - -module.exports = { BDIpc }; - -/***/ }), -/* 132 */ -/***/ (function(module, exports) { - -module.exports = window.require("electron"); - /***/ }), /* 133 */ /***/ (function(module, exports) { @@ -27607,7 +27726,7 @@ function isUndefined(arg) { const { Events } = __webpack_require__(2); const { Module } = __webpack_require__(1); -const { Global } = __webpack_require__(123); +const { Global } = __webpack_require__(124); const { Utils } = __webpack_require__(3); class SocketProxy extends Module { diff --git a/client/src/modules/pluginmanager.js b/client/src/modules/pluginmanager.js index 4afba4dd..f76550fa 100644 --- a/client/src/modules/pluginmanager.js +++ b/client/src/modules/pluginmanager.js @@ -9,6 +9,102 @@ */ const { Module } = require('./modulebase'); +const { BDIpc } = require('./bdipc'); +const fs = window.require('fs'); + +//TODO add these to actual utils +class Utils { + + static async tryParseJson(jsonString) { + try { + return JSON.parse(jsonString); + } catch (err) { + throw ({ + 'message': 'Failed to parse json', + err + }); + } + } + + static get timestamp() { + return 'Timestamp'; + } + +} + +class FileUtils { + + static async fileExists(path) { + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `No such file or directory: ${err.path}`, + err + }); + + if (!stats.isFile()) return reject({ + 'message': `Not a file: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async directoryExists(path) { + return new Promise(resolve => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `Directory does not exist: ${path}`, + err + }); + + if (!stats.isDirectory()) return reject({ + 'message': `Not a directory: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async readFile(path) { + try { + await this.fileExists(path); + } catch (err) { + throw (err); + } + + return new Promise(resolve => { + fs.readFile(path, 'utf-8', (err, data) => { + if (err) reject({ + 'message': `Could not read file: ${path}`, + err + }); + + resolve(data); + }); + }); + } + + static async readJsonFromFile(path) { + let readFile; + try { + readFile = await this.readFile(path); + } catch (err) { + throw (err); + } + + try { + const parsed = await Utils.tryParseJson(readFile); + return parsed; + } catch (err) { + throw (Object.assign(err, { path })); + } + } +} class PluginManager extends Module { @@ -16,7 +112,7 @@ class PluginManager extends Module { this.setState({ plugins: [] }); -//Testpush + tests(); } get plugins() { @@ -24,7 +120,7 @@ class PluginManager extends Module { } loadPlugin(plugin) { - const { plugins } = this; + const { plugins } = this.state; plugins.push(plugin); this.setState({ plugins @@ -39,8 +135,32 @@ class PluginManager extends Module { return this.plugins.find(plugin => plugin.id === id); } + + + async readConfig(path) { + path = `${path}/config.json`; + return FileUtils.readJsonFromFile(path); + } + } const _instance = new PluginManager(); +async function tests() { + + const config = await BDIpc.send('getConfig'); + const pluginPath = config.paths.find(path => 'plugins' in path).plugins; + console.log(`Plugin Path: ${pluginPath}`); + + const examplePluginPath = `${pluginPath}/Example`; + + //Test read config + try { + const readConfig = await _instance.readConfig(examplePluginPath); + console.log(readConfig); + } catch (err) { + console.log(err); + } +} + module.exports = { PluginManager: _instance } \ No newline at end of file diff --git a/tests/plugins/Example/config.json b/tests/plugins/Example/config.json new file mode 100644 index 00000000..49f23de7 --- /dev/null +++ b/tests/plugins/Example/config.json @@ -0,0 +1,6 @@ +{ + "name": "Example Plugin", + "authors": [ "Jiiks" ], + "version": 1.0, + "main": "index.js" +} \ No newline at end of file From 50f0d4f91f5076b078269f6de7dcd0dd7f98ed83 Mon Sep 17 00:00:00 2001 From: Jiiks Date: Tue, 16 Jan 2018 03:45:23 +0200 Subject: [PATCH 3/5] Plugin loading and tests --- client/dist/betterdiscord.client.js | 80 +++++++++++++++++++++++---- client/src/modules/pluginmanager.js | 85 +++++++++++++++++++++++------ tests/plugins/Example/index.js | 18 ++++++ 3 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 tests/plugins/Example/index.js diff --git a/client/dist/betterdiscord.client.js b/client/dist/betterdiscord.client.js index 1abbcb99..083291d1 100644 --- a/client/dist/betterdiscord.client.js +++ b/client/dist/betterdiscord.client.js @@ -27146,6 +27146,7 @@ webpackContext.id = 129; const { Module } = __webpack_require__(1); const { BDIpc } = __webpack_require__(123); const fs = window.require('fs'); +const path = window.require('path'); //TODO add these to actual utils class Utils { @@ -27241,9 +27242,24 @@ class FileUtils { } } +class Plugin { + + constructor(args) {} + + start() { + if (this.onStart) return this.onStart(); + } + + stop() { + if (this.onStop) return this.onStop(); + } + +} + class PluginManager extends Module { setInitialState() { + window.pm = this; this.setState({ plugins: [] }); @@ -27254,12 +27270,33 @@ class PluginManager extends Module { return this.state.plugins; } - loadPlugin(plugin) { + async loadPlugin(pluginPath) { const { plugins } = this.state; - plugins.push(plugin); - this.setState({ - plugins - }); + + try { + const loaded = plugins.find(plugin => plugin.pluginPath === pluginPath); + if (loaded) { + throw { 'message': 'Attempted to load an already loaded plugin' }; + } + + const readConfig = await this.readConfig(pluginPath); + + const mainPath = path.join(pluginPath, readConfig.main); + + const plugin = window.require(mainPath)(Plugin, {}, {}); + const instance = new plugin(); + + plugins.push(Object.assign({ + pluginPath, + instance + }, readConfig)); + + this.setState(plugins); + + return instance; + } catch (err) { + throw err; + } } getPluginByName(name) { @@ -27270,6 +27307,18 @@ class PluginManager extends Module { return this.plugins.find(plugin => plugin.id === id); } + stopPlugin(name) { + const plugin = this.getPluginByName(name); + if (plugin && plugin.instance) return plugin.instance.stop(); + return null; + } + + startPlugin(name) { + const plugin = this.getPluginByName(name); + if (plugin && plugin.instance) return plugin.instance.start(); + return null; + } + async readConfig(path) { path = `${path}/config.json`; return FileUtils.readJsonFromFile(path); @@ -27281,19 +27330,30 @@ const _instance = new PluginManager(); async function tests() { + const pluginName = 'Example'; const config = await BDIpc.send('getConfig'); const pluginPath = config.paths.find(path => 'plugins' in path).plugins; - console.log(`Plugin Path: ${pluginPath}`); + try { + const plugin = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + const plugin2 = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + window.pl = plugin; + } catch (err) { + console.log(`Failed to load plugin! ${err.message}`); + } - const examplePluginPath = `${pluginPath}/Example`; + // const config = await BDIpc.send('getConfig'); + // const pluginPath = config.paths.find(path => 'plugins' in path).plugins; + + // const examplePluginPath = path.join(pluginPath, pluginName); //Test read config - try { + /*try { const readConfig = await _instance.readConfig(examplePluginPath); console.log(readConfig); - } catch (err) { + window.testPlugin = window.require(path.join(examplePluginPath, readConfig.main)); + } catch (err) { console.log(err); - } + }*/ } module.exports = { PluginManager: _instance }; diff --git a/client/src/modules/pluginmanager.js b/client/src/modules/pluginmanager.js index f76550fa..fc66cac0 100644 --- a/client/src/modules/pluginmanager.js +++ b/client/src/modules/pluginmanager.js @@ -11,6 +11,7 @@ const { Module } = require('./modulebase'); const { BDIpc } = require('./bdipc'); const fs = window.require('fs'); +const path = window.require('path'); //TODO add these to actual utils class Utils { @@ -106,9 +107,28 @@ class FileUtils { } } +class Plugin { + + constructor(args) { + + } + + start() { + if (this.onStart) return this.onStart(); + return true; //Assume plugin started since it doesn't have onStart + } + + stop() { + if (this.onStop) return this.onStop(); + return true; //Assume plugin stopped since it doesn't have onStop + } + +} + class PluginManager extends Module { setInitialState() { + window.pm = this; this.setState({ plugins: [] }); @@ -119,23 +139,54 @@ class PluginManager extends Module { return this.state.plugins; } - loadPlugin(plugin) { + async loadPlugin(pluginPath) { const { plugins } = this.state; - plugins.push(plugin); - this.setState({ - plugins - }); + + try { + + const loaded = plugins.find(plugin => plugin.pluginPath === pluginPath); + if (loaded) { + throw { 'message': 'Attempted to load an already loaded plugin' }; + } + + const readConfig = await this.readConfig(pluginPath); + const mainPath = path.join(pluginPath, readConfig.main); + + const plugin = window.require(mainPath)(Plugin, {}, {}); + const instance = new plugin(); + + plugins.push(Object.assign({ + pluginPath, + instance + },readConfig)); + + this.setState(plugins); + + return instance; + } catch (err) { + throw err; + } } - getPluginByName(name) { - return this.plugins.find(plugin => plugin.name === name); + async reloadPlugin(pluginPath) { + //TODO Cleanup loaded plugin + return await this.loadPlugin(pluginPath); } - getPluginById(id) { - return this.plugins.find(plugin => plugin.id === id); + getPluginByName(name) { return this.plugins.find(plugin => plugin.name === name); } + getPluginById(id) { return this.plugins.find(plugin => plugin.id === id); } + + stopPlugin(name) { + const plugin = this.getPluginByName(name); + if (plugin && plugin.instance) return plugin.instance.stop(); + return true; //Return true anyways since plugin doesn't exist } - + startPlugin(name) { + const plugin = this.getPluginByName(name); + if (plugin && plugin.instance) return plugin.instance.start(); + return true; //Return true anyways since plugin doesn't exist + } async readConfig(path) { path = `${path}/config.json`; @@ -148,18 +199,16 @@ const _instance = new PluginManager(); async function tests() { + const pluginName = 'Example'; const config = await BDIpc.send('getConfig'); const pluginPath = config.paths.find(path => 'plugins' in path).plugins; - console.log(`Plugin Path: ${pluginPath}`); - - const examplePluginPath = `${pluginPath}/Example`; - - //Test read config try { - const readConfig = await _instance.readConfig(examplePluginPath); - console.log(readConfig); + //Load test plugin + const plugin = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + //Attempt to load the same plugin again + const plugin2 = await _instance.loadPlugin(path.join(pluginPath, pluginName)); } catch (err) { - console.log(err); + console.log(`Failed to load plugin! ${err.message}`); } } diff --git a/tests/plugins/Example/index.js b/tests/plugins/Example/index.js new file mode 100644 index 00000000..251f81a2 --- /dev/null +++ b/tests/plugins/Example/index.js @@ -0,0 +1,18 @@ +module.exports = (Plugin, Api, Vendor) => { + + const { $, moment } = Vendor; + const { Events } = Api; + + const test = 'Testing'; + + return class extends Plugin { + test() { + return test; + } + + onStart() { + console.log('On Start!'); + } + } + +} \ No newline at end of file From a1261cab0a7986125d8edda00ba4670ef3427795 Mon Sep 17 00:00:00 2001 From: Jiiks Date: Tue, 16 Jan 2018 03:55:14 +0200 Subject: [PATCH 4/5] Use Utils --- client/dist/betterdiscord.client.js | 296 +++++++++++++--------------- client/src/modules/pluginmanager.js | 94 +-------- 2 files changed, 143 insertions(+), 247 deletions(-) diff --git a/client/dist/betterdiscord.client.js b/client/dist/betterdiscord.client.js index 083291d1..360db7eb 100644 --- a/client/dist/betterdiscord.client.js +++ b/client/dist/betterdiscord.client.js @@ -4666,41 +4666,6 @@ module.exports = { Module }; /* 2 */ /***/ (function(module, exports, __webpack_require__) { -/** - * BetterDiscord Events - * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks - * All rights reserved. - * https://github.com/JsSucks - 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. -*/ - -const { EventEmitter } = __webpack_require__(134); -const emitter = new EventEmitter(); - -class Events { - - static on(eventName, callBack) { - emitter.on(eventName, callBack); - } - - static off(eventName, callBack) { - emitter.removeListener(eventName, callBack); - } - - static emit(...args) { - emitter.emit(...args); - } - -} - -module.exports = { Events }; - -/***/ }), -/* 3 */ -/***/ (function(module, exports, __webpack_require__) { - /** * BetterDiscord Client Utils Module * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks @@ -4713,6 +4678,8 @@ module.exports = { Events }; const { Module } = __webpack_require__(1); const moment = __webpack_require__(0); +const fs = window.require('fs'); +const path = window.require('path'); const logs = []; @@ -4752,9 +4719,129 @@ class Utils { }; } + static async tryParseJson(jsonString) { + try { + return JSON.parse(jsonString); + } catch (err) { + throw { + 'message': 'Failed to parse json', + err + }; + } + } + } -module.exports = { Logger, Utils }; +class FileUtils { + + static async fileExists(path) { + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `No such file or directory: ${err.path}`, + err + }); + + if (!stats.isFile()) return reject({ + 'message': `Not a file: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async directoryExists(path) { + return new Promise(resolve => { + fs.stat(path, (err, stats) => { + if (err) return reject({ + 'message': `Directory does not exist: ${path}`, + err + }); + + if (!stats.isDirectory()) return reject({ + 'message': `Not a directory: ${path}`, + stats + }); + + resolve(); + }); + }); + } + + static async readFile(path) { + try { + await this.fileExists(path); + } catch (err) { + throw err; + } + + return new Promise(resolve => { + fs.readFile(path, 'utf-8', (err, data) => { + if (err) reject({ + 'message': `Could not read file: ${path}`, + err + }); + + resolve(data); + }); + }); + } + + static async readJsonFromFile(path) { + let readFile; + try { + readFile = await this.readFile(path); + } catch (err) { + throw err; + } + + try { + const parsed = await Utils.tryParseJson(readFile); + return parsed; + } catch (err) { + throw Object.assign(err, { path }); + } + } +} + +module.exports = { Logger, Utils, FileUtils }; + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * BetterDiscord Events + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://github.com/JsSucks - 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. +*/ + +const { EventEmitter } = __webpack_require__(134); +const emitter = new EventEmitter(); + +class Events { + + static on(eventName, callBack) { + emitter.on(eventName, callBack); + } + + static off(eventName, callBack) { + emitter.removeListener(eventName, callBack); + } + + static emit(...args) { + emitter.emit(...args); + } + +} + +module.exports = { Events }; /***/ }), /* 4 */ @@ -16463,7 +16550,7 @@ module.exports = { BDIpc }; */ const { Module } = __webpack_require__(1); -const { Events } = __webpack_require__(2); +const { Events } = __webpack_require__(3); class Global extends Module { @@ -26807,10 +26894,11 @@ if (window.BetterDiscord) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils__ = __webpack_require__(3); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils__ = __webpack_require__(2); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__utils__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__utils__, "Logger")) __webpack_require__.d(__webpack_exports__, "Logger", function() { return __WEBPACK_IMPORTED_MODULE_0__utils__["Logger"]; }); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__utils__, "Utils")) __webpack_require__.d(__webpack_exports__, "Utils", function() { return __WEBPACK_IMPORTED_MODULE_0__utils__["Utils"]; }); +/* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__utils__, "FileUtils")) __webpack_require__.d(__webpack_exports__, "FileUtils", function() { return __WEBPACK_IMPORTED_MODULE_0__utils__["FileUtils"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__pluginmanager__ = __webpack_require__(130); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__pluginmanager___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__pluginmanager__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_1__pluginmanager__, "PluginManager")) __webpack_require__.d(__webpack_exports__, "PluginManager", function() { return __WEBPACK_IMPORTED_MODULE_1__pluginmanager__["PluginManager"]; }); @@ -26826,7 +26914,7 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__global__ = __webpack_require__(124); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__global___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__global__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_5__global__, "Global")) __webpack_require__.d(__webpack_exports__, "Global", function() { return __WEBPACK_IMPORTED_MODULE_5__global__["Global"]; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__events__ = __webpack_require__(2); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__events__ = __webpack_require__(3); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__events___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__events__); /* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_6__events__, "Events")) __webpack_require__.d(__webpack_exports__, "Events", function() { return __WEBPACK_IMPORTED_MODULE_6__events__["Events"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__discordsocket__ = __webpack_require__(135); @@ -27145,113 +27233,22 @@ webpackContext.id = 129; const { Module } = __webpack_require__(1); const { BDIpc } = __webpack_require__(123); +const { Utils, FileUtils } = __webpack_require__(2); const fs = window.require('fs'); const path = window.require('path'); -//TODO add these to actual utils -class Utils { - - static async tryParseJson(jsonString) { - try { - return JSON.parse(jsonString); - } catch (err) { - throw { - 'message': 'Failed to parse json', - err - }; - } - } - - static get timestamp() { - return 'Timestamp'; - } - -} - -class FileUtils { - - static async fileExists(path) { - return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { - if (err) return reject({ - 'message': `No such file or directory: ${err.path}`, - err - }); - - if (!stats.isFile()) return reject({ - 'message': `Not a file: ${path}`, - stats - }); - - resolve(); - }); - }); - } - - static async directoryExists(path) { - return new Promise(resolve => { - fs.stat(path, (err, stats) => { - if (err) return reject({ - 'message': `Directory does not exist: ${path}`, - err - }); - - if (!stats.isDirectory()) return reject({ - 'message': `Not a directory: ${path}`, - stats - }); - - resolve(); - }); - }); - } - - static async readFile(path) { - try { - await this.fileExists(path); - } catch (err) { - throw err; - } - - return new Promise(resolve => { - fs.readFile(path, 'utf-8', (err, data) => { - if (err) reject({ - 'message': `Could not read file: ${path}`, - err - }); - - resolve(data); - }); - }); - } - - static async readJsonFromFile(path) { - let readFile; - try { - readFile = await this.readFile(path); - } catch (err) { - throw err; - } - - try { - const parsed = await Utils.tryParseJson(readFile); - return parsed; - } catch (err) { - throw Object.assign(err, { path }); - } - } -} - class Plugin { constructor(args) {} start() { if (this.onStart) return this.onStart(); + return true; //Assume plugin started since it doesn't have onStart } stop() { if (this.onStop) return this.onStop(); + return true; //Assume plugin stopped since it doesn't have onStop } } @@ -27274,13 +27271,13 @@ class PluginManager extends Module { const { plugins } = this.state; try { + const loaded = plugins.find(plugin => plugin.pluginPath === pluginPath); if (loaded) { throw { 'message': 'Attempted to load an already loaded plugin' }; } const readConfig = await this.readConfig(pluginPath); - const mainPath = path.join(pluginPath, readConfig.main); const plugin = window.require(mainPath)(Plugin, {}, {}); @@ -27299,10 +27296,14 @@ class PluginManager extends Module { } } + async reloadPlugin(pluginPath) { + //TODO Cleanup loaded plugin + return await this.loadPlugin(pluginPath); + } + getPluginByName(name) { return this.plugins.find(plugin => plugin.name === name); } - getPluginById(id) { return this.plugins.find(plugin => plugin.id === id); } @@ -27310,13 +27311,13 @@ class PluginManager extends Module { stopPlugin(name) { const plugin = this.getPluginByName(name); if (plugin && plugin.instance) return plugin.instance.stop(); - return null; + return true; //Return true anyways since plugin doesn't exist } startPlugin(name) { const plugin = this.getPluginByName(name); if (plugin && plugin.instance) return plugin.instance.start(); - return null; + return true; //Return true anyways since plugin doesn't exist } async readConfig(path) { @@ -27334,26 +27335,13 @@ async function tests() { const config = await BDIpc.send('getConfig'); const pluginPath = config.paths.find(path => 'plugins' in path).plugins; try { + //Load test plugin const plugin = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + //Attempt to load the same plugin again const plugin2 = await _instance.loadPlugin(path.join(pluginPath, pluginName)); - window.pl = plugin; } catch (err) { console.log(`Failed to load plugin! ${err.message}`); } - - // const config = await BDIpc.send('getConfig'); - // const pluginPath = config.paths.find(path => 'plugins' in path).plugins; - - // const examplePluginPath = path.join(pluginPath, pluginName); - - //Test read config - /*try { - const readConfig = await _instance.readConfig(examplePluginPath); - console.log(readConfig); - window.testPlugin = window.require(path.join(examplePluginPath, readConfig.main)); - } catch (err) { - console.log(err); - }*/ } module.exports = { PluginManager: _instance }; @@ -27784,10 +27772,10 @@ function isUndefined(arg) { * LICENSE file in the root directory of this source tree. */ -const { Events } = __webpack_require__(2); +const { Events } = __webpack_require__(3); const { Module } = __webpack_require__(1); const { Global } = __webpack_require__(124); -const { Utils } = __webpack_require__(3); +const { Utils } = __webpack_require__(2); class SocketProxy extends Module { diff --git a/client/src/modules/pluginmanager.js b/client/src/modules/pluginmanager.js index fc66cac0..105141ed 100644 --- a/client/src/modules/pluginmanager.js +++ b/client/src/modules/pluginmanager.js @@ -10,102 +10,10 @@ const { Module } = require('./modulebase'); const { BDIpc } = require('./bdipc'); +const { Utils, FileUtils } = require('./utils'); const fs = window.require('fs'); const path = window.require('path'); -//TODO add these to actual utils -class Utils { - - static async tryParseJson(jsonString) { - try { - return JSON.parse(jsonString); - } catch (err) { - throw ({ - 'message': 'Failed to parse json', - err - }); - } - } - - static get timestamp() { - return 'Timestamp'; - } - -} - -class FileUtils { - - static async fileExists(path) { - return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { - if (err) return reject({ - 'message': `No such file or directory: ${err.path}`, - err - }); - - if (!stats.isFile()) return reject({ - 'message': `Not a file: ${path}`, - stats - }); - - resolve(); - }); - }); - } - - static async directoryExists(path) { - return new Promise(resolve => { - fs.stat(path, (err, stats) => { - if (err) return reject({ - 'message': `Directory does not exist: ${path}`, - err - }); - - if (!stats.isDirectory()) return reject({ - 'message': `Not a directory: ${path}`, - stats - }); - - resolve(); - }); - }); - } - - static async readFile(path) { - try { - await this.fileExists(path); - } catch (err) { - throw (err); - } - - return new Promise(resolve => { - fs.readFile(path, 'utf-8', (err, data) => { - if (err) reject({ - 'message': `Could not read file: ${path}`, - err - }); - - resolve(data); - }); - }); - } - - static async readJsonFromFile(path) { - let readFile; - try { - readFile = await this.readFile(path); - } catch (err) { - throw (err); - } - - try { - const parsed = await Utils.tryParseJson(readFile); - return parsed; - } catch (err) { - throw (Object.assign(err, { path })); - } - } -} class Plugin { From 39fbae166f7b4034bfc6c13f349992f4e16049ab Mon Sep 17 00:00:00 2001 From: Jiiks Date: Tue, 16 Jan 2018 06:38:28 +0200 Subject: [PATCH 5/5] Load plugins by directory name not full path --- client/dist/betterdiscord.client.js | 11 ++++++++++- client/src/modules/pluginmanager.js | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/client/dist/betterdiscord.client.js b/client/dist/betterdiscord.client.js index 360db7eb..7cc400db 100644 --- a/client/dist/betterdiscord.client.js +++ b/client/dist/betterdiscord.client.js @@ -27260,15 +27260,24 @@ class PluginManager extends Module { this.setState({ plugins: [] }); - tests(); + // tests(); } get plugins() { return this.state.plugins; } + async pluginsPath() { + //TODO Get this from config module + const config = await BDIpc.send('getConfig'); + return config.paths.find(path => 'plugins' in path).plugins; + } + async loadPlugin(pluginPath) { const { plugins } = this.state; + const pluginsPath = await this.pluginsPath(); + + pluginPath = path.join(pluginsPath, pluginPath); try { diff --git a/client/src/modules/pluginmanager.js b/client/src/modules/pluginmanager.js index 105141ed..18a8a131 100644 --- a/client/src/modules/pluginmanager.js +++ b/client/src/modules/pluginmanager.js @@ -47,10 +47,18 @@ class PluginManager extends Module { return this.state.plugins; } + async pluginsPath() { + //TODO Get this from config module + const config = await BDIpc.send('getConfig'); + return config.paths.find(path => 'plugins' in path).plugins; + } + async loadPlugin(pluginPath) { const { plugins } = this.state; try { + const pluginsPath = await this.pluginsPath(); + pluginPath = path.join(pluginsPath, pluginPath); const loaded = plugins.find(plugin => plugin.pluginPath === pluginPath); if (loaded) { @@ -66,10 +74,12 @@ class PluginManager extends Module { plugins.push(Object.assign({ pluginPath, instance - },readConfig)); + }, readConfig)); this.setState(plugins); + //TODO Read plugin user config and call onStart if enabled + return instance; } catch (err) { throw err; @@ -108,13 +118,12 @@ const _instance = new PluginManager(); async function tests() { const pluginName = 'Example'; - const config = await BDIpc.send('getConfig'); - const pluginPath = config.paths.find(path => 'plugins' in path).plugins; + try { //Load test plugin - const plugin = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + const plugin = await _instance.loadPlugin(pluginName); //Attempt to load the same plugin again - const plugin2 = await _instance.loadPlugin(path.join(pluginPath, pluginName)); + const plugin2 = await _instance.loadPlugin(pluginName); } catch (err) { console.log(`Failed to load plugin! ${err.message}`); }