Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4a5371a746 | ||
|
77749ed5e1 | ||
|
45aa9fbb6d | ||
|
4a2657f928 | ||
|
6578eb487e | ||
|
c080a0eaac | ||
|
3ea6a96715 | ||
|
af614465a4 | ||
|
7b248ee309 | ||
|
fd25b5f296 | ||
|
77d08c5c28 | ||
|
ea11f2244f | ||
|
96126fa39f | ||
|
9bd82943e3 | ||
|
5edc94062c | ||
|
394d2060eb | ||
|
119b628f33 | ||
|
32f2043193 | ||
|
04d2dd26c4 |
12
README.md
12
README.md
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
The cutest Discord client mod
|
The cutest Discord client mod
|
||||||
|
|
||||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334)
|
| ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) |
|
||||||
|
|:--:|
|
||||||
|
| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) |
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -28,6 +30,14 @@ Visit https://vencord.dev/download
|
|||||||
|
|
||||||
https://discord.gg/D9uwnFnqmd
|
https://discord.gg/D9uwnFnqmd
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** |
|
||||||
|
|:--:|
|
||||||
|
| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) |
|
||||||
|
| *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* |
|
||||||
|
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
<a href="https://star-history.com/#Vendicated/Vencord&Timeline">
|
<a href="https://star-history.com/#Vendicated/Vencord&Timeline">
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"zip-local": "^0.3.5"
|
"zip-local": "^0.3.5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.1.1",
|
"packageManager": "pnpm@8.10.2",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch",
|
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch",
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
|
import { readdir } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
||||||
|
|
||||||
const defines = {
|
const defines = {
|
||||||
IS_STANDALONE: isStandalone,
|
IS_STANDALONE: isStandalone,
|
||||||
@ -43,13 +45,59 @@ const nodeCommonOpts = {
|
|||||||
format: "cjs",
|
format: "cjs",
|
||||||
platform: "node",
|
platform: "node",
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
external: ["electron", "original-fs", ...commonOpts.external],
|
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
|
||||||
define: defines,
|
define: defines,
|
||||||
};
|
};
|
||||||
|
|
||||||
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
||||||
const sourcemap = watch ? "inline" : "external";
|
const sourcemap = watch ? "inline" : "external";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("esbuild").Plugin}
|
||||||
|
*/
|
||||||
|
const globNativesPlugin = {
|
||||||
|
name: "glob-natives-plugin",
|
||||||
|
setup: build => {
|
||||||
|
const filter = /^~pluginNatives$/;
|
||||||
|
build.onResolve({ filter }, args => {
|
||||||
|
return {
|
||||||
|
namespace: "import-natives",
|
||||||
|
path: args.path
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
build.onLoad({ filter, namespace: "import-natives" }, async () => {
|
||||||
|
const pluginDirs = ["plugins", "userplugins"];
|
||||||
|
let code = "";
|
||||||
|
let natives = "\n";
|
||||||
|
let i = 0;
|
||||||
|
for (const dir of pluginDirs) {
|
||||||
|
const dirPath = join("src", dir);
|
||||||
|
if (!await existsAsync(dirPath)) continue;
|
||||||
|
const plugins = await readdir(dirPath);
|
||||||
|
for (const p of plugins) {
|
||||||
|
if (!await existsAsync(join(dirPath, p, "native.ts"))) continue;
|
||||||
|
|
||||||
|
const nameParts = p.split(".");
|
||||||
|
const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1);
|
||||||
|
// pluginName.thing.desktop -> PluginName.thing
|
||||||
|
const cleanPluginName = p[0].toUpperCase() + namePartsWithoutTarget.join(".").slice(1);
|
||||||
|
|
||||||
|
const mod = `p${i}`;
|
||||||
|
code += `import * as ${mod} from "./${dir}/${p}/native";\n`;
|
||||||
|
natives += `${JSON.stringify(cleanPluginName)}:${mod},\n`;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code += `export default {${natives}};`;
|
||||||
|
return {
|
||||||
|
contents: code,
|
||||||
|
resolveDir: "./src"
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// Discord Desktop main & renderer & preload
|
// Discord Desktop main & renderer & preload
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -62,7 +110,11 @@ await Promise.all([
|
|||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
},
|
||||||
|
plugins: [
|
||||||
|
...nodeCommonOpts.plugins,
|
||||||
|
globNativesPlugin
|
||||||
|
]
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
@ -107,7 +159,11 @@ await Promise.all([
|
|||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
},
|
||||||
|
plugins: [
|
||||||
|
...nodeCommonOpts.plugins,
|
||||||
|
globNativesPlugin
|
||||||
|
]
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
|
@ -20,8 +20,8 @@ import "../suppressExperimentalWarnings.js";
|
|||||||
import "../checkNodeVersion.js";
|
import "../checkNodeVersion.js";
|
||||||
|
|
||||||
import { exec, execSync } from "child_process";
|
import { exec, execSync } from "child_process";
|
||||||
import { existsSync, readFileSync } from "fs";
|
import { constants as FsConstants, readFileSync } from "fs";
|
||||||
import { readdir, readFile } from "fs/promises";
|
import { access, readdir, readFile } from "fs/promises";
|
||||||
import { join, relative } from "path";
|
import { join, relative } from "path";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
@ -47,6 +47,12 @@ export const banner = {
|
|||||||
|
|
||||||
const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs"));
|
const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs"));
|
||||||
|
|
||||||
|
export function existsAsync(path) {
|
||||||
|
return access(path, FsConstants.F_OK)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
||||||
/**
|
/**
|
||||||
* @type {import("esbuild").Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
@ -79,7 +85,7 @@ export const globPlugins = kind => ({
|
|||||||
let plugins = "\n";
|
let plugins = "\n";
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const dir of pluginDirs) {
|
for (const dir of pluginDirs) {
|
||||||
if (!existsSync(`./src/${dir}`)) continue;
|
if (!await existsAsync(`./src/${dir}`)) continue;
|
||||||
const files = await readdir(`./src/${dir}`);
|
const files = await readdir(`./src/${dir}`);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.startsWith("_") || file.startsWith(".")) continue;
|
if (file.startsWith("_") || file.startsWith(".")) continue;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { IpcEvents } from "@utils/IpcEvents";
|
import { IpcEvents } from "@utils/IpcEvents";
|
||||||
import { IpcRes } from "@utils/types";
|
import { IpcRes } from "@utils/types";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
|
import { PluginIpcMappings } from "main/ipcPlugins";
|
||||||
import type { UserThemeHeader } from "main/themes";
|
import type { UserThemeHeader } from "main/themes";
|
||||||
|
|
||||||
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
||||||
@ -17,6 +18,16 @@ export function sendSync<T = any>(event: IpcEvents, ...args: any[]) {
|
|||||||
return ipcRenderer.sendSync(event, ...args) as T;
|
return ipcRenderer.sendSync(event, ...args) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PluginHelpers = {} as Record<string, Record<string, (...args: any[]) => Promise<any>>>;
|
||||||
|
const pluginIpcMap = sendSync<PluginIpcMappings>(IpcEvents.GET_PLUGIN_IPC_METHOD_MAP);
|
||||||
|
|
||||||
|
for (const [plugin, methods] of Object.entries(pluginIpcMap)) {
|
||||||
|
const map = PluginHelpers[plugin] = {};
|
||||||
|
for (const [methodName, method] of Object.entries(methods)) {
|
||||||
|
map[methodName] = (...args: any[]) => invoke(method as IpcEvents, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
themes: {
|
themes: {
|
||||||
uploadTheme: (fileName: string, fileData: string) => invoke<void>(IpcEvents.UPLOAD_THEME, fileName, fileData),
|
uploadTheme: (fileName: string, fileData: string) => invoke<void>(IpcEvents.UPLOAD_THEME, fileName, fileData),
|
||||||
@ -61,12 +72,5 @@ export default {
|
|||||||
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
|
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
|
||||||
},
|
},
|
||||||
|
|
||||||
pluginHelpers: {
|
pluginHelpers: PluginHelpers
|
||||||
OpenInApp: {
|
|
||||||
resolveRedirect: (url: string) => invoke<string>(IpcEvents.OPEN_IN_APP__RESOLVE_REDIRECT, url),
|
|
||||||
},
|
|
||||||
VoiceMessages: {
|
|
||||||
readRecording: (path: string) => invoke<Uint8Array | null>(IpcEvents.VOICE_MESSAGES_READ_RECORDING, path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -17,73 +17,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IpcEvents } from "@utils/IpcEvents";
|
import { IpcEvents } from "@utils/IpcEvents";
|
||||||
import { app, ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import { readFile } from "fs/promises";
|
|
||||||
import { request } from "https";
|
|
||||||
import { basename, normalize } from "path";
|
|
||||||
|
|
||||||
import { getSettings } from "./ipcMain";
|
import PluginNatives from "~pluginNatives";
|
||||||
|
|
||||||
// FixSpotifyEmbeds
|
const PluginIpcMappings = {} as Record<string, Record<string, string>>;
|
||||||
app.on("browser-window-created", (_, win) => {
|
export type PluginIpcMappings = typeof PluginIpcMappings;
|
||||||
win.webContents.on("frame-created", (_, { frame }) => {
|
|
||||||
frame.once("dom-ready", () => {
|
|
||||||
if (frame.url.startsWith("https://open.spotify.com/embed/")) {
|
|
||||||
const settings = getSettings().plugins?.FixSpotifyEmbeds;
|
|
||||||
if (!settings?.enabled) return;
|
|
||||||
|
|
||||||
frame.executeJavaScript(`
|
for (const [plugin, methods] of Object.entries(PluginNatives)) {
|
||||||
const original = Audio.prototype.play;
|
const entries = Object.entries(methods);
|
||||||
Audio.prototype.play = function() {
|
if (!entries.length) continue;
|
||||||
this.volume = ${(settings.volume / 100) || 0.1};
|
|
||||||
return original.apply(this, arguments);
|
const mappings = PluginIpcMappings[plugin] = {};
|
||||||
|
|
||||||
|
for (const [methodName, method] of entries) {
|
||||||
|
const key = `VencordPluginNative_${plugin}_${methodName}`;
|
||||||
|
ipcMain.handle(key, method);
|
||||||
|
mappings[methodName] = key;
|
||||||
}
|
}
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// #region OpenInApp
|
|
||||||
// These links don't support CORS, so this has to be native
|
|
||||||
const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
|
||||||
|
|
||||||
function getRedirect(url: string) {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
const req = request(new URL(url), { method: "HEAD" }, res => {
|
|
||||||
resolve(
|
|
||||||
res.headers.location
|
|
||||||
? getRedirect(res.headers.location)
|
|
||||||
: url
|
|
||||||
);
|
|
||||||
});
|
|
||||||
req.on("error", reject);
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.OPEN_IN_APP__RESOLVE_REDIRECT, async (_, url: string) => {
|
ipcMain.on(IpcEvents.GET_PLUGIN_IPC_METHOD_MAP, e => {
|
||||||
if (!validRedirectUrls.test(url)) return url;
|
e.returnValue = PluginIpcMappings;
|
||||||
|
|
||||||
return getRedirect(url);
|
|
||||||
});
|
});
|
||||||
// #endregion
|
|
||||||
|
|
||||||
|
|
||||||
// #region VoiceMessages
|
|
||||||
ipcMain.handle(IpcEvents.VOICE_MESSAGES_READ_RECORDING, async (_, filePath: string) => {
|
|
||||||
filePath = normalize(filePath);
|
|
||||||
const filename = basename(filePath);
|
|
||||||
const discordBaseDirWithTrailingSlash = normalize(app.getPath("userData") + "/");
|
|
||||||
console.log(filename, discordBaseDirWithTrailingSlash, filePath);
|
|
||||||
if (filename !== "recording.ogg" || !filePath.startsWith(discordBaseDirWithTrailingSlash)) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const buf = await readFile(filePath);
|
|
||||||
return new Uint8Array(buf.buffer);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
@ -32,6 +32,8 @@ function isNewer($new: string, old: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function patchLatest() {
|
function patchLatest() {
|
||||||
|
if (process.env.DISABLE_UPDATER_AUTO_PATCHING) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentAppPath = dirname(process.execPath);
|
const currentAppPath = dirname(process.execPath);
|
||||||
const currentVersion = basename(currentAppPath);
|
const currentVersion = basename(currentAppPath);
|
||||||
|
5
src/modules.d.ts
vendored
5
src/modules.d.ts
vendored
@ -24,6 +24,11 @@ declare module "~plugins" {
|
|||||||
export default plugins;
|
export default plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "~pluginNatives" {
|
||||||
|
const pluginNatives: Record<string, Record<string, (event: Electron.IpcMainInvokeEvent, ...args: unknown[]) => unknown>>;
|
||||||
|
export default pluginNatives;
|
||||||
|
}
|
||||||
|
|
||||||
declare module "~git-hash" {
|
declare module "~git-hash" {
|
||||||
const hash: string;
|
const hash: string;
|
||||||
export default hash;
|
export default hash;
|
||||||
|
@ -50,7 +50,7 @@ async function embedDidMount(this: Component<Props>) {
|
|||||||
const { titles, thumbnails } = await res.json();
|
const { titles, thumbnails } = await res.json();
|
||||||
|
|
||||||
const hasTitle = titles[0]?.votes >= 0;
|
const hasTitle = titles[0]?.votes >= 0;
|
||||||
const hasThumb = thumbnails[0]?.votes >= 0;
|
const hasThumb = thumbnails[0]?.votes >= 0 && !thumbnails[0].original;
|
||||||
|
|
||||||
if (!hasTitle && !hasThumb) return;
|
if (!hasTitle && !hasThumb) return;
|
||||||
|
|
||||||
@ -58,12 +58,12 @@ async function embedDidMount(this: Component<Props>) {
|
|||||||
enabled: true
|
enabled: true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (titles[0]?.votes >= 0) {
|
if (hasTitle) {
|
||||||
embed.dearrow.oldTitle = embed.rawTitle;
|
embed.dearrow.oldTitle = embed.rawTitle;
|
||||||
embed.rawTitle = titles[0].title;
|
embed.rawTitle = titles[0].title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thumbnails[0]?.votes >= 0) {
|
if (hasThumb) {
|
||||||
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||||
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||||
}
|
}
|
||||||
|
@ -77,15 +77,6 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// Fix search history being disabled / broken with isStaff
|
|
||||||
{
|
|
||||||
find: '("showNewSearch")',
|
|
||||||
predicate: () => settings.store.enableIsStaff,
|
|
||||||
replacement: {
|
|
||||||
match: /(?<=showNewSearch"\);return)\s?/,
|
|
||||||
replace: "!1&&"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
find: 'H1,title:"Experiments"',
|
find: 'H1,title:"Experiments"',
|
||||||
replacement: {
|
replacement: {
|
||||||
|
@ -206,10 +206,10 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// Allow stickers to be sent everywhere
|
// Allow stickers to be sent everywhere
|
||||||
{
|
{
|
||||||
find: "canUseStickersEverywhere:function",
|
find: "canUseCustomStickersEverywhere:function",
|
||||||
predicate: () => settings.store.enableStickerBypass,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /canUseStickersEverywhere:function\(\i\){/,
|
match: /canUseCustomStickersEverywhere:function\(\i\){/,
|
||||||
replace: "$&return true;"
|
replace: "$&return true;"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
27
src/plugins/fixSpotifyEmbeds.desktop/native.ts
Normal file
27
src/plugins/fixSpotifyEmbeds.desktop/native.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { app } from "electron";
|
||||||
|
import { getSettings } from "main/ipcMain";
|
||||||
|
|
||||||
|
app.on("browser-window-created", (_, win) => {
|
||||||
|
win.webContents.on("frame-created", (_, { frame }) => {
|
||||||
|
frame.once("dom-ready", () => {
|
||||||
|
if (frame.url.startsWith("https://open.spotify.com/embed/")) {
|
||||||
|
const settings = getSettings().plugins?.FixSpotifyEmbeds;
|
||||||
|
if (!settings?.enabled) return;
|
||||||
|
|
||||||
|
frame.executeJavaScript(`
|
||||||
|
const original = Audio.prototype.play;
|
||||||
|
Audio.prototype.play = function() {
|
||||||
|
this.volume = ${(settings.volume / 100) || 0.1};
|
||||||
|
return original.apply(this, arguments);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -302,6 +302,7 @@ export default definePlugin({
|
|||||||
match: /attachments:(\i)\((\i)\)/,
|
match: /attachments:(\i)\((\i)\)/,
|
||||||
replace:
|
replace:
|
||||||
"attachments: $1((() => {" +
|
"attachments: $1((() => {" +
|
||||||
|
" if ($self.shouldIgnore($2)) return $2;" +
|
||||||
" let old = arguments[1]?.attachments;" +
|
" let old = arguments[1]?.attachments;" +
|
||||||
" if (!old) return $2;" +
|
" if (!old) return $2;" +
|
||||||
" let new_ = $2.attachments?.map(a => a.id) ?? [];" +
|
" let new_ = $2.attachments?.map(a => a.id) ?? [];" +
|
||||||
|
@ -25,10 +25,6 @@ import definePlugin, { OptionType } from "@utils/types";
|
|||||||
const EMOTE = "<:luna:1035316192220553236>";
|
const EMOTE = "<:luna:1035316192220553236>";
|
||||||
const DATA_KEY = "MessageTags_TAGS";
|
const DATA_KEY = "MessageTags_TAGS";
|
||||||
const MessageTagsMarker = Symbol("MessageTags");
|
const MessageTagsMarker = Symbol("MessageTags");
|
||||||
const author = {
|
|
||||||
id: "821472922140803112",
|
|
||||||
bot: false
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Tag {
|
interface Tag {
|
||||||
name: string;
|
name: string;
|
||||||
@ -59,14 +55,12 @@ function createTagCommand(tag: Tag) {
|
|||||||
execute: async (_, ctx) => {
|
execute: async (_, ctx) => {
|
||||||
if (!await getTag(tag.name)) {
|
if (!await getTag(tag.name)) {
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
|
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
|
||||||
});
|
});
|
||||||
return { content: `/${tag.name}` };
|
return { content: `/${tag.name}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, {
|
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} The tag **${tag.name}** has been sent!`
|
content: `${EMOTE} The tag **${tag.name}** has been sent!`
|
||||||
});
|
});
|
||||||
return { content: tag.message.replaceAll("\\n", "\n") };
|
return { content: tag.message.replaceAll("\\n", "\n") };
|
||||||
@ -162,7 +156,6 @@ export default definePlugin({
|
|||||||
|
|
||||||
if (await getTag(name))
|
if (await getTag(name))
|
||||||
return sendBotMessage(ctx.channel.id, {
|
return sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} A Tag with the name **${name}** already exists!`
|
content: `${EMOTE} A Tag with the name **${name}** already exists!`
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -176,7 +169,6 @@ export default definePlugin({
|
|||||||
await addTag(tag);
|
await addTag(tag);
|
||||||
|
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} Successfully created the tag **${name}**!`
|
content: `${EMOTE} Successfully created the tag **${name}**!`
|
||||||
});
|
});
|
||||||
break; // end 'create'
|
break; // end 'create'
|
||||||
@ -186,7 +178,6 @@ export default definePlugin({
|
|||||||
|
|
||||||
if (!await getTag(name))
|
if (!await getTag(name))
|
||||||
return sendBotMessage(ctx.channel.id, {
|
return sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -194,14 +185,12 @@ export default definePlugin({
|
|||||||
await removeTag(name);
|
await removeTag(name);
|
||||||
|
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} Successfully deleted the tag **${name}**!`
|
content: `${EMOTE} Successfully deleted the tag **${name}**!`
|
||||||
});
|
});
|
||||||
break; // end 'delete'
|
break; // end 'delete'
|
||||||
}
|
}
|
||||||
case "list": {
|
case "list": {
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -224,12 +213,10 @@ export default definePlugin({
|
|||||||
|
|
||||||
if (!tag)
|
if (!tag)
|
||||||
return sendBotMessage(ctx.channel.id, {
|
return sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||||
});
|
});
|
||||||
|
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: tag.message.replaceAll("\\n", "\n")
|
content: tag.message.replaceAll("\\n", "\n")
|
||||||
});
|
});
|
||||||
break; // end 'preview'
|
break; // end 'preview'
|
||||||
@ -237,7 +224,6 @@ export default definePlugin({
|
|||||||
|
|
||||||
default: {
|
default: {
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
author,
|
|
||||||
content: "Invalid sub-command"
|
content: "Invalid sub-command"
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -26,15 +26,13 @@ export default definePlugin({
|
|||||||
authors: [Devs.Ven, Devs.adryd],
|
authors: [Devs.Ven, Devs.adryd],
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
fetch("https://raw.githubusercontent.com/adryd325/oneko.js/5977144dce83e4d71af1de005d16e38eebeb7b72/oneko.js")
|
fetch("https://raw.githubusercontent.com/adryd325/oneko.js/8fa8a1864aa71cd7a794d58bc139e755e96a236c/oneko.js")
|
||||||
.then(x => x.text())
|
.then(x => x.text())
|
||||||
.then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif"))
|
.then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif"))
|
||||||
.then(eval);
|
.then(eval);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
clearInterval(window.onekoInterval);
|
|
||||||
delete window.onekoInterval;
|
|
||||||
document.getElementById("oneko")?.remove();
|
document.getElementById("oneko")?.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType, PluginNative } from "@utils/types";
|
||||||
import { showToast, Toasts } from "@webpack/common";
|
import { showToast, Toasts } from "@webpack/common";
|
||||||
import type { MouseEvent } from "react";
|
import type { MouseEvent } from "react";
|
||||||
|
|
||||||
@ -45,6 +45,8 @@ const settings = definePluginSettings({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Native = VencordNative.pluginHelpers.OpenInApp as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "OpenInApp",
|
name: "OpenInApp",
|
||||||
description: "Open Spotify, Steam and Epic Games URLs in their respective apps instead of your browser",
|
description: "Open Spotify, Steam and Epic Games URLs in their respective apps instead of your browser",
|
||||||
@ -84,7 +86,7 @@ export default definePlugin({
|
|||||||
if (!IS_WEB && ShortUrlMatcher.test(url)) {
|
if (!IS_WEB && ShortUrlMatcher.test(url)) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
// CORS jumpscare
|
// CORS jumpscare
|
||||||
url = await VencordNative.pluginHelpers.OpenInApp.resolveRedirect(url);
|
url = await Native.resolveRedirect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
spotify: {
|
spotify: {
|
||||||
|
31
src/plugins/openInApp/native.ts
Normal file
31
src/plugins/openInApp/native.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IpcMainInvokeEvent } from "electron";
|
||||||
|
import { request } from "https";
|
||||||
|
|
||||||
|
// These links don't support CORS, so this has to be native
|
||||||
|
const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
||||||
|
|
||||||
|
function getRedirect(url: string) {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const req = request(new URL(url), { method: "HEAD" }, res => {
|
||||||
|
resolve(
|
||||||
|
res.headers.location
|
||||||
|
? getRedirect(res.headers.location)
|
||||||
|
: url
|
||||||
|
);
|
||||||
|
});
|
||||||
|
req.on("error", reject);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveRedirect(_: IpcMainInvokeEvent, url: string) {
|
||||||
|
if (!validRedirectUrls.test(url)) return url;
|
||||||
|
|
||||||
|
return getRedirect(url);
|
||||||
|
}
|
@ -100,10 +100,10 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Fix getRowHeight's check for whether this is the DMs section
|
// Fix getRowHeight's check for whether this is the DMs section
|
||||||
// section === DMS
|
// DMS (inlined) === section
|
||||||
match: /===\i\.DMS&&0/,
|
match: /(?<=getRowHeight=\(.{2,50}?)1===\i/,
|
||||||
// section -1 === DMS
|
// DMS (inlined) === section - 1
|
||||||
replace: "-1$&"
|
replace: "$&-1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Override scrollToChannel to properly account for pinned channels
|
// Override scrollToChannel to properly account for pinned channels
|
||||||
|
@ -20,12 +20,12 @@ import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCal
|
|||||||
import { ReplyIcon } from "@components/Icons";
|
import { ReplyIcon } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common";
|
import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
|
|
||||||
const replyFn = findByCodeLazy("showMentionToggle", "TEXTAREA_FOCUS", "shiftKey");
|
const messageUtils = findByPropsLazy("replyToMessage");
|
||||||
|
|
||||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => () => {
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => () => {
|
||||||
// make sure the message is in the selected channel
|
// make sure the message is in the selected channel
|
||||||
@ -43,7 +43,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag
|
|||||||
id="reply"
|
id="reply"
|
||||||
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
||||||
icon={ReplyIcon}
|
icon={ReplyIcon}
|
||||||
action={(e: React.MouseEvent) => replyFn(channel, message, e)}
|
action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag
|
|||||||
id="reply"
|
id="reply"
|
||||||
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
||||||
icon={ReplyIcon}
|
icon={ReplyIcon}
|
||||||
action={(e: React.MouseEvent) => replyFn(channel, message, e)}
|
action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export default definePlugin({
|
|||||||
createHighlighter,
|
createHighlighter,
|
||||||
renderHighlighter: ({ lang, content }: { lang: string; content: string; }) => {
|
renderHighlighter: ({ lang, content }: { lang: string; content: string; }) => {
|
||||||
return createHighlighter({
|
return createHighlighter({
|
||||||
lang,
|
lang: lang?.toLowerCase(),
|
||||||
content,
|
content,
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
});
|
});
|
||||||
|
@ -68,7 +68,7 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
// RenderLevel defines if a channel is hidden, collapsed in category, visible, etc
|
// RenderLevel defines if a channel is hidden, collapsed in category, visible, etc
|
||||||
find: ".CannotShow=",
|
find: '"placeholder-channel-id"',
|
||||||
replacement: [
|
replacement: [
|
||||||
// Remove the special logic for channels we don't have access to
|
// Remove the special logic for channels we don't have access to
|
||||||
{
|
{
|
||||||
@ -82,13 +82,8 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// Make channels we dont have access to be the same level as normal ones
|
// Make channels we dont have access to be the same level as normal ones
|
||||||
{
|
{
|
||||||
match: /(?<=renderLevel:(\i\(this,\i\)\?\i\.Show:\i\.WouldShowIfUncollapsed).+?renderLevel:).+?(?=,)/,
|
match: /(activeJoinedRelevantThreads:.{0,50}VIEW_CHANNEL.+?renderLevel:(.+?),threadIds.+?renderLevel:).+?(?=,threadIds)/g,
|
||||||
replace: (_, renderLevelExpression) => renderLevelExpression
|
replace: (_, rest, defaultRenderLevel) => `${rest}${defaultRenderLevel}`
|
||||||
},
|
|
||||||
// Make channels we dont have access to be the same level as normal ones
|
|
||||||
{
|
|
||||||
match: /(?<=activeJoinedRelevantThreads.+?renderLevel:.+?,threadIds:\i\(this.record.+?renderLevel:)(\i)\..+?(?=,)/,
|
|
||||||
replace: (_, RenderLevels) => `${RenderLevels}.Show`
|
|
||||||
},
|
},
|
||||||
// Remove permission checking for getRenderLevel function
|
// Remove permission checking for getRenderLevel function
|
||||||
{
|
{
|
||||||
|
11
src/plugins/superReactionTweaks/README.md
Normal file
11
src/plugins/superReactionTweaks/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Super Reaction Tweaks
|
||||||
|
|
||||||
|
This plugin applies configurable various tweaks to super reactions.
|
||||||
|
|
||||||
|
![Screenshot](https://user-images.githubusercontent.com/22851444/281598795-58f07116-9f95-4f64-940b-23a5499f2302.png)
|
||||||
|
|
||||||
|
## Features:
|
||||||
|
|
||||||
|
**Super React By Default** - The reaction picker will default to super reactions instead of normal reactions.
|
||||||
|
|
||||||
|
**Super Reaction Play Limit** - Allows you to decide how many super reaction animations can play at once, including removing the limit entirely.
|
63
src/plugins/superReactionTweaks/index.ts
Normal file
63
src/plugins/superReactionTweaks/index.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated, ant0n, FieryFlames and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
|
export const settings = definePluginSettings({
|
||||||
|
superReactByDefault: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Reaction picker will default to Super Reactions",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
unlimitedSuperReactionPlaying: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Remove the limit on Super Reactions playing at once",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
superReactionPlayingLimit: {
|
||||||
|
description: "Max Super Reactions to play at once",
|
||||||
|
type: OptionType.SLIDER,
|
||||||
|
default: 20,
|
||||||
|
markers: [5, 10, 20, 40, 60, 80, 100],
|
||||||
|
stickToMarkers: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
superReactionPlayingLimit: {
|
||||||
|
disabled() { return this.store.unlimitedSuperReactionPlaying; },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "SuperReactionTweaks",
|
||||||
|
description: "Customize the limit of Super Reactions playing at once, and super react by default",
|
||||||
|
authors: [Devs.FieryFlames, Devs.ant0n],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ",BURST_REACTION_EFFECT_PLAY",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=BURST_REACTION_EFFECT_PLAY:\i=>{.{50,100})(\i\(\i,\i\))>=\d+/,
|
||||||
|
replace: "!$self.shouldPlayBurstReaction($1)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".hasAvailableBurstCurrency)",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.useBurstReactionsExperiment.{0,20})useState\(!1\)(?=.+?(\i===\i\.EmojiIntention.REACTION))/,
|
||||||
|
replace: "useState($self.settings.store.superReactByDefault && $1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
settings,
|
||||||
|
|
||||||
|
shouldPlayBurstReaction(playingCount: number) {
|
||||||
|
if (settings.store.unlimitedSuperReactionPlaying) return true;
|
||||||
|
if (playingCount <= settings.store.superReactionPlayingLimit) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
@ -16,11 +16,14 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { PluginNative } from "@utils/types";
|
||||||
import { Button, showToast, Toasts, useState } from "@webpack/common";
|
import { Button, showToast, Toasts, useState } from "@webpack/common";
|
||||||
|
|
||||||
import type { VoiceRecorder } from ".";
|
import type { VoiceRecorder } from ".";
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
|
|
||||||
|
const Native = VencordNative.pluginHelpers.VoiceMessages as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingChange }) => {
|
export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingChange }) => {
|
||||||
const [recording, setRecording] = useState(false);
|
const [recording, setRecording] = useState(false);
|
||||||
|
|
||||||
@ -49,7 +52,7 @@ export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingC
|
|||||||
} else {
|
} else {
|
||||||
discordVoice.stopLocalAudioRecording(async (filePath: string) => {
|
discordVoice.stopLocalAudioRecording(async (filePath: string) => {
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
const buf = await VencordNative.pluginHelpers.VoiceMessages.readRecording(filePath);
|
const buf = await Native.readRecording(filePath);
|
||||||
if (buf)
|
if (buf)
|
||||||
setAudioBlob(new Blob([buf], { type: "audio/ogg; codecs=opus" }));
|
setAudioBlob(new Blob([buf], { type: "audio/ogg; codecs=opus" }));
|
||||||
else
|
else
|
||||||
|
24
src/plugins/voiceMessages/native.ts
Normal file
24
src/plugins/voiceMessages/native.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { app } from "electron";
|
||||||
|
import { readFile } from "fs/promises";
|
||||||
|
import { basename, normalize } from "path";
|
||||||
|
|
||||||
|
export async function readRecording(_, filePath: string) {
|
||||||
|
filePath = normalize(filePath);
|
||||||
|
const filename = basename(filePath);
|
||||||
|
const discordBaseDirWithTrailingSlash = normalize(app.getPath("userData") + "/");
|
||||||
|
console.log(filename, discordBaseDirWithTrailingSlash, filePath);
|
||||||
|
if (filename !== "recording.ogg" || !filePath.startsWith(discordBaseDirWithTrailingSlash)) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const buf = await readFile(filePath);
|
||||||
|
return new Uint8Array(buf.buffer);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,8 @@ export const enum IpcEvents {
|
|||||||
BUILD = "VencordBuild",
|
BUILD = "VencordBuild",
|
||||||
OPEN_MONACO_EDITOR = "VencordOpenMonacoEditor",
|
OPEN_MONACO_EDITOR = "VencordOpenMonacoEditor",
|
||||||
|
|
||||||
|
GET_PLUGIN_IPC_METHOD_MAP = "VencordGetPluginIpcMethodMap",
|
||||||
|
|
||||||
OPEN_IN_APP__RESOLVE_REDIRECT = "VencordOIAResolveRedirect",
|
OPEN_IN_APP__RESOLVE_REDIRECT = "VencordOIAResolveRedirect",
|
||||||
VOICE_MESSAGES_READ_RECORDING = "VencordVMReadRecording",
|
VOICE_MESSAGES_READ_RECORDING = "VencordVMReadRecording",
|
||||||
}
|
}
|
||||||
|
@ -379,6 +379,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||||||
name: "ProffDea",
|
name: "ProffDea",
|
||||||
id: 609329952180928513n
|
id: 609329952180928513n
|
||||||
},
|
},
|
||||||
|
ant0n: {
|
||||||
|
name: "ant0n",
|
||||||
|
id: 145224646868860928n
|
||||||
|
},
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
@ -307,3 +307,10 @@ export type PluginOptionBoolean = PluginSettingBooleanDef & PluginSettingCommon
|
|||||||
export type PluginOptionSelect = PluginSettingSelectDef & PluginSettingCommon & IsDisabled & IsValid<PluginSettingSelectOption>;
|
export type PluginOptionSelect = PluginSettingSelectDef & PluginSettingCommon & IsDisabled & IsValid<PluginSettingSelectOption>;
|
||||||
export type PluginOptionSlider = PluginSettingSliderDef & PluginSettingCommon & IsDisabled & IsValid<number>;
|
export type PluginOptionSlider = PluginSettingSliderDef & PluginSettingCommon & IsDisabled & IsValid<number>;
|
||||||
export type PluginOptionComponent = PluginSettingComponentDef & PluginSettingCommon;
|
export type PluginOptionComponent = PluginSettingComponentDef & PluginSettingCommon;
|
||||||
|
|
||||||
|
export type PluginNative<PluginExports extends Record<string, (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any>> = {
|
||||||
|
[key in keyof PluginExports]:
|
||||||
|
PluginExports[key] extends (event: Electron.IpcMainInvokeEvent, ...args: infer Args) => infer Return
|
||||||
|
? (...args: Args) => Return extends Promise<any> ? Return : Promise<Return>
|
||||||
|
: never;
|
||||||
|
};
|
||||||
|
@ -31,7 +31,7 @@ waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m
|
|||||||
export const RestAPI: t.RestAPI = findByPropsLazy("getAPIBaseURL", "get");
|
export const RestAPI: t.RestAPI = findByPropsLazy("getAPIBaseURL", "get");
|
||||||
export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear");
|
export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear");
|
||||||
|
|
||||||
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight");
|
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage");
|
||||||
|
|
||||||
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
|
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user