diff --git a/client/src/modules/patcher.js b/client/src/modules/patcher.js index 55e8ee80..ac4d19b7 100644 --- a/client/src/modules/patcher.js +++ b/client/src/modules/patcher.js @@ -5,14 +5,16 @@ * 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. + * LICENSE file in the root directory of this source tree. */ import { WebpackModules } from './webpackmodules'; import { ClientLogger as Logger, Utils } from 'common'; export class Patcher { + static get patches() { return this._patches || (this._patches = {}) } + static getPatchesByCaller(id) { const patches = []; for (const patch in this.patches) { @@ -22,16 +24,21 @@ export class Patcher { } return patches; } + static unpatchAll(patches) { + if (typeof patches === 'string') + patches = this.getPatchesByCaller(patches); + for (const patch of patches) { for (const child of patch.children) { child.unpatch(); } } } + static resolveModule(module) { if (module instanceof Function || (module instanceof Object && !(module instanceof Array))) return module; - if ('string' === typeof module) return WebpackModules.getModuleByName(module); + if (typeof module === 'string') return WebpackModules.getModuleByName(module); if (module instanceof Array) return WebpackModules.getModuleByProps(module); return null; } @@ -99,10 +106,12 @@ export class Patcher { static before() { return this.pushChildPatch(...arguments, 'before') } static after() { return this.pushChildPatch(...arguments, 'after') } static instead() { return this.pushChildPatch(...arguments, 'instead') } + static pushChildPatch(caller, unresolvedModule, functionName, callback, displayName, type = 'after') { const module = this.resolveModule(unresolvedModule); if (!module || !module[functionName] || !(module[functionName] instanceof Function)) return null; - displayName = 'string' === typeof unresolvedModule ? unresolvedModule : displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name; + displayName = typeof unresolvedModule === 'string' ? unresolvedModule : + displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name; const patchId = `${displayName}:${functionName}:${caller}`; const patch = this.patches[patchId] || this.pushPatch(caller, patchId, module, functionName); diff --git a/client/src/modules/plugin.js b/client/src/modules/plugin.js index dcf26bef..039ba05e 100644 --- a/client/src/modules/plugin.js +++ b/client/src/modules/plugin.js @@ -18,8 +18,12 @@ export default class Plugin extends Content { get start() { return this.enable } get stop() { return this.disable } + reload() { + return PluginManager.reloadPlugin(this); + } + unload() { - PluginManager.unloadPlugin(this); + return PluginManager.unloadPlugin(this); } } diff --git a/client/src/modules/pluginapi.js b/client/src/modules/pluginapi.js index 670a407c..38284465 100644 --- a/client/src/modules/pluginapi.js +++ b/client/src/modules/pluginapi.js @@ -388,10 +388,39 @@ export default class PluginApi { get ReactComponents() { return ReactComponents; } + get Reflection() { return Reflection; } - get MonkeyPatch() { + + /** + * Patcher + */ + + get patches() { + return Patcher.getPatchesByCaller(this.plugin.id); + } + patchBefore(...args) { return this.pushChildPatch(...args, 'before') } + patchAfter(...args) { return this.pushChildPatch(...args, 'after') } + patchInstead(...args) { return this.pushChildPatch(...args, 'instead') } + pushChildPatch(...args) { + return Patcher.pushChildPatch(this.plugin.id, ...args); + } + unpatchAll(patches) { + return Patcher.unpatchAll(patches || this.plugin.id); + } + get Patcher() { + return Object.defineProperty({ + before: this.patchBefore.bind(this), + after: this.patchAfter.bind(this), + instead: this.patchInstead.bind(this), + pushChildPatch: this.pushChildPatch.bind(this), + unpatchAll: this.unpatchAll.bind(this), + }, 'patches', { + get: () => this.patches + }); + } + get monkeyPatch() { return module => MonkeyPatch(this.plugin.id, module); } diff --git a/client/src/ui/profilebadges.js b/client/src/ui/profilebadges.js index 9b43bb9b..008bb448 100644 --- a/client/src/ui/profilebadges.js +++ b/client/src/ui/profilebadges.js @@ -59,7 +59,9 @@ export default class extends EventListener { const c = contributors.find(c => c.id === msgGroup.dataset.authorId); if (!c) return; const root = document.createElement('span'); - const wrapperParent = msgGroup.querySelector('.username-wrapper').parentElement; + const usernameWrapper = msgGroup.querySelector('.username-wrapper'); + if (!usernameWrapper) return; + const wrapperParent = usernameWrapper.parentElement; if (!wrapperParent || wrapperParent.children.length < 2) return; wrapperParent.insertBefore(root, wrapperParent.children[1]); const { developer, contributor, webdev } = c; diff --git a/tests/ext/plugins/Patcher Test/index.js b/tests/ext/plugins/Patcher Test/index.js index 8c7eefaf..7f280b5d 100644 --- a/tests/ext/plugins/Patcher Test/index.js +++ b/tests/ext/plugins/Patcher Test/index.js @@ -1,26 +1,25 @@ module.exports = (Plugin, Api, Vendor) => { - const { ReactComponents } = Api; + const { Logger, ReactComponents, Patcher, monkeyPatch } = Api; return class extends Plugin { - test() { - - } onStart() { this.patchMessage(); return true; } + async patchMessage() { const Message = await ReactComponents.getComponent('Message'); - this.unpatchTest = Api.MonkeyPatch(Message.component.prototype).after('render', () => { - console.log('MESSAGE RENDER!'); + monkeyPatch(Message.component.prototype).after('render', e => { + Logger.log('MESSAGE RENDER!', e); }); } onStop() { - this.unpatchTest(); // The automatic unpatcher is not there yet + // The automatic unpatcher is not there yet + Patcher.unpatchAll(); return true; } } -} +};