Patcher
This commit is contained in:
parent
5083a80ba2
commit
6a854ab070
|
@ -16,6 +16,9 @@ export default new class extends Module {
|
|||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
initg() {
|
||||
this.first();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import WebpackModules from './webpackmodules';
|
||||
|
||||
export default class Patcher {
|
||||
static get patches() { return this._patches || (this._patches = {}) }
|
||||
static resolveModule(mn) {
|
||||
if (mn instanceof Function || (mn instanceof Object && !(mn instanceof Array))) return mn;
|
||||
if ('string' === typeof mn) return WebpackModules.getModuleByName(mn);
|
||||
if (mn instanceof Array) return WebpackModules.getModuleByProps(mn);
|
||||
return null;
|
||||
}
|
||||
static overrideFn(patch) {
|
||||
return function () {
|
||||
for (const s of patch.supers) {
|
||||
try {
|
||||
s.fn.apply(this, arguments);
|
||||
} catch (err) { }
|
||||
}
|
||||
const retVal = patch.ofn.apply(this, arguments);
|
||||
for (const s of patch.slaves) {
|
||||
try {
|
||||
s.fn.apply(this, [arguments, { patch, retVal }]);
|
||||
} catch (err) { }
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
static rePatch(po) {
|
||||
po.patch = po.module[po.fnn] = this.overrideFn(po);
|
||||
}
|
||||
|
||||
static pushPatch(id, module, fnn) {
|
||||
const patch = {
|
||||
module,
|
||||
fnn,
|
||||
ofn: module[fnn],
|
||||
revert: () => {
|
||||
patch.module[patch.fnn] = patch.ofn;
|
||||
patch.patch = null;
|
||||
patch.slaves = patch.supers = [];
|
||||
},
|
||||
supers: [],
|
||||
slaves: [],
|
||||
patch: null
|
||||
};
|
||||
patch.patch = module[fnn] = this.overrideFn(patch);
|
||||
return this.patches[id] = patch;
|
||||
}
|
||||
|
||||
static superpatch(mn, fnn, cb, dn) {
|
||||
const module = this.resolveModule(mn);
|
||||
if (!module || !module[fnn] || !(module[fnn] instanceof Function)) return null;
|
||||
const displayName = 'string' === typeof mn ? mn : dn || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
|
||||
const patchId = `${displayName}:${fnn}`;
|
||||
const patchObject = this.patches[patchId] || this.pushPatch(patchId, module, fnn);
|
||||
if (!patchObject.patch) this.rePatch(patchObject);
|
||||
const id = patchObject.supers.length + 1;
|
||||
const patch = {
|
||||
id,
|
||||
fn: cb,
|
||||
unpatch: () => patchObject.supers.splice(patchObject.supers.findIndex(slave => slave.id === id), 1)
|
||||
};
|
||||
patchObject.supers.push(patch);
|
||||
return patch;
|
||||
}
|
||||
|
||||
static slavepatch(mn, fnn, cb, dn) {
|
||||
const module = this.resolveModule(mn);
|
||||
if (!module || !module[fnn] || !(module[fnn] instanceof Function)) return null;
|
||||
const displayName = 'string' === typeof mn ? mn : dn || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
|
||||
const patchId = `${displayName}:${fnn}`;
|
||||
const patchObject = this.patches[patchId] || this.pushPatch(patchId, module, fnn);
|
||||
if (!patchObject.patch) this.rePatch(patchObject);
|
||||
const id = patchObject.slaves.length + 1;
|
||||
const patch = {
|
||||
id,
|
||||
fn: cb,
|
||||
unpatch: () => patchObject.slaves.splice(patchObject.slaves.findIndex(slave => slave.id === id), 1)
|
||||
};
|
||||
patchObject.slaves.push(patch);
|
||||
return patch;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
import Patcher from './patcher';
|
||||
|
||||
class Helpers {
|
||||
static get plannedActions() {
|
||||
return this._plannedActions || (this._plannedActions = new Map());
|
||||
}
|
||||
static recursiveArray(parent, key, count = 1) {
|
||||
let index = 0;
|
||||
function* innerCall(parent, key) {
|
||||
const item = parent[key];
|
||||
if (item instanceof Array) {
|
||||
for (const subKey of item.keys()) {
|
||||
yield* innerCall(item, subKey)
|
||||
}
|
||||
return;
|
||||
}
|
||||
yield { item, parent, key, index: index++, count };
|
||||
}
|
||||
|
||||
return innerCall(parent, key);
|
||||
}
|
||||
static recursiveArrayCount(parent, key) {
|
||||
let count = 0;
|
||||
for (let { } of this.recursiveArray(parent, key))
|
||||
++count;
|
||||
return this.recursiveArray(parent, key, count);
|
||||
}
|
||||
static get recursiveChildren() {
|
||||
return function*(parent, key, index = 0, count = 1) {
|
||||
const item = parent[key];
|
||||
yield { item, parent, key, index, count };
|
||||
if (item && item.props && item.props.children) {
|
||||
for (let { parent, key, index, count } of this.recursiveArrayCount(item.props, 'children')) {
|
||||
yield* this.recursiveChildren(parent, key, index, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static returnFirst(iterator, process) {
|
||||
for (let child of iterator) {
|
||||
const retVal = process(child);
|
||||
if (retVal !== undefined) return retVal;
|
||||
}
|
||||
}
|
||||
static getFirstChild(rootParent, rootKey, selector) {
|
||||
const getDirectChild = (item, selector) => {
|
||||
if (item && item.props && item.props.children) {
|
||||
return this.returnFirst(this.recursiveArrayCount(item.props, 'children'), checkFilter.bind(null, selector));
|
||||
}
|
||||
};
|
||||
const checkFilter = (selector, { item, parent, key, count, index }) => {
|
||||
let match = true;
|
||||
if (match && selector.type)
|
||||
match = item && selector.type === item.type;
|
||||
if (match && selector.tag)
|
||||
match = item && typeof item.type === 'string' && selector.tag === item.type;
|
||||
if (match && selector.className) {
|
||||
match = item && item.props && typeof item.props.className === 'string';
|
||||
if (match) {
|
||||
const classes = item.props.className.split(' ');
|
||||
if (selector.className === true)
|
||||
match = !!classes[0];
|
||||
else if (typeof selector.className === 'string')
|
||||
match = classes.includes(selector.className);
|
||||
else if (selector.className instanceof RegExp)
|
||||
match = !!classes.find(cls => selector.className.test(cls));
|
||||
else match = false;
|
||||
}
|
||||
}
|
||||
if (match && selector.text) {
|
||||
if (selector.text === true)
|
||||
match = typeof item === 'string';
|
||||
else if (typeof selector.text === 'string')
|
||||
match = item === selector.text;
|
||||
else if (selector.text instanceof RegExp)
|
||||
match = typeof item === 'string' && selector.text.test(item);
|
||||
else match = false;
|
||||
}
|
||||
if (match && selector.nthChild)
|
||||
match = index === (selector.nthChild < 0 ? count + selector.nthChild : selector.nthChild);
|
||||
if (match && selector.hasChild)
|
||||
match = getDirectChild(item, selector.hasChild);
|
||||
if (match && selector.hasSuccessor)
|
||||
match = item && !!this.getFirstChild(parent, key, selector.hasSuccessor).item;
|
||||
if (match && selector.eq) {
|
||||
--selector.eq;
|
||||
return;
|
||||
}
|
||||
if (match) {
|
||||
if (selector.child) {
|
||||
return getDirectChild(item, selector.child);
|
||||
}
|
||||
else if (selector.successor) {
|
||||
return this.getFirstChild(parent, key, selector.successor);
|
||||
}
|
||||
else {
|
||||
return { item, parent, key };
|
||||
}
|
||||
}
|
||||
};
|
||||
return this.returnFirst(this.recursiveChildren(rootParent, rootKey), checkFilter.bind(null, selector)) || {};
|
||||
}
|
||||
static parseSelector(selector) {
|
||||
if (selector.startsWith('.')) return { className: selector.substr(1) }
|
||||
if (selector.startsWith('#')) return { id: selector.substr(1) }
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
class ReactComponent {
|
||||
constructor(id, component, retVal) {
|
||||
this._id = id;
|
||||
this._component = component;
|
||||
this._retVal = retVal;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get component() {
|
||||
return this._component;
|
||||
}
|
||||
|
||||
get retVal() {
|
||||
return this._retVal;
|
||||
}
|
||||
|
||||
unpatchRender() {
|
||||
|
||||
}
|
||||
|
||||
patchRender(actions, updateOthers) {
|
||||
const self = this;
|
||||
if (!(actions instanceof Array)) actions = [actions];
|
||||
Patcher.slavepatch(this.component.prototype, 'render', function(args, obj) {
|
||||
for (const action of actions) {
|
||||
let { selector, method, fn } = action;
|
||||
if ('string' === typeof selector) selector = Helpers.parseSelector(selector);
|
||||
const { item, parent, key } = Helpers.getFirstChild(obj, 'retVal', selector);
|
||||
if (!item) continue;
|
||||
const content = fn.apply(this, [item]);
|
||||
switch (method) {
|
||||
case 'replace':
|
||||
parent[key] = content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (updateOthers) self.forceUpdateOthers();
|
||||
});
|
||||
}
|
||||
|
||||
forceUpdateOthers() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default class ReactComponents {
|
||||
static get components() { return this._components || (this._components = []) }
|
||||
static get listeners() { return this._listeners || (this._listeners = []) }
|
||||
|
||||
static push(component, retVal) {
|
||||
if (!(component instanceof Function)) return null;
|
||||
const { displayName } = component;
|
||||
if (!displayName) return null;
|
||||
const have = this.components.find(comp => comp.id === displayName);
|
||||
if (have) return component;
|
||||
const c = new ReactComponent(displayName, component, retVal);
|
||||
this.components.push(c);
|
||||
const listener = this.listeners.find(listener => listener.id === displayName);
|
||||
if (!listener) return c;
|
||||
for (const l of listener.listeners) {
|
||||
l(c);
|
||||
}
|
||||
this.listeners.splice(this.listeners.findIndex(listener => listener.id === displayName), 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
static async getComponent(name) {
|
||||
const have = this.components.find(c => c.id === name);
|
||||
if (have) return have;
|
||||
const listener = this.listeners.find(l => l.id === name);
|
||||
if (!listener) this.listeners.push({
|
||||
id: name,
|
||||
listeners: []
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
this.listeners.find(l => l.id === name).listeners.push(c => resolve(c));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Events, WebpackModules, EventListener } from 'modules';
|
||||
import { Events, WebpackModules, EventListener, ReactComponents, Renderer } from 'modules';
|
||||
import Reflection from './reflection';
|
||||
import DOM from './dom';
|
||||
import VueInjector from './vueinjector';
|
||||
|
@ -41,6 +41,10 @@ class TempApi {
|
|||
|
||||
export default class extends EventListener {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
bindings() {
|
||||
this.manipAll = this.manipAll.bind(this);
|
||||
this.markupInjector = this.markupInjector.bind(this);
|
||||
|
@ -50,6 +54,8 @@ export default class extends EventListener {
|
|||
}
|
||||
|
||||
get eventBindings() {
|
||||
return [{ id: 'gkh:keyup', callback: this.injectAutocomplete }];
|
||||
/*
|
||||
return [
|
||||
{ id: 'server-switch', callback: this.manipAll },
|
||||
{ id: 'channel-switch', callback: this.manipAll },
|
||||
|
@ -57,6 +63,7 @@ export default class extends EventListener {
|
|||
{ id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
||||
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
|
||||
];
|
||||
*/
|
||||
}
|
||||
|
||||
manipAll() {
|
||||
|
|
|
@ -57,10 +57,10 @@ export default class {
|
|||
if (!this.profilePopupModule) return;
|
||||
clearInterval(defer);
|
||||
|
||||
Utils.monkeyPatch(this.profilePopupModule, 'open', 'after', (data, userid) => Events.emit('ui-event', {
|
||||
/*Utils.monkeyPatch(this.profilePopupModule, 'open', 'after', (data, userid) => Events.emit('ui-event', {
|
||||
event: 'profile-popup-open',
|
||||
data: { userid }
|
||||
}));
|
||||
}));*/
|
||||
}, 100);
|
||||
|
||||
const ehookInterval = setInterval(() => {
|
||||
|
|
|
@ -150,7 +150,7 @@ class BetterDiscord {
|
|||
//this.windowUtils.webContents.on('did-finish-load', e => this.injectScripts(true));
|
||||
|
||||
this.windowUtils.events('did-get-response-details', () => this.ignite(this.windowUtils.window));
|
||||
this.windowUtils.events('did-finish-load', e => this.injectScripts(true));
|
||||
this.windowUtils.events('did-get-response-details', e => this.injectScripts(true));
|
||||
|
||||
this.windowUtils.events('did-navigate-in-page', (event, url, isMainFrame) => {
|
||||
this.windowUtils.send('did-navigate-in-page', { event, url, isMainFrame });
|
||||
|
|
Loading…
Reference in New Issue