diff --git a/src/api/commands.js b/src/api/commands.js index 3d98808..666032c 100644 --- a/src/api/commands.js +++ b/src/api/commands.js @@ -1,35 +1,43 @@ // @flow + +const commandsSym = Symbol("__commands"); + function init(obj: Object) { - obj.demon.__commands = {} - obj.demon.patcher.after("sendMessage", obj.demon.webpack.findByProps("sendMessage"), - (args, otherRes) => { - let res; - for (const key of Reflect.ownKeys(obj.demon.__commands)) { - let command = obj.demon.__commands[key] - if (args[1].content.split(" ")[0] === ">" + command.name) { - res = command.callback(args) - break - } - } - if (res !== undefined) args[1].content = res - return args - } - ) + obj.demon[commandsSym] = {}; + obj.demon.patcher.after( + "sendMessage", + obj.demon.webpack.findByProps("sendMessage"), + (args, otherRes) => { + let res; + for (const key of Reflect.ownKeys(obj.demon[commandsSym])) { + let command = obj.demon[commandsSym][key]; + if (args[1].content.split(" ")[0] === ">" + command.name) { + res = command.callback(args); + break; + } + } + if (res !== undefined) args[1].content = res; + return args; + } + ); } // command = { // name: "name", // callback: (args)=>"Hello, world!" // } -function add(command: {name: string, callback: (args: Array) => string}): ()=>void { - let sym = Symbol(command.name); - window.demon.__commands[sym] = command; - return () => { - delete window.demon.__commands[sym]; - } +function add(command: { + name: string, + callback: (args: Array) => string, +}): () => void { + let sym = Symbol(command.name); + window.demon[commandsSym][sym] = command; + return () => { + delete window.demon[commandsSym][sym]; + }; } export default { - add: add, - init: init -} + add: add, + init: init, +}; diff --git a/src/api/plugins.js b/src/api/plugins.js index 5e1628c..1528b38 100644 --- a/src/api/plugins.js +++ b/src/api/plugins.js @@ -2,41 +2,44 @@ import { get, set } from 'idb-keyval'; import logger from './utils/logger.js' +const pluginsSym = Symbol("__plugins"); + async function init(obj: Object): Promise { - //TODO: check for enabled plugins in the idb, start them - let globalSettings = await get("demoncord") - if (globalSettings === undefined) { - await set("demoncord", {plugin: {}}) - globalSettings = await get("demoncord") - } - obj.demon.__plugins = {} - for (let plug: Object in globalSettings.plugin) { - plug = globalSettings.plugin[plug] - if (plug.enabled) { - startPlugin(plug.metadata.name) - } - } - return true + //TODO: check for enabled plugins in the idb, start them + let globalSettings = await get("demoncord"); + if (globalSettings === undefined) { + await set("demoncord", { plugin: {} }); + globalSettings = await get("demoncord"); + } + obj.demon[pluginsSym] = {}; + for (let plug: Object in globalSettings.plugin) { + plug = globalSettings.plugin[plug]; + if (plug.enabled) { + startPlugin(plug.metadata.name); + } + } + return true; } async function addPlugin(iife: string, metadata: Object): Promise { - // expected metadata: {name: "name", desc: "description", author: "author"} - const obj: Object = { // whether the plugin is started or stopped isn't going to be stored in the iDB, so it can be accessed more easily by all components - metadata: metadata, - iife: iife, - enabled: false // should plugins be enabled by default? not sure - } - const globalSettings: Object = await get("demoncord"); - /*try { + // expected metadata: {name: "name", desc: "description", author: "author"} + const obj: Object = { + // whether the plugin is started or stopped isn't going to be stored in the iDB, so it can be accessed more easily by all components + metadata: metadata, + iife: iife, + enabled: false, // should plugins be enabled by default? not sure + }; + const globalSettings: Object = await get("demoncord"); + /*try { if (globalSettings["plugin"][metadata.name] !== undefined) { - log("Cannot add plugin that already exists!", "error", "Plugins") + console.error("[Demoncord] Cannot add plugin that already exists!") return false } } catch (error) {*/ - globalSettings["plugin"][metadata.name] = obj - //} //Disabling checking for previous plugins for now as it is breaking literally fucking everything - await set("demoncord", globalSettings) - return true + globalSettings["plugin"][metadata.name] = obj; + //} //Disabling checking for previous plugins for now as it is breaking literally fucking everything + await set("demoncord", globalSettings); + return true; } async function delPlugin(name: string): Promise { @@ -65,7 +68,7 @@ async function startPlugin(name: string): Promise { let ctx = {} // ctx is how you're going to store things that need to be accessed in both onStart and onStop. dumb, i know onStart(ctx) logger.log(`Started ${name}!`, ["Plugins"]) - window.demon.__plugins[name] = {status: 1, ctx: ctx} + window.demon[pluginsSym][name] = {status: 1, ctx: ctx} return true } } @@ -80,7 +83,7 @@ async function stopPlugin(name: string): Promise { const plug = globalSettings["plugin"][name] const exports: Object = (0, eval)(plug.iife) const onStop: (ctx: Object)=>void = exports.onStop - onStop(window.demon.__plugins[name].ctx) + onStop(window.demon[pluginsSym][name].ctx); logger.log(`Stopped ${name}!`, ["Plugins"]) return true } @@ -99,10 +102,10 @@ async function togglePlugin(name: string): Promise { } export default { - init, - addPlugin, - delPlugin, - startPlugin, - stopPlugin, - togglePlugin -} + init, + addPlugin, + delPlugin, + startPlugin, + stopPlugin, + togglePlugin, +}; diff --git a/src/api/settingsInjection.js b/src/api/settingsInjection.js new file mode 100644 index 0000000..1a9261c --- /dev/null +++ b/src/api/settingsInjection.js @@ -0,0 +1,71 @@ +// @flow + +import webpack from "./webpack"; + +const settingsSym = Symbol("__settings"); + +const settingsView = webpack.find(m => m.default && m.default.displayName === "SettingsView"); + +type getPredicateSectionsEntry = + | { + section: "HEADER", + label: string, + } + | { + section: "DIVIDER", + } + | { + section: string, + label: string, + component: Function, + }; + +function patch(args: mixed, ret: getPredicateSectionsEntry[]) { + const processedEntries = window.demon[settingsSym].entries.map((e) => ({ + section: "DEMON_SETTINGS_LOADER_" + e.name, + label: e.name, + element: e.component, + })); + + const injectionIndex = + 2 + ret.findIndex((section) => section.section === "Game Activity"); + ret.splice( + injectionIndex, + 0, + { section: "HEADER", label: "Demon" }, + ...processedEntries, + { section: "DIVIDER" } + ); + return ret; +} + +function init() { + window.demon[settingsSym] = { + patch: window.demon.patcher.after( + "getPredicateSections", + settingsView.default.prototype, + patch + ), + entries: [], + }; + + // debug + window.demonunpatch = window.demon[settingsSym].patch; +} + +function unInit(obj: Object) { + window.demon[settingsSym].patch(); + delete window.demon[settingsSym]; +} + +function unregisterSettingsEntry(name: string) { + let s = window.demon[settingsSym]; + s.entries = s.entries.filter((e) => e.name !== name); +} + +function registerSettingsEntry(name: string, component: Function): () => void { + window.demon[settingsSym].entries.push({ name, component }); + return () => unregisterSettingsEntry(name); +} + +export default { init, unInit, registerSettingsEntry, unregisterSettingsEntry }; diff --git a/src/api/webpack.js b/src/api/webpack.js index 83f8f3f..8ecb3b5 100644 --- a/src/api/webpack.js +++ b/src/api/webpack.js @@ -1,9 +1,9 @@ // @flow -type ModuleType = {[symbol]: any}; -type FilterFunc = (module: ModuleType) => boolean +type ModuleType = Object/* { [symbol]: any } */; +type FilterFunc = (module: ModuleType) => boolean; function getModules(): any { - let modules: {c: mixed[]} = {}; + let modules: { c: mixed[] } = {}; window.webpackChunkdiscord_app.push([ [Math.random().toString(36)], @@ -17,32 +17,44 @@ function getModules(): any { } function filter(filter: FilterFunc, moduleList: any): Array { - let modules: Array = []; - for (const mod in moduleList) { - const module = moduleList[mod].exports; - if (module) { - if (module.default && module.__esModule && filter(module.default)) { modules.push(module.default); } - else if (filter(module)) { modules.push(module); } - } - } - return modules; + let modules: Array = []; + for (const mod in moduleList) { + const module = moduleList[mod].exports; + if (module) { + if (module.default && module.__esModule && filter(module.default)) { + modules.push(module.default); + } else if (filter(module)) { + modules.push(module); + } + } + } + return modules; } -let webpack: Object = { - modules: getModules(), - filter: filter, - find: (filter: FilterFunc)=>webpack.filter(filter, webpack.modules)[0], - findAll: (filter: FilterFunc)=>webpack.filter(filter, webpack.modules), - findByProps: (...props: Array) => { - return webpack.find((module) => { - return props.every((prop)=>module[prop]!==undefined) - }) - }, - findByPropsAll: (...props: Array) => { - return webpack.findAll((module) => - props.every((prop)=>module[prop]!==undefined) - ) - } -} +type WebpackModules = { + modules: () => any, + filter: (filter: FilterFunc, moduleList: any) => Object[], + find: (filter: FilterFunc) => Object, + findAll: (filter: FilterFunc) => Object[], + findByProps: (...props: string[]) => Object, + findByPropsAll: (...props: string[]) => Object[], +}; + +let webpack: WebpackModules = { + modules: getModules(), + filter: filter, + find: (filter: FilterFunc) => webpack.filter(filter, webpack.modules)[0], + findAll: (filter: FilterFunc) => webpack.filter(filter, webpack.modules), + findByProps: (...props: Array) => { + return webpack.find((module) => { + return props.every((prop) => module[prop] !== undefined); + }); + }, + findByPropsAll: (...props: Array) => { + return webpack.findAll((module) => + props.every((prop) => module[prop] !== undefined) + ); + }, +}; export default webpack; diff --git a/src/init.js b/src/init.js index 4df6413..55337ca 100644 --- a/src/init.js +++ b/src/init.js @@ -1,52 +1,53 @@ // @flow -import Patcher from "simian" -import webpack from "./api/webpack.js" -import common from "./api/common.js" -import commands from "./api/commands.js" -import plugins from "./api/plugins.js" +import Patcher from "simian"; +import webpack from "./api/webpack"; +import common from "./api/common"; +import commands from "./api/commands"; +import plugins from "./api/plugins"; +import settingsInj from "./api/settingsInjection"; async function init(obj: Object): Promise { - const patcher = new Patcher() - obj.demon = { - patcher: { - monkeyPatch: function(name: string, parentObj: Object, patches: Object): ()=>void { - let [upb,upi,upa] = [()=>{},()=>{},()=>{}] - if (patches.before !== undefined) upb = patcher.before( - name, - parentObj, - patches.before - ) - if (patches.instead !== undefined) upb = patcher.instead( - name, - parentObj, - patches.instead - ) - if (patches.after !== undefined) upb = patcher.after( - name, - parentObj, - patches.after - ) - return ()=>{ - upb() - upi() - upa() - } - }, - before: patcher.before, - instead: patcher.instead, - after: patcher.after - }, - webpack, - common, - commands: { - add: commands.add - }, - __DO_NOT_USE_OR_YOU_WILL_BE_FIRED_UNTO_THE_DEPTHS_OF_HELL: { - plugins - } - } - commands.init(obj) - plugins.init(obj) + const patcher = new Patcher(); + obj.demon = { + patcher: { + monkeyPatch: function ( + name: string, + parentObj: Object, + patches: Object + ): () => void { + let [upb, upi, upa] = [() => {}, () => {}, () => {}]; + if (patches.before !== undefined) + upb = patcher.before(name, parentObj, patches.before); + if (patches.instead !== undefined) + upb = patcher.instead(name, parentObj, patches.instead); + if (patches.after !== undefined) + upb = patcher.after(name, parentObj, patches.after); + return () => { + upb(); + upi(); + upa(); + }; + }, + before: patcher.before, + instead: patcher.instead, + after: patcher.after, + }, + webpack, + common, + commands: { + add: commands.add, + }, + __DO_NOT_USE_OR_YOU_WILL_BE_FIRED_UNTO_THE_DEPTHS_OF_HELL: { + plugins, + }, + settings: { + register: settingsInj.registerSettingsEntry, + unregister: settingsInj.unregisterSettingsEntry, + }, + }; + commands.init(obj); + plugins.init(obj); + settingsInj.init(); } export default init;