aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pnpm-lock.yaml2
-rw-r--r--src/VencordNative.ts23
-rw-r--r--src/api/MessageEvents.ts36
-rw-r--r--src/components/Settings.tsx13
-rw-r--r--src/plugins/index.ts43
-rw-r--r--src/plugins/messageActions.ts23
-rw-r--r--src/plugins/nitroBypass.ts12
-rw-r--r--src/utils/logger.ts20
-rw-r--r--src/utils/types.ts17
-rw-r--r--src/webpack/common.ts15
10 files changed, 146 insertions, 58 deletions
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f1774b5..304261d 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 755c471..e5ccaaa 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<T = any>(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<IpcRenderer["on"]>[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<IpcRenderer["off"]>[1]) {
+ assertEventAllowed(event);
+ ipcRenderer.off(event, listener);
},
invoke<T = any>(event: string, ...args: any[]): Promise<T> {
- 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 9ee54a6..65b4f45 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<SendListener>();
const editListeners = new Set<EditListener>();
@@ -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 19f6dd2..ee293bb 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<string>(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 ae67f4f..448d247 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 {
+ 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 {
- logger.info("Starting plugin", plugin.name);
- plugin.start();
- } catch (err) {
- logger.error("Failed to start plugin", plugin.name, err);
+ 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 db4b808..961be12 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 5d128a4..d5c4d22 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(/(?<!\\)<a?:(\w+):(\d+)>/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 be341ee..be32f2b 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 520b506..810869c 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<string, any>) {
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<Patch, "plugin">[];
dependencies?: string[],
required?: boolean;
-}
-
-// @ts-ignore lole
-interface PluginDef extends Plugin {
- patches?: Omit<Patch, "plugin">[];
} \ No newline at end of file
diff --git a/src/webpack/common.ts b/src/webpack/common.ts
index f678a94..e45d2ca 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