From f60ccb766f30e7518bc348013b72f071f86ecbc6 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 31 Aug 2022 22:08:05 +0200 Subject: [PATCH] Add Plugin.start, make Settings actually start/stop plugins --- pnpm-lock.yaml | 2 +- src/VencordNative.ts | 23 ++++++++++++------- src/api/MessageEvents.ts | 36 +++++++++++++++++++---------- src/components/Settings.tsx | 13 +++++++++++ src/plugins/index.ts | 43 ++++++++++++++++++++++++++++++----- src/plugins/messageActions.ts | 23 ++++++++++++------- src/plugins/nitroBypass.ts | 12 +++++++--- src/utils/logger.ts | 20 ++++++++++------ src/utils/types.ts | 17 +++++++------- src/webpack/common.ts | 15 ++++++++---- 10 files changed, 146 insertions(+), 58 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1774b509..304261da2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: 5.4 +lockfileVersion: 5.3 specifiers: '@types/flux': ^3.1.11 diff --git a/src/VencordNative.ts b/src/VencordNative.ts index 755c47133..e5ccaaa57 100644 --- a/src/VencordNative.ts +++ b/src/VencordNative.ts @@ -1,24 +1,31 @@ import IPC_EVENTS from './utils/IpcEvents'; import { IpcRenderer, ipcRenderer } from 'electron'; +function assertEventAllowed(event: string) { + if (!(event in IPC_EVENTS)) throw new Error(`Event ${event} not allowed.`); +} export default { getVersions: () => process.versions, ipc: { send(event: string, ...args: any[]) { - if (event in IPC_EVENTS) ipcRenderer.send(event, ...args); - else throw new Error(`Event ${event} not allowed.`); + assertEventAllowed(event); + ipcRenderer.send(event, ...args); }, sendSync(event: string, ...args: any[]): T { - if (event in IPC_EVENTS) return ipcRenderer.sendSync(event, ...args); - else throw new Error(`Event ${event} not allowed.`); + assertEventAllowed(event); + return ipcRenderer.sendSync(event, ...args); }, on(event: string, listener: Parameters[1]) { - if (event in IPC_EVENTS) ipcRenderer.on(event, listener); - else throw new Error(`Event ${event} not allowed.`); + assertEventAllowed(event); + ipcRenderer.on(event, listener); + }, + off(event: string, listener: Parameters[1]) { + assertEventAllowed(event); + ipcRenderer.off(event, listener); }, invoke(event: string, ...args: any[]): Promise { - if (event in IPC_EVENTS) return ipcRenderer.invoke(event, ...args); - else throw new Error(`Event ${event} not allowed.`); + assertEventAllowed(event); + return ipcRenderer.invoke(event, ...args); } }, require(mod: string) { diff --git a/src/api/MessageEvents.ts b/src/api/MessageEvents.ts index 9ee54a6a4..65b4f456e 100644 --- a/src/api/MessageEvents.ts +++ b/src/api/MessageEvents.ts @@ -3,10 +3,10 @@ import Logger from '../utils/logger'; const MessageEventsLogger = new Logger("MessageEvents", "#e5c890"); -interface Emoji { +export interface Emoji { require_colons: boolean, originalName: string, - animated: boolean + animated: boolean; guildId: string, name: string, url: string, @@ -15,11 +15,11 @@ interface Emoji { interface MessageObject { content: string, - validNonShortcutEmojis: Emoji[] + validNonShortcutEmojis: Emoji[]; } -type SendListener = (channelId: string, messageObj: MessageObject, extra: any) => void; -type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => void; +export type SendListener = (channelId: string, messageObj: MessageObject, extra: any) => void; +export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => void; const sendListeners = new Set(); const editListeners = new Set(); @@ -28,7 +28,7 @@ export function _handlePreSend(channelId: string, messageObj: MessageObject, ext for (const listener of sendListeners) { try { listener(channelId, messageObj, extra); - } catch (e) { MessageEventsLogger.error(`MessageSendHandler: Listener encoutered an unknown error. (${e})`) } + } catch (e) { MessageEventsLogger.error(`MessageSendHandler: Listener encoutered an unknown error. (${e})`); } } } @@ -36,20 +36,31 @@ export function _handlePreEdit(channeld: string, messageId: string, messageObj: for (const listener of editListeners) { try { listener(channeld, messageId, messageObj); - } catch (e) { MessageEventsLogger.error(`MessageEditHandler: Listener encoutered an unknown error. (${e})`) } + } catch (e) { MessageEventsLogger.error(`MessageEditHandler: Listener encoutered an unknown error. (${e})`); } } } /** * Note: This event fires off before a message is sent, allowing you to edit the message. */ -export function addPreSendListener(listener: SendListener) { sendListeners.add(listener) } +export function addPreSendListener(listener: SendListener) { + sendListeners.add(listener); + return listener; +} /** * Note: This event fires off before a message's edit is applied, allowing you to further edit the message. */ -export function addPreEditListener(listener: EditListener) { editListeners.add(listener) } -export function removePreSendListener(listener: SendListener) { sendListeners.delete(listener) } -export function removePreEditListener(listener: EditListener) { editListeners.delete(listener) } +export function addPreEditListener(listener: EditListener) { + editListeners.add(listener); + return listener; +} +export function removePreSendListener(listener: SendListener) { + return sendListeners.delete(listener); +} +export function removePreEditListener(listener: EditListener) { + return editListeners.delete(listener); +} + // Message clicks type ClickListener = (message: Message, channel: Channel, event: MouseEvent) => void; @@ -60,12 +71,13 @@ export function _handleClick(message, channel, event) { for (const listener of listeners) { try { listener(message, channel, event); - } catch (e) { MessageEventsLogger.error(`MessageClickHandler: Listener encoutered an unknown error. (${e})`) } + } catch (e) { MessageEventsLogger.error(`MessageClickHandler: Listener encoutered an unknown error. (${e})`); } } } export function addClickListener(listener: ClickListener) { listeners.add(listener); + return listener; } export function removeClickListener(listener: ClickListener) { diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 19f6dd2e2..ee293bbe2 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -5,6 +5,8 @@ import IpcEvents from "../utils/IpcEvents"; import { Button, ButtonProps, Flex, Switch, Forms } from "../webpack/common"; import ErrorBoundary from "./ErrorBoundary"; +import { startPlugin } from "../plugins"; +import { stopPlugin } from '../plugins/index'; export default ErrorBoundary.wrap(function Settings(props) { const [settingsDir, , settingsDirPending] = useAwaiter(() => VencordNative.ipc.invoke(IpcEvents.GET_SETTINGS_DIR), "Loading..."); @@ -52,8 +54,19 @@ export default ErrorBoundary.wrap(function Settings(props) { settings.plugins[p.name].enabled = v; if (v) { p.dependencies?.forEach(d => { + // TODO: start every dependency settings.plugins[d].enabled = true; }); + if (!p.started && !startPlugin(p)) { + // TODO show notification + } + } else { + if (p.started && !stopPlugin(p)) { + // TODO show notification + } + } + if (p.patches) { + // TODO show notification } }} note={p.description} diff --git a/src/plugins/index.ts b/src/plugins/index.ts index ae67f4f0b..448d2476e 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,7 +1,7 @@ import Plugins from "plugins"; import { Settings } from "../api/settings"; import Logger from "../utils/logger"; -import { Patch } from "../utils/types"; +import { Patch, Plugin } from "../utils/types"; const logger = new Logger("PluginManager", "#a6d189"); @@ -17,12 +17,43 @@ for (const plugin of Plugins) if (plugin.patches && Settings.plugins[plugin.name } export function startAll() { - for (const plugin of plugins) if (plugin.start && Settings.plugins[plugin.name].enabled) { + for (const plugin of plugins) if (Settings.plugins[plugin.name].enabled) { + startPlugin(plugin); + } +} + +export function startPlugin(p: Plugin) { + if (p.start) { + logger.info("Starting plugin", p.name); + if (p.started) { + logger.warn(`${p.name} already started`); + return false; + } try { - logger.info("Starting plugin", plugin.name); - plugin.start(); - } catch (err) { - logger.error("Failed to start plugin", plugin.name, err); + p.start(); + p.started = true; + return true; + } catch (err: any) { + logger.error(`Failed to start ${p.name}\n`, err); + return false; + } + } +} + +export function stopPlugin(p: Plugin) { + if (p.stop) { + logger.info("Stopping plugin", p.name); + if (!p.started) { + logger.warn(`${p.name} already stopped / never started`); + return false; + } + try { + p.stop(); + p.started = false; + return true; + } catch (err: any) { + logger.error(`Failed to stop ${p.name}\n`, err); + return false; } } } \ No newline at end of file diff --git a/src/plugins/messageActions.ts b/src/plugins/messageActions.ts index db4b8084d..961be125c 100644 --- a/src/plugins/messageActions.ts +++ b/src/plugins/messageActions.ts @@ -1,12 +1,17 @@ -import { addClickListener } from "../api/MessageEvents"; +import { addClickListener, removeClickListener } from '../api/MessageEvents'; import definePlugin from "../utils/types"; import { find, findByProps } from "../webpack"; +let isDeletePressed = false; +const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); +const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false); + export default definePlugin({ name: "MessageQuickActions", description: "Quick Delete, Quick edit", author: "Vendicated", dependencies: ["MessageEventsAPI"], + start() { const { deleteMessage, startEditMessage } = findByProps("deleteMessage"); const { can } = findByProps("can", "initialize"); @@ -14,14 +19,10 @@ export default definePlugin({ const { getCurrentUser } = findByProps("getCurrentUser"); let isDeletePressed = false; - document.addEventListener("keydown", e => { - if (e.key === "Backspace") isDeletePressed = true; - }); - document.addEventListener("keyup", e => { - if (e.key === "Backspace") isDeletePressed = false; - }); + document.addEventListener("keydown", keydown); + document.addEventListener("keyup", keyup); - addClickListener((msg, chan, event) => { + this.onClick = addClickListener((msg, chan, event) => { const isMe = msg.author.id === getCurrentUser().id; if (!isDeletePressed) { if (isMe && event.detail >= 2) { @@ -33,5 +34,11 @@ export default definePlugin({ event.preventDefault(); } }); + }, + + stop() { + removeClickListener(this.onClick); + document.removeEventListener("keydown", keydown); + document.removeEventListener("keyup", keyup); } }); \ No newline at end of file diff --git a/src/plugins/nitroBypass.ts b/src/plugins/nitroBypass.ts index 5d128a4c4..d5c4d2218 100644 --- a/src/plugins/nitroBypass.ts +++ b/src/plugins/nitroBypass.ts @@ -1,4 +1,4 @@ -import { addPreSendListener, addPreEditListener } from "../api/MessageEvents"; +import { addPreSendListener, addPreEditListener, SendListener, removePreSendListener, removePreEditListener } from '../api/MessageEvents'; import { findByProps } from "../webpack"; import definePlugin from "../utils/types"; @@ -22,6 +22,7 @@ export default definePlugin({ }) }, ], + start() { const { getCustomEmojiById } = findByProps("getCustomEmojiById"); @@ -33,7 +34,7 @@ export default definePlugin({ delete x.guildPremiumTier; }); - addPreSendListener((_, messageObj) => { + this.preSend = addPreSendListener((_, messageObj) => { const guildId = window.location.href.split("channels/")[1].split("/")[0]; for (const emoji of messageObj.validNonShortcutEmojis) { if (!emoji.require_colons) continue; @@ -44,7 +45,7 @@ export default definePlugin({ messageObj.content = messageObj.content.replace(emojiString, ` ${url} `); } }); - addPreEditListener((_, __, messageObj) => { + this.preEdit = addPreEditListener((_, __, messageObj) => { const guildId = window.location.href.split("channels/")[1].split("/")[0]; for (const [emojiStr, _, emojiId] of messageObj.content.matchAll(/(?/ig)) { @@ -56,4 +57,9 @@ export default definePlugin({ } }); }, + + stop() { + removePreSendListener(this.preSend); + removePreEditListener(this.preEdit); + } }); diff --git a/src/utils/logger.ts b/src/utils/logger.ts index be341ee91..be32f2b38 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,27 +1,33 @@ export default class Logger { constructor(public name: string, public color: string) { } - private _log(level: "log" | "error" | "warn" | "info" | "debug", args: any[]) { - console[level](`%c ${this.name} `, `background: ${this.color}; color: black; font-weight: bold`, ...args); + private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[]) { + console[level]( + `%c Vencord %c %c ${this.name} `, + `background: ${levelColor}; color: black; font-weight: bold; border-radius: 5px;`, + "", + `background: ${this.color}; color: black; font-weight: bold; border-radius: 5px;` + , ...args + ); } public log(...args: any[]) { - this._log("log", args); + this._log("log", "#a6d189", args); } public info(...args: any[]) { - this._log("info", args); + this._log("info", "#a6d189", args); } public error(...args: any[]) { - this._log("error", args); + this._log("error", "#e78284", args); } public warn(...args: any[]) { - this._log("warn", args); + this._log("warn", "#e5c890", args); } public debug(...args: any[]) { - this._log("debug", args); + this._log("debug", "#eebebe", args); } } \ No newline at end of file diff --git a/src/utils/types.ts b/src/utils/types.ts index 520b50645..810869c17 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -1,5 +1,5 @@ // exists to export default definePlugin({...}) -export default function definePlugin(p: PluginDef) { +export default function definePlugin(p: PluginDef & Record) { return p; } @@ -14,17 +14,18 @@ export interface Patch { replacement: PatchReplacement | PatchReplacement[]; } -export interface Plugin { +export interface Plugin extends PluginDef { + patches?: Patch[]; + started: boolean; +} + +interface PluginDef { name: string; description: string; author: string; start?(): void; - patches?: Patch[]; + stop?(): void; + patches?: Omit[]; dependencies?: string[], required?: boolean; -} - -// @ts-ignore lole -interface PluginDef extends Plugin { - patches?: Omit[]; } \ No newline at end of file diff --git a/src/webpack/common.ts b/src/webpack/common.ts index f678a9457..e45d2caa1 100644 --- a/src/webpack/common.ts +++ b/src/webpack/common.ts @@ -1,15 +1,19 @@ import { startAll } from "../plugins"; import { waitFor, filters } from './webpack'; +import type Components from "discord-types/components"; +import type Stores from "discord-types/stores"; +import type Other from "discord-types/other"; -export let FluxDispatcher: any; +export let FluxDispatcher: Other.FluxDispatcher; export let React: typeof import("react"); -export let UserStore: any; +export let UserStore: Stores.UserStore; export let Forms: any; export let Button: any; export let ButtonProps: any; export let Switch: any; -export let Flex: any; -export let Card: any; +export let Flex: Components.Flex; +export let Card: Components.Card; +export let Tooltip: Components.Tooltip; waitFor("useState", m => React = m); waitFor(["dispatch", "subscribe"], m => { @@ -28,4 +32,5 @@ waitFor(["ButtonLooks", "default"], m => { }); waitFor(filters.byDisplayName("SwitchItem"), m => Switch = m.default); waitFor(filters.byDisplayName("Flex"), m => Flex = m.default); -waitFor(filters.byDisplayName("Card"), m => Card = m.default); \ No newline at end of file +waitFor(filters.byDisplayName("Card"), m => Card = m.default); +waitFor(filters.byDisplayName("Tooltip"), m => Tooltip = m.default); \ No newline at end of file