Compare commits
177 Commits
Author | SHA1 | Date | |
---|---|---|---|
ea11f2244f | |||
96126fa39f | |||
9bd82943e3 | |||
5edc94062c | |||
394d2060eb | |||
119b628f33 | |||
32f2043193 | |||
04d2dd26c4 | |||
86e94343cc | |||
dd44ac1ad2 | |||
370b3d366d | |||
a67c7f841d | |||
2c9793202d | |||
a257926609 | |||
77659be4f0 | |||
37b9a62460 | |||
7f73e13364 | |||
fcf2bdda70 | |||
fa9da2d693 | |||
44b21394b3 | |||
27fffc8bc3 | |||
098da8c3fd | |||
9cf88d4232 | |||
dd61b0c999 | |||
5dc0d06be1 | |||
9de6c2d4ff | |||
e37f62ac0a | |||
56a9d79f85 | |||
9d78233afa | |||
9af2ec65ae | |||
584885acf5 | |||
18fdc33ee7 | |||
522fdcd15d | |||
af135b9245 | |||
4b958d17fd | |||
bfb48b4faf | |||
9dd8e72245 | |||
aae790f1c1 | |||
7f17e70697 | |||
b3311c6f12 | |||
bc09225258 | |||
9ce923d4d7 | |||
7845af0802 | |||
89672882b9 | |||
e05c630a54 | |||
38834ef7ac | |||
98d49af728 | |||
0afe319141 | |||
a9e67e2955 | |||
1676956f61 | |||
8692109bc5 | |||
0847f205b8 | |||
2f94e167c4 | |||
589c070773 | |||
8567ff6239 | |||
e4701769a5 | |||
25f101602d | |||
85bfa1e719 | |||
b48998d485 | |||
6d605050e1 | |||
07c4a097e0 | |||
c1de41436a | |||
64c6f5740f | |||
03523446c1 | |||
25dc25c707 | |||
8ac8048845 | |||
ffe6bb1c5d | |||
74faaa216f | |||
a6b8b59d5c | |||
9eaeb24196 | |||
6b28e5ad85 | |||
d852393cfb | |||
0c12500c0a | |||
1502024440 | |||
45fa4f89c6 | |||
96dff84ed8 | |||
c1b8739104 | |||
2183d2d29d | |||
722dcb033e | |||
53cd14844f | |||
38daf6ec2b | |||
635b80c58b | |||
ec9e111047 | |||
d54f8b5e8e | |||
09a922a01c | |||
23963f40c2 | |||
a41abfef31 | |||
ddee3e1264 | |||
6c4afa52a3 | |||
ce6081c39b | |||
123853e848 | |||
0c6445b66b | |||
7094345516 | |||
148a32096c | |||
ab1b002ed1 | |||
c7a20769f9 | |||
9c13befcb6 | |||
3fdd0edf14 | |||
2169090ce5 | |||
5e13de72c2 | |||
3a7c27253b | |||
465a87f66b | |||
13c59f47cb | |||
0d7157dd20 | |||
88c6e2a0e9 | |||
535d510d3b | |||
cbc23b1bdd | |||
aa2a57b733 | |||
c3ccbbfa99 | |||
685f44d40f | |||
8f0009778a | |||
f385dc380e | |||
bb900785ed | |||
61fac0a8f1 | |||
1a607639cc | |||
c90440f031 | |||
baf952512c | |||
fbc5038306 | |||
ddc39fe84d | |||
4f8c75372c | |||
024a77c577 | |||
1130521e4b | |||
3bd657611c | |||
b659d4e9c1 | |||
9c60b38acc | |||
c5dd50ad8f | |||
131e91a37c | |||
06b4dffa62 | |||
3917193e8f | |||
8d1561aed4 | |||
cf3c28e1ff | |||
af1aa39647 | |||
7de1b5dcb6 | |||
cd06980016 | |||
e69236c5f8 | |||
2478ffb695 | |||
788d22f9e9 | |||
b69ac1cdad | |||
892a79b2a7 | |||
9895540943 | |||
7e0fc1d0ea | |||
bb771153e3 | |||
65d39dcf21 | |||
cf5e93ee52 | |||
4d8e4e62ca | |||
cb2532d22c | |||
8c998b9330 | |||
fb22c57da1 | |||
554bb18e9b | |||
44c9675795 | |||
322ecc5e88 | |||
7ee9a8bb99 | |||
69b54535c3 | |||
2a56081bc2 | |||
baa7d8c078 | |||
940193c30b | |||
09b646b860 | |||
922e3ce6fe | |||
cb93c11e16 | |||
4beef9f73b | |||
4da79abb21 | |||
4e27722b54 | |||
97c0face2f | |||
1a1d9b07e8 | |||
4a2def03e7 | |||
544edce9f9 | |||
e4485165d0 | |||
fada76ec81 | |||
f659c46031 | |||
ae1dc4eab0 | |||
fe60a72b80 | |||
5a0b2ee3f5 | |||
6c1b8b0d8a | |||
b2a1410a96 | |||
c25c95eecd | |||
d94418f42f | |||
da1a8cdd67 |
.eslintrc.json
.github/ISSUE_TEMPLATE
README.mdpackage.jsonscripts
src
Vencord.tsVencordNative.ts
api
components
main
modules.d.tsplugins
_api
badges.tsxcommands.tscontextMenu.tsmemberListDecorators.tsmessageAccessories.tsmessageDecorations.tsmessageEvents.tsnotices.tsserverList.tssettingsStore.ts
_core
alwaysAnimate
alwaysTrust
arRPC.web
banger
betterFolders
betterGifAltText
betterNotes
betterRoleDot
betterUploadButton
blurNsfw
callTimer
colorSighted
customRPC
dearrow
disableDMCallIdle
emoteCloner
experiments
fakeNitro
fakeProfileThemes
favEmojiFirst
favGifSearch
fixSpotifyEmbeds.desktop
forceOwnerCrown
gameActivityToggle
gifPaste
greetStickerPicker
iLoveSpam
ignoreActivities
imageZoom
invisibleChat.desktop
lastfm
loadingQuotes
memberCount
messageLinkEmbeds
messageLogger
moreUserTags
muteNewGuild
mutualGroupDMs
noBlockedMessages
noDevtoolsWarning
noMosaic
noPendingCount
noProfileThemes
noScreensharePreview
noSystemBadge.discordDesktop
noUnblockToJump
onePingPerDM
openInApp
permissionFreeWill
permissionsViewer
petpet
pictureInPicture
pinDms
platformIndicators
previewMessage
pronoundb
relationshipNotifier
@ -51,7 +51,10 @@
|
|||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||||
"yoda": "error",
|
"yoda": "error",
|
||||||
"prefer-destructuring": ["error", { "object": true, "array": false }],
|
"prefer-destructuring": ["error", {
|
||||||
|
"VariableDeclarator": { "array": false, "object": true },
|
||||||
|
"AssignmentExpression": { "array": false, "object": false }
|
||||||
|
}],
|
||||||
"operator-assignment": ["error", "always"],
|
"operator-assignment": ["error", "always"],
|
||||||
"no-useless-computed-key": "error",
|
"no-useless-computed-key": "error",
|
||||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||||
|
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -14,7 +14,8 @@ body:
|
|||||||
DO NOT USE THIS FORM, unless
|
DO NOT USE THIS FORM, unless
|
||||||
- you are a vencord contributor
|
- you are a vencord contributor
|
||||||
- you were given explicit permission to use this form by a moderator in our support server
|
- you were given explicit permission to use this form by a moderator in our support server
|
||||||
- you are filing a security related report
|
|
||||||
|
DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new)
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: discord
|
id: discord
|
||||||
|
@ -28,6 +28,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://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">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.5.7",
|
"version": "1.6.3",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -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", ...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;
|
||||||
|
@ -61,6 +61,13 @@ const report = {
|
|||||||
otherErrors: [] as string[]
|
otherErrors: [] as string[]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IGNORED_DISCORD_ERRORS = [
|
||||||
|
"KeybindStore: Looking for callback action",
|
||||||
|
"Unable to process domain list delta: Client revision number is null",
|
||||||
|
"Downloading the full bad domains file",
|
||||||
|
/\[GatewaySocket\].{0,110}Cannot access '/
|
||||||
|
] as Array<string | RegExp>;
|
||||||
|
|
||||||
function toCodeBlock(s: string) {
|
function toCodeBlock(s: string) {
|
||||||
s = s.replace(/```/g, "`\u200B`\u200B`");
|
s = s.replace(/```/g, "`\u200B`\u200B`");
|
||||||
return "```" + s + " ```";
|
return "```" + s + " ```";
|
||||||
@ -86,6 +93,8 @@ async function printReport() {
|
|||||||
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
|
||||||
|
|
||||||
console.log("## Discord Errors");
|
console.log("## Discord Errors");
|
||||||
report.otherErrors.forEach(e => {
|
report.otherErrors.forEach(e => {
|
||||||
console.log(`- ${toCodeBlock(e)}`);
|
console.log(`- ${toCodeBlock(e)}`);
|
||||||
@ -259,7 +268,7 @@ function runTime(token: string) {
|
|||||||
const { wreq } = Vencord.Webpack;
|
const { wreq } = Vencord.Webpack;
|
||||||
|
|
||||||
console.error("[PUP_DEBUG]", "Loading all chunks...");
|
console.error("[PUP_DEBUG]", "Loading all chunks...");
|
||||||
const ids = Function("return" + wreq.u.toString().match(/\{.+\}/s)![0])();
|
const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])();
|
||||||
for (const id in ids) {
|
for (const id in ids) {
|
||||||
const isWasm = await fetch(wreq.p + wreq.u(id))
|
const isWasm = await fetch(wreq.p + wreq.u(id))
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
@ -280,7 +289,7 @@ function runTime(token: string) {
|
|||||||
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
||||||
}, 1000));
|
}, 1000));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[PUP_DEBUG]", "A fatal error occured");
|
console.error("[PUP_DEBUG]", "A fatal error occurred");
|
||||||
console.error("[PUP_DEBUG]", e);
|
console.error("[PUP_DEBUG]", e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLow
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.head.append(Object.assign(document.createElement("style"), {
|
document.head.append(Object.assign(document.createElement("style"), {
|
||||||
id: "vencord-native-titlebar-style",
|
id: "vencord-native-titlebar-style",
|
||||||
textContent: "[class*=titleBar-]{display: none!important}"
|
textContent: "[class*=titleBar]{display: none!important}"
|
||||||
}));
|
}));
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
@ -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,14 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { mergeDefaults } from "@utils/misc";
|
import { mergeDefaults } from "@utils/misc";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { SnowflakeUtils } from "@webpack/common";
|
import { SnowflakeUtils } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
import type { PartialDeep } from "type-fest";
|
import type { PartialDeep } from "type-fest";
|
||||||
|
|
||||||
import { Argument } from "./types";
|
import { Argument } from "./types";
|
||||||
|
|
||||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
const MessageCreator = findByPropsLazy("createBotMessage");
|
||||||
const MessageSender = findByPropsLazy("receiveMessage");
|
const MessageSender = findByPropsLazy("receiveMessage");
|
||||||
|
|
||||||
export function generateId() {
|
export function generateId() {
|
||||||
@ -38,7 +38,7 @@ export function generateId() {
|
|||||||
* @returns {Message}
|
* @returns {Message}
|
||||||
*/
|
*/
|
||||||
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
|
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
|
||||||
const botMessage = createBotMessage({ channelId, content: "", embeds: [] });
|
const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] });
|
||||||
|
|
||||||
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
|
|||||||
* Remove a context menu patch
|
* Remove a context menu patch
|
||||||
* @param navId The navId(s) for the context menu(s) to remove the patch
|
* @param navId The navId(s) for the context menu(s) to remove the patch
|
||||||
* @param patch The patch to be removed
|
* @param patch The patch to be removed
|
||||||
* @returns Wheter the patch was sucessfully removed from the context menu(s)
|
* @returns Whether the patch was successfully removed from the context menu(s)
|
||||||
*/
|
*/
|
||||||
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
|
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
|
||||||
const navIds = Array.isArray(navId) ? navId : [navId as string];
|
const navIds = Array.isArray(navId) ? navId : [navId as string];
|
||||||
@ -82,7 +82,7 @@ export function removeContextMenuPatch<T extends string | Array<string>>(navId:
|
|||||||
/**
|
/**
|
||||||
* Remove a global context menu patch
|
* Remove a global context menu patch
|
||||||
* @param patch The patch to be removed
|
* @param patch The patch to be removed
|
||||||
* @returns Wheter the patch was sucessfully removed
|
* @returns Whether the patch was successfully removed
|
||||||
*/
|
*/
|
||||||
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
|
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
|
||||||
return globalPatches.delete(patch);
|
return globalPatches.delete(patch);
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import { Logger } from "@utils/Logger";
|
|
||||||
import { findModuleId, wreq } from "@webpack";
|
|
||||||
|
|
||||||
import { Settings } from "./Settings";
|
|
||||||
|
|
||||||
interface Setting<T> {
|
|
||||||
/**
|
|
||||||
* Get the setting value
|
|
||||||
*/
|
|
||||||
getSetting(): T;
|
|
||||||
/**
|
|
||||||
* Update the setting value
|
|
||||||
* @param value The new value
|
|
||||||
*/
|
|
||||||
updateSetting(value: T | ((old: T) => T)): Promise<void>;
|
|
||||||
/**
|
|
||||||
* React hook for automatically updating components when the setting is updated
|
|
||||||
*/
|
|
||||||
useSetting(): T;
|
|
||||||
settingsStoreApiGroup: string;
|
|
||||||
settingsStoreApiName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingsStores: Array<Setting<any>> | undefined = proxyLazy(() => {
|
|
||||||
const modId = findModuleId('"textAndImages","renderSpoilers"');
|
|
||||||
if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module.");
|
|
||||||
|
|
||||||
const mod = wreq(modId);
|
|
||||||
if (mod == null) return;
|
|
||||||
|
|
||||||
return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the store for a setting
|
|
||||||
* @param group The setting group
|
|
||||||
* @param name The name of the setting
|
|
||||||
*/
|
|
||||||
export function getSettingStore<T = any>(group: string, name: string): Setting<T> | undefined {
|
|
||||||
if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency.");
|
|
||||||
|
|
||||||
return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getSettingStore but lazy
|
|
||||||
*/
|
|
||||||
export function getSettingStoreLazy<T = any>(group: string, name: string) {
|
|
||||||
return proxyLazy(() => getSettingStore<T>(group, name));
|
|
||||||
}
|
|
@ -29,7 +29,6 @@ import * as $Notices from "./Notices";
|
|||||||
import * as $Notifications from "./Notifications";
|
import * as $Notifications from "./Notifications";
|
||||||
import * as $ServerList from "./ServerList";
|
import * as $ServerList from "./ServerList";
|
||||||
import * as $Settings from "./Settings";
|
import * as $Settings from "./Settings";
|
||||||
import * as $SettingsStore from "./SettingsStore";
|
|
||||||
import * as $Styles from "./Styles";
|
import * as $Styles from "./Styles";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,10 +90,6 @@ export const MemberListDecorators = $MemberListDecorators;
|
|||||||
* An API allowing you to persist data
|
* An API allowing you to persist data
|
||||||
*/
|
*/
|
||||||
export const Settings = $Settings;
|
export const Settings = $Settings;
|
||||||
/**
|
|
||||||
* An API allowing you to read, manipulate and automatically update components based on Discord settings
|
|
||||||
*/
|
|
||||||
export const SettingsStore = $SettingsStore;
|
|
||||||
/**
|
/**
|
||||||
* An API allowing you to dynamically load styles
|
* An API allowing you to dynamically load styles
|
||||||
* a
|
* a
|
||||||
|
@ -94,7 +94,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
(async () => {
|
(async () => {
|
||||||
for (const user of plugin.authors.slice(0, 6)) {
|
for (const user of plugin.authors.slice(0, 6)) {
|
||||||
const author = user.id
|
const author = user.id
|
||||||
? await UserUtils.fetchUser(`${user.id}`)
|
? await UserUtils.getUser(`${user.id}`)
|
||||||
.catch(() => makeDummyUser({ username: user.name }))
|
.catch(() => makeDummyUser({ username: user.name }))
|
||||||
: makeDummyUser({ username: user.name });
|
: makeDummyUser({ username: user.name });
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ import { openModalLazy } from "@utils/modal";
|
|||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ export default function PluginSettings() {
|
|||||||
}
|
}
|
||||||
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
||||||
|
|
||||||
return window._.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
return lodash.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
type P = JSX.Element | JSX.Element[];
|
type P = JSX.Element | JSX.Element[];
|
||||||
|
@ -25,7 +25,7 @@ import { Margins } from "@utils/margins";
|
|||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { showItemInFolder } from "@utils/native";
|
import { showItemInFolder } from "@utils/native";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
import { findByPropsLazy, findLazy } from "@webpack";
|
||||||
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||||
import { UserThemeHeader } from "main/themes";
|
import { UserThemeHeader } from "main/themes";
|
||||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||||
@ -41,7 +41,7 @@ type FileInput = ComponentType<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const InviteActions = findByPropsLazy("resolveInvite");
|
const InviteActions = findByPropsLazy("resolveInvite");
|
||||||
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
|
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||||
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
||||||
|
|
||||||
const cl = classNameFactory("vc-settings-theme-");
|
const cl = classNameFactory("vc-settings-theme-");
|
||||||
|
@ -46,7 +46,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
|
|||||||
if (code === "ENOENT")
|
if (code === "ENOENT")
|
||||||
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
||||||
else {
|
else {
|
||||||
var err = `An error occured while running \`${cmd}\`:\n`;
|
var err = `An error occurred while running \`${cmd}\`:\n`;
|
||||||
err += stderr || `Code \`${code}\`. See the console for more info`;
|
err += stderr || `Code \`${code}\`. See the console for more info`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// #region OpenInApp
|
const mappings = PluginIpcMappings[plugin] = {};
|
||||||
// These links don't support CORS, so this has to be native
|
|
||||||
const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
|
||||||
|
|
||||||
function getRedirect(url: string) {
|
for (const [methodName, method] of entries) {
|
||||||
return new Promise<string>((resolve, reject) => {
|
const key = `VencordPluginNative_${plugin}_${methodName}`;
|
||||||
const req = request(new URL(url), { method: "HEAD" }, res => {
|
ipcMain.handle(key, method);
|
||||||
resolve(
|
mappings[methodName] = key;
|
||||||
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
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs";
|
||||||
import { basename, dirname, join } from "path";
|
import { basename, dirname, join } from "path";
|
||||||
|
|
||||||
function isNewer($new: string, old: string) {
|
function isNewer($new: string, old: string) {
|
||||||
|
@ -49,7 +49,9 @@ async function getRepo() {
|
|||||||
async function calculateGitChanges() {
|
async function calculateGitChanges() {
|
||||||
await git("fetch");
|
await git("fetch");
|
||||||
|
|
||||||
const res = await git("log", "HEAD...origin/main", "--pretty=format:%an/%h/%s");
|
const branch = await git("branch", "--show-current");
|
||||||
|
|
||||||
|
const res = await git("log", `HEAD...origin/${branch.stdout.trim()}`, "--pretty=format:%an/%h/%s");
|
||||||
|
|
||||||
const commits = res.stdout.trim();
|
const commits = res.stdout.trim();
|
||||||
return commits ? commits.split("\n").map(line => {
|
return commits ? commits.split("\n").map(line => {
|
||||||
|
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;
|
||||||
|
@ -85,17 +85,19 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// alt: "", aria-hidden: false, src: originalSrc
|
// alt: "", aria-hidden: false, src: originalSrc
|
||||||
match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/g,
|
match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/,
|
||||||
// ...badge.props, ..., src: badge.image ?? ...
|
// ...badge.props, ..., src: badge.image ?? ...
|
||||||
replace: "...$1.props,$& $1.image??"
|
replace: "...$1.props,$& $1.image??"
|
||||||
},
|
},
|
||||||
|
// replace their component with ours if applicable
|
||||||
{
|
{
|
||||||
match: /children:function(?<=(\i)\.(?:tooltip|description),spacing:\d.+?)/g,
|
match: /(?<=text:(\i)\.description,spacing:12,)children:/,
|
||||||
replace: "children:$1.component ? () => $self.renderBadgeComponent($1) : function"
|
replace: "children:$1.component ? () => $self.renderBadgeComponent($1) :"
|
||||||
},
|
},
|
||||||
|
// conditionally override their onClick with badge.onClick if it exists
|
||||||
{
|
{
|
||||||
match: /onClick:function(?=.{0,200}href:(\i)\.link)/,
|
match: /href:(\i)\.link/,
|
||||||
replace: "onClick:$1.onClick??function"
|
replace: "...($1.onClick && { onClick: $1.onClick }),$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,8 @@ export default definePlugin({
|
|||||||
find: "Unexpected value for option",
|
find: "Unexpected value for option",
|
||||||
replacement: {
|
replacement: {
|
||||||
// return [2, cmd.execute(args, ctx)]
|
// return [2, cmd.execute(args, ctx)]
|
||||||
match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/,
|
match: /,(\i)\.execute\((\i),(\i)\)/,
|
||||||
replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]`
|
replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Show plugin name instead of "Built-In"
|
// Show plugin name instead of "Built-In"
|
||||||
|
@ -29,8 +29,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "♫ (つ。◕‿‿◕。)つ ♪",
|
find: "♫ (つ。◕‿‿◕。)つ ♪",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=function \i\((\i)\){)(?=var \i,\i=\i\.navId)/,
|
match: /let{navId:/,
|
||||||
replace: (_, props) => `Vencord.Api.ContextMenu._patchContextMenu(${props});`
|
replace: "Vencord.Api.ContextMenu._patchContextMenu(arguments[0]);$&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -25,25 +25,23 @@ export default definePlugin({
|
|||||||
authors: [Devs.TheSun, Devs.Ven],
|
authors: [Devs.TheSun, Devs.Ven],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "lostPermissionTooltipText,",
|
find: ".lostPermission)",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /decorators:.{0,100}?children:\[(?<=(\i)\.lostPermissionTooltipText.+?)/,
|
{
|
||||||
replace: "$&...Vencord.Api.MemberListDecorators.__getDecorators($1),"
|
match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/,
|
||||||
}
|
replace: "$&vencordProps=$1,"
|
||||||
|
}, {
|
||||||
|
match: /decorators:.{0,100}?children:\[/,
|
||||||
|
replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)),"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "PrivateChannel.renderAvatar",
|
find: "PrivateChannel.renderAvatar",
|
||||||
replacement: [
|
replacement: {
|
||||||
// props are shadowed by nested props so we have to do this
|
match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/,
|
||||||
{
|
replace: "decorators:[...Vencord.Api.MemberListDecorators.__getDecorators(arguments[0]), $1?$2:null]"
|
||||||
match: /\i=(\i)\.applicationStream,/,
|
}
|
||||||
replace: "$&vencordProps=$1,"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/,
|
|
||||||
replace: "decorators:[...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)), $1?$2:null]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -27,9 +27,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.container\)?,children:)(\[[^\]]+\])(}\)\};return)/,
|
match: /(?<=.container\)?,children:)(\[.+?\])/,
|
||||||
replace: (_, pre, accessories, post) =>
|
replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)",
|
||||||
`${pre}Vencord.Api.MessageAccessories._modifyAccessories(${accessories},this.props)${post}`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -25,10 +25,10 @@ export default definePlugin({
|
|||||||
authors: [Devs.TheSun],
|
authors: [Devs.TheSun],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".withMentionPrefix",
|
find: '"Message Username"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.roleDot.{10,50}{children:.{1,2})}\)/,
|
match: /\.Messages\.GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE.+?}\),\i(?=\])/,
|
||||||
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
|
replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -27,10 +27,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '"MessageActionCreators"',
|
find: '"MessageActionCreators"',
|
||||||
replacement: {
|
replacement: {
|
||||||
// editMessage: function (...) {
|
match: /async editMessage\(.+?\)\{/,
|
||||||
match: /\beditMessage:(function\(.+?\))\{/,
|
replace: "$&await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
||||||
// editMessage: async function (...) { await handlePreEdit(...); ...
|
|
||||||
replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -38,7 +36,7 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
||||||
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
||||||
match: /(props\.chatInputType.+?\.then\(\()(function.+?var (\i)=\i\.\i\.parse\((\i),.+?var (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
match: /(type:this\.props\.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
||||||
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
||||||
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
||||||
`${rest1}async ${rest2}` +
|
`${rest1}async ${rest2}` +
|
||||||
@ -49,10 +47,10 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '("interactionUsernameProfile',
|
find: '("interactionUsernameProfile',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /var \i=(\i)\.id,\i=(\i)\.id;return \i\.useCallback\(\(?function\((\i)\){/,
|
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
||||||
replace: (m, message, channel, event) =>
|
replace: (m, message, channel, event) =>
|
||||||
// the message param is shadowed by the event param, so need to alias them
|
// the message param is shadowed by the event param, so need to alias them
|
||||||
`var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});`
|
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -29,12 +29,12 @@ export default definePlugin({
|
|||||||
find: 'displayName="NoticeStore"',
|
find: 'displayName="NoticeStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?=;\i=null;.{0,70}getPremiumSubscription)/g,
|
match: /\i=null;(?=.{0,80}getPremiumSubscription\(\))/g,
|
||||||
replace: ";if(Vencord.Api.Notices.currentNotice)return false"
|
replace: "if(Vencord.Api.Notices.currentNotice)return false;$&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=,NOTICE_DISMISS:function\(\i\){)(?=if\(null==(\i)\))/,
|
match: /(?<=,NOTICE_DISMISS:function\(\i\){)return null!=(\i)/,
|
||||||
replace: (_, notice) => `if(${notice}.id=="VencordNotice")return(${notice}=null,Vencord.Api.Notices.nextNotice(),true);`
|
replace: "if($1.id==\"VencordNotice\")return($1=null,Vencord.Api.Notices.nextNotice(),true);$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -27,15 +27,15 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Messages.DISCODO_DISABLED",
|
find: "Messages.DISCODO_DISABLED",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||||
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "Messages.SERVERS,children",
|
find: "Messages.SERVERS,children",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(Messages\.SERVERS,children:)(.+?default:return null\}\}\)\))/,
|
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
|
||||||
replace: "$1Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($2)"
|
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "SettingsStoreAPI",
|
|
||||||
description: "Patches Discord's SettingsStores to expose their group and name",
|
|
||||||
authors: [Devs.Nuckyz],
|
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: '"textAndImages","renderSpoilers"',
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:function/,
|
|
||||||
replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
@ -26,7 +26,7 @@ export default definePlugin({
|
|||||||
required: true,
|
required: true,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "TRACKING_URL:",
|
find: "AnalyticsActionHandlers.handle",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /^.+$/,
|
match: /^.+$/,
|
||||||
replace: "()=>{}",
|
replace: "()=>{}",
|
||||||
@ -43,20 +43,21 @@ export default definePlugin({
|
|||||||
find: ".METRICS,",
|
find: ".METRICS,",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /this\._intervalId.+?12e4\)/,
|
match: /this\._intervalId=/,
|
||||||
replace: ""
|
replace: "this._intervalId=undefined&&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=increment=function\(\i\){)/,
|
match: /(increment\(\i\){)/,
|
||||||
replace: "return;"
|
replace: "$1return;"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".installedLogHooks)",
|
find: ".installedLogHooks)",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /if\(\i\.getDebugLogging\(\)&&!\i\.installedLogHooks\)/,
|
// if getDebugLogging() returns false, the hooks don't get installed.
|
||||||
replace: "if(false)"
|
match: "getDebugLogging(){",
|
||||||
|
replace: "getDebugLogging(){return false;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -63,26 +63,26 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
get match() {
|
get match() {
|
||||||
switch (Settings.plugins.Settings.settingsLocation) {
|
switch (Settings.plugins.Settings.settingsLocation) {
|
||||||
case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
||||||
case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
||||||
case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
||||||
case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/;
|
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
|
||||||
case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/;
|
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
|
||||||
case "aboveActivity":
|
case "aboveActivity":
|
||||||
default:
|
default:
|
||||||
return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replace: "...$self.makeSettingsCategories($1),$&"
|
replace: "...$self.makeSettingsCategories($1),$&"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
customSections: [] as ((ID: Record<string, unknown>) => any)[],
|
customSections: [] as ((SectionTypes: Record<string, unknown>) => any)[],
|
||||||
|
|
||||||
makeSettingsCategories({ ID }: { ID: Record<string, unknown>; }) {
|
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
section: ID.HEADER,
|
section: SectionTypes.HEADER,
|
||||||
label: "Vencord",
|
label: "Vencord",
|
||||||
className: "vc-settings-header"
|
className: "vc-settings-header"
|
||||||
},
|
},
|
||||||
@ -128,9 +128,9 @@ export default definePlugin({
|
|||||||
element: require("@components/VencordSettings/PatchHelperTab").default,
|
element: require("@components/VencordSettings/PatchHelperTab").default,
|
||||||
className: "vc-patch-helper"
|
className: "vc-patch-helper"
|
||||||
},
|
},
|
||||||
...this.customSections.map(func => func(ID)),
|
...this.customSections.map(func => func(SectionTypes)),
|
||||||
{
|
{
|
||||||
section: ID.DIVIDER
|
section: SectionTypes.DIVIDER
|
||||||
}
|
}
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
},
|
},
|
||||||
|
@ -21,16 +21,30 @@ import definePlugin from "@utils/types";
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "AlwaysAnimate",
|
name: "AlwaysAnimate",
|
||||||
description: "Animates anything that can be animated, besides status emojis.",
|
description: "Animates anything that can be animated",
|
||||||
authors: [Devs.FieryFlames],
|
authors: [Devs.FieryFlames],
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".canAnimate",
|
find: "canAnimate:",
|
||||||
all: true,
|
all: true,
|
||||||
|
// Some modules match the find but the replacement is returned untouched
|
||||||
|
noWarn: true,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.canAnimate\b/g,
|
match: /canAnimate:.+?(?=([,}].*?\)))/g,
|
||||||
replace: ".canAnimate || true"
|
replace: (m, rest) => {
|
||||||
|
const destructuringMatch = rest.match(/}=.+/);
|
||||||
|
if (destructuringMatch == null) return "canAnimate:!0";
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Status emojis
|
||||||
|
find: ".Messages.GUILD_OWNER,",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.activityEmoji,.+?animate:)\i/,
|
||||||
|
replace: "!0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -27,15 +27,15 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".displayName=\"MaskedLinkStore\"",
|
find: ".displayName=\"MaskedLinkStore\"",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.isTrustedDomain=function\(.\){return.+?};/,
|
match: /(?<=isTrustedDomain\(\i\){)return \i\(\i\)/,
|
||||||
replace: ".isTrustedDomain=function(){return true};"
|
replace: "return true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '"7z","ade","adp"',
|
find: "isSuspiciousDownload:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /JSON\.parse\('\[.+?'\)/,
|
match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/,
|
||||||
replace: "[]"
|
replace: "$&return null;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -20,26 +20,19 @@ import { popNotice, showNotice } from "@api/Notices";
|
|||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { filters, findByCodeLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
const assetManager = mapMangledModuleLazy(
|
const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL");
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const lookupRpcApp = findByCodeLazy(".APPLICATION_RPC(");
|
|
||||||
|
|
||||||
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
||||||
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const apps: any = {};
|
const apps: any = {};
|
||||||
async function lookupApp(applicationId: string): Promise<string> {
|
async function lookupApp(applicationId: string): Promise<string> {
|
||||||
const socket: any = {};
|
const socket: any = {};
|
||||||
await lookupRpcApp(socket, applicationId);
|
await RpcUtils.fetchApplicationsRPC(socket, applicationId);
|
||||||
return socket.application;
|
return socket.application;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "BAN_CONFIRM_TITLE.",
|
find: "BAN_CONFIRM_TITLE.",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /src:\w\(\d+\)/g,
|
match: /src:\i\("\d+"\)/g,
|
||||||
replace: "src: Vencord.Settings.plugins.BANger.source"
|
replace: "src: Vencord.Settings.plugins.BANger.source"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,56 +16,44 @@
|
|||||||
* 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 { Settings } from "@api/Settings";
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { i18n, React, useStateFromStores } from "@webpack/common";
|
import { find, findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
|
import { useStateFromStores } from "@webpack/common";
|
||||||
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-bf-");
|
import { ExpandedGuildFolderStore, settings } from ".";
|
||||||
const classes = findByPropsLazy("sidebar", "guilds");
|
|
||||||
|
|
||||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||||
const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||||
|
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
|
||||||
|
|
||||||
function Guilds(props: {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
className: string;
|
|
||||||
bfGuildFolders: any[];
|
|
||||||
}) {
|
|
||||||
// @ts-expect-error
|
|
||||||
const res = Vencord.Plugins.plugins.BetterFolders.Guilds(props);
|
|
||||||
|
|
||||||
// TODO: Make this better
|
|
||||||
const scrollerProps = res.props.children?.props?.children?.props?.children?.[1]?.props;
|
|
||||||
if (scrollerProps?.children) {
|
|
||||||
const servers = scrollerProps.children.find(c => c?.props?.["aria-label"] === i18n.Messages.SERVERS);
|
|
||||||
if (servers) scrollerProps.children = servers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBoundary.wrap(() => {
|
|
||||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||||
const fullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
||||||
|
|
||||||
const guilds = document.querySelector(`.${classes.guilds}`);
|
|
||||||
|
|
||||||
const visible = !!expandedFolders.size;
|
|
||||||
const className = cl("folder-sidebar", { fullscreen });
|
|
||||||
|
|
||||||
const Sidebar = (
|
const Sidebar = (
|
||||||
<Guilds
|
<GuildsBar
|
||||||
className={classes.guilds}
|
{...guildsBarProps}
|
||||||
bfGuildFolders={Array.from(expandedFolders)}
|
isBetterFolders={true}
|
||||||
|
betterFoldersExpandedIds={expandedFolders}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!guilds || !Settings.plugins.BetterFolders.sidebarAnim)
|
const visible = !!expandedFolders.size;
|
||||||
|
const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join(""));
|
||||||
|
|
||||||
|
// We need to display none if we are in fullscreen. Yes this seems horrible doing with css, but it's literally how Discord does it.
|
||||||
|
// Also display flex otherwise to fix scrolling
|
||||||
|
const barStyle = {
|
||||||
|
display: isFullscreen ? "none" : "flex",
|
||||||
|
} as CSSProperties;
|
||||||
|
|
||||||
|
if (!guilds || !settings.store.sidebarAnim) {
|
||||||
return visible
|
return visible
|
||||||
? <div className={className}>{Sidebar}</div>
|
? <div style={barStyle}>{Sidebar}</div>
|
||||||
: null;
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animations.Transition
|
<Animations.Transition
|
||||||
@ -75,11 +63,13 @@ export default ErrorBoundary.wrap(() => {
|
|||||||
leave={{ width: 0 }}
|
leave={{ width: 0 }}
|
||||||
config={{ duration: 200 }}
|
config={{ duration: 200 }}
|
||||||
>
|
>
|
||||||
{(style, show) => show && (
|
{(animationStyle, show) =>
|
||||||
<Animations.animated.div style={style} className={className}>
|
show && (
|
||||||
{Sidebar}
|
<Animations.animated.div style={{ ...animationStyle, ...barStyle }}>
|
||||||
</Animations.animated.div>
|
{Sidebar}
|
||||||
)}
|
</Animations.animated.div>
|
||||||
|
)
|
||||||
|
}
|
||||||
</Animations.Transition>
|
</Animations.Transition>
|
||||||
);
|
);
|
||||||
}, { noop: true });
|
}, { noop: true });
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
.vc-bf-folder-sidebar [class*="wrapper-"] > [class*="listItem-"]:first-of-type,
|
|
||||||
.vc-bf-folder-sidebar [class*="unreadMentionsIndicator"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-folder-sidebar [class*="expandedFolderBackground-"] {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-folder-sidebar {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-fullscreen {
|
|
||||||
width: 0 !important;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "./betterFolders.css";
|
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
|
||||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
|
||||||
|
|
||||||
import FolderSideBar from "./FolderSideBar";
|
|
||||||
|
|
||||||
const GuildsTree = findLazy(m => m.prototype?.convertToFolder);
|
|
||||||
const GuildFolderStore = findStoreLazy("SortedGuildStore");
|
|
||||||
const ExpandedFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
|
||||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
|
||||||
sidebar: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Display servers from folder on dedicated sidebar",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
sidebarAnim: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Animate opening the folder sidebar",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
closeAllFolders: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close all folders when selecting a server not in a folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
closeAllHomeButton: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close all folders when clicking on the home button",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
closeOthers: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close other folders when opening a folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
forceOpen: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Force a folder to open when switching to a server of that folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "BetterFolders",
|
|
||||||
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
|
|
||||||
authors: [Devs.juby, Devs.AutumnVN],
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: '("guildsnav")',
|
|
||||||
predicate: () => settings.store.sidebar,
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /(\i)\(\){return \i\(\(0,\i\.jsx\)\("div",{className:\i\(\)\.guildSeparator}\)\)}/,
|
|
||||||
replace: "$&$self.Separator=$1;"
|
|
||||||
},
|
|
||||||
|
|
||||||
// Folder component patch
|
|
||||||
{
|
|
||||||
match: /\i\(\(function\(\i,\i,\i\){var \i=\i\.key;return.+\(\i\)},\i\)}\)\)/,
|
|
||||||
replace: "arguments[0].bfHideServers?null:$&"
|
|
||||||
},
|
|
||||||
|
|
||||||
// BEGIN Guilds component patch
|
|
||||||
{
|
|
||||||
match: /(\i)\.themeOverride,(.{15,25}\(function\(\){var \i=)(\i\.\i\.getGuildsTree\(\))/,
|
|
||||||
replace: "$1.themeOverride,bfPatch=$1.bfGuildFolders,$2bfPatch?$self.getGuildsTree(bfPatch,$3):$3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /return(\(0,\i\.jsx\))(\(\i,{)(folderNode:\i,setNodeRef:\i\.setNodeRef,draggable:!0,.+},\i\.id\));case/,
|
|
||||||
replace: "var bfHideServers=typeof bfPatch==='undefined',folder=$1$2bfHideServers,$3;return !bfHideServers&&arguments[1]?[$1($self.Separator,{}),folder]:folder;case"
|
|
||||||
},
|
|
||||||
// END
|
|
||||||
|
|
||||||
{
|
|
||||||
match: /\("guildsnav"\);return\(0,\i\.jsx\)\(.{1,6},{navigator:\i,children:\(0,\i\.jsx\)\(/,
|
|
||||||
replace: "$&$self.Guilds="
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "APPLICATION_LIBRARY,render",
|
|
||||||
predicate: () => settings.store.sidebar,
|
|
||||||
replacement: {
|
|
||||||
match: /(\(0,\i\.jsx\))\(\i\..,{className:\i\(\)\.guilds,themeOverride:\i}\)/,
|
|
||||||
replace: "$&,$1($self.FolderSideBar,{})"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: '("guildsnav")',
|
|
||||||
predicate: () => settings.store.closeAllHomeButton,
|
|
||||||
replacement: {
|
|
||||||
match: ",onClick:function(){if(!__OVERLAY__){",
|
|
||||||
replace: "$&$self.closeFolders();"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
settings,
|
|
||||||
|
|
||||||
start() {
|
|
||||||
const getGuildFolder = (id: string) => GuildFolderStore.getGuildFolders().find(f => f.guildIds.includes(id));
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("CHANNEL_SELECT", this.onSwitch = data => {
|
|
||||||
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.lastGuildId !== data.guildId) {
|
|
||||||
this.lastGuildId = data.guildId;
|
|
||||||
|
|
||||||
const guildFolder = getGuildFolder(data.guildId);
|
|
||||||
if (guildFolder?.folderId) {
|
|
||||||
if (settings.store.forceOpen && !ExpandedFolderStore.isFolderExpanded(guildFolder.folderId))
|
|
||||||
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
|
|
||||||
} else if (settings.store.closeAllFolders)
|
|
||||||
this.closeFolders();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder = e => {
|
|
||||||
if (settings.store.closeOthers && !this.dispatching)
|
|
||||||
FluxDispatcher.wait(() => {
|
|
||||||
const expandedFolders = ExpandedFolderStore.getExpandedFolders();
|
|
||||||
if (expandedFolders.size > 1) {
|
|
||||||
this.dispatching = true;
|
|
||||||
|
|
||||||
for (const id of expandedFolders) if (id !== e.folderId)
|
|
||||||
FolderUtils.toggleGuildFolderExpand(id);
|
|
||||||
|
|
||||||
this.dispatching = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onSwitch);
|
|
||||||
FluxDispatcher.unsubscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder);
|
|
||||||
},
|
|
||||||
|
|
||||||
FolderSideBar,
|
|
||||||
|
|
||||||
getGuildsTree(folders, oldTree) {
|
|
||||||
const tree = new GuildsTree();
|
|
||||||
tree.root.children = oldTree.root.children.filter(e => folders.includes(e.id));
|
|
||||||
tree.nodes = folders.map(id => oldTree.nodes[id]);
|
|
||||||
return tree;
|
|
||||||
},
|
|
||||||
|
|
||||||
closeFolders() {
|
|
||||||
for (const id of ExpandedFolderStore.getExpandedFolders())
|
|
||||||
FolderUtils.toggleGuildFolderExpand(id);
|
|
||||||
},
|
|
||||||
});
|
|
307
src/plugins/betterFolders/index.tsx
Normal file
307
src/plugins/betterFolders/index.tsx
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { proxyLazy } from "@utils/lazy";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { findByProps, findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
|
import { FluxDispatcher, i18n } from "@webpack/common";
|
||||||
|
|
||||||
|
import FolderSideBar from "./FolderSideBar";
|
||||||
|
|
||||||
|
enum FolderIconDisplay {
|
||||||
|
Never,
|
||||||
|
Always,
|
||||||
|
MoreThanOneFolderExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
const GuildsTree = proxyLazy(() => findByProps("GuildsTree").GuildsTree);
|
||||||
|
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||||
|
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||||
|
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||||
|
|
||||||
|
let lastGuildId = null as string | null;
|
||||||
|
let dispatchingFoldersClose = false;
|
||||||
|
|
||||||
|
function getGuildFolder(id: string) {
|
||||||
|
return SortedGuildStore.getGuildFolders().find(folder => folder.guildIds.includes(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFolders() {
|
||||||
|
for (const id of ExpandedGuildFolderStore.getExpandedFolders())
|
||||||
|
FolderUtils.toggleGuildFolderExpand(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const settings = definePluginSettings({
|
||||||
|
sidebar: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Display servers from folder on dedicated sidebar",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
sidebarAnim: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Animate opening the folder sidebar",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
closeAllFolders: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close all folders when selecting a server not in a folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closeAllHomeButton: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close all folders when clicking on the home button",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closeOthers: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close other folders when opening a folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
forceOpen: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Force a folder to open when switching to a server of that folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
keepIcons: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Keep showing guild icons in the primary guild bar folder when it's open in the BetterFolders sidebar",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showFolderIcon: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: "Show the folder icon above the folder guilds in the BetterFolders sidebar",
|
||||||
|
options: [
|
||||||
|
{ label: "Never", value: FolderIconDisplay.Never },
|
||||||
|
{ label: "Always", value: FolderIconDisplay.Always, default: true },
|
||||||
|
{ label: "When more than one folder is expanded", value: FolderIconDisplay.MoreThanOneFolderExpanded }
|
||||||
|
],
|
||||||
|
restartNeeded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "BetterFolders",
|
||||||
|
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
|
||||||
|
authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '("guildsnav")',
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: [
|
||||||
|
// Create the isBetterFolders variable in the GuildsBar component
|
||||||
|
{
|
||||||
|
match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
|
||||||
|
replace: ",isBetterFolders"
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
|
||||||
|
{
|
||||||
|
match: /(useStateFromStoresArray\).{0,25}let \i)=(\i\.\i.getGuildsTree\(\))/,
|
||||||
|
replace: (_, rest, guildsTree) => `${rest}=$self.getGuildTree(!!arguments[0].isBetterFolders,${guildsTree},arguments[0].betterFoldersExpandedIds)`
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
|
||||||
|
{
|
||||||
|
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/,
|
||||||
|
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0].isBetterFolders))"
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
|
||||||
|
{
|
||||||
|
match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/,
|
||||||
|
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
|
||||||
|
},
|
||||||
|
// Export the isBetterFolders variable to the folders component
|
||||||
|
{
|
||||||
|
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
|
||||||
|
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// This is the parent folder component
|
||||||
|
find: ".MAX_GUILD_FOLDER_NAME_LENGTH,",
|
||||||
|
predicate: () => settings.store.sidebar && settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
// Modify the expanded state to instead return the list of expanded folders
|
||||||
|
match: /(useStateFromStores\).{0,20}=>)(\i\.\i)\.isFolderExpanded\(\i\)/,
|
||||||
|
replace: (_, rest, ExpandedGuildFolderStore) => `${rest}${ExpandedGuildFolderStore}.getExpandedFolders()`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds
|
||||||
|
// Also export the list of expanded folders to the child folder component if the patch above succeeds, else export undefined
|
||||||
|
match: /(?<=folderNode:(\i),expanded:)\i(?=,)/,
|
||||||
|
replace: (isExpandedOrExpandedIds, folderNote) => ""
|
||||||
|
+ `typeof ${isExpandedOrExpandedIds}==="boolean"?${isExpandedOrExpandedIds}:${isExpandedOrExpandedIds}.has(${folderNote}.id),`
|
||||||
|
+ `betterFoldersExpandedIds:${isExpandedOrExpandedIds} instanceof Set?${isExpandedOrExpandedIds}:void 0`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".FOLDER_ITEM_GUILD_ICON_MARGIN);",
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: [
|
||||||
|
// We use arguments[0] to access the isBetterFolders variable in this nested folder component (the parent exports all the props so we don't have to patch it)
|
||||||
|
|
||||||
|
// If we are rendering the normal GuildsBar sidebar, we make Discord think the folder is always collapsed to show better icons (the mini guild icons) and avoid transitions
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.keepIcons,
|
||||||
|
match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i),.+?;)(?=let)/,
|
||||||
|
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0].isBetterFolders&&${isExpanded};`
|
||||||
|
},
|
||||||
|
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
||||||
|
{
|
||||||
|
predicate: () => !settings.store.keepIcons,
|
||||||
|
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/,
|
||||||
|
replace: "!!arguments[0].isBetterFolders&&"
|
||||||
|
},
|
||||||
|
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
||||||
|
{
|
||||||
|
predicate: () => !settings.store.keepIcons,
|
||||||
|
match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/,
|
||||||
|
replace: (m, isExpanded) => `${m}!arguments[0].isBetterFolders&&${isExpanded}?null:`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
||||||
|
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||||
|
match: /(?<=\.wrapper,children:\[)/,
|
||||||
|
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)&&"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
|
||||||
|
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||||
|
match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/,
|
||||||
|
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)?null:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "APPLICATION_LIBRARY,render",
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: {
|
||||||
|
// Render the Better Folders sidebar
|
||||||
|
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
|
||||||
|
replace: ",$self.FolderSideBar($1)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.DISCODO_DISABLED",
|
||||||
|
predicate: () => settings.store.closeAllHomeButton,
|
||||||
|
replacement: {
|
||||||
|
// Close all folders when clicking the home button
|
||||||
|
match: /(?<=onClick:\(\)=>{)(?=.{0,200}"discodo")/,
|
||||||
|
replace: "$self.closeFolders();"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
flux: {
|
||||||
|
CHANNEL_SELECT(data) {
|
||||||
|
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lastGuildId !== data.guildId) {
|
||||||
|
lastGuildId = data.guildId;
|
||||||
|
const guildFolder = getGuildFolder(data.guildId);
|
||||||
|
|
||||||
|
if (guildFolder?.folderId) {
|
||||||
|
if (settings.store.forceOpen && !ExpandedGuildFolderStore.isFolderExpanded(guildFolder.folderId)) {
|
||||||
|
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
|
||||||
|
}
|
||||||
|
} else if (settings.store.closeAllFolders) {
|
||||||
|
closeFolders();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
TOGGLE_GUILD_FOLDER_EXPAND(data) {
|
||||||
|
if (settings.store.closeOthers && !dispatchingFoldersClose) {
|
||||||
|
dispatchingFoldersClose = true;
|
||||||
|
|
||||||
|
FluxDispatcher.wait(() => {
|
||||||
|
const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders();
|
||||||
|
|
||||||
|
if (expandedFolders.size > 1) {
|
||||||
|
for (const id of expandedFolders) if (id !== data.folderId)
|
||||||
|
FolderUtils.toggleGuildFolderExpand(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchingFoldersClose = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getGuildTree(isBetterFolders: boolean, oldTree: any, expandedFolderIds?: Set<any>) {
|
||||||
|
if (!isBetterFolders || expandedFolderIds == null) return oldTree;
|
||||||
|
|
||||||
|
const newTree = new GuildsTree();
|
||||||
|
// Children is every folder and guild which is not in a folder, this filters out only the expanded folders
|
||||||
|
newTree.root.children = oldTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id));
|
||||||
|
// Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them
|
||||||
|
newTree.nodes = Object.fromEntries(
|
||||||
|
Object.entries(oldTree.nodes)
|
||||||
|
.filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId))
|
||||||
|
);
|
||||||
|
|
||||||
|
return newTree;
|
||||||
|
},
|
||||||
|
|
||||||
|
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||||
|
return child => {
|
||||||
|
if (isBetterFolders) {
|
||||||
|
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||||
|
return child => {
|
||||||
|
if (isBetterFolders) {
|
||||||
|
return "onScroll" in child.props;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldShowFolderIconAndBackground(isBetterFolders: boolean, expandedFolderIds?: Set<any>) {
|
||||||
|
if (!isBetterFolders) return true;
|
||||||
|
|
||||||
|
switch (settings.store.showFolderIcon) {
|
||||||
|
case FolderIconDisplay.Never:
|
||||||
|
return false;
|
||||||
|
case FolderIconDisplay.Always:
|
||||||
|
return true;
|
||||||
|
case FolderIconDisplay.MoreThanOneFolderExpanded:
|
||||||
|
return (expandedFolderIds?.size ?? 0) > 1;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
|
||||||
|
|
||||||
|
closeFolders
|
||||||
|
});
|
@ -34,16 +34,18 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".embedGallerySide",
|
find: ".Messages.GIF,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/,
|
match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
|
||||||
replace:
|
replace:
|
||||||
"?($1.alt='GIF',$self.altify($1))",
|
// rename prop so we can always use default value
|
||||||
|
"alt_$$:$1=$self.altify($3)||$2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
altify(props: any) {
|
altify(props: any) {
|
||||||
|
props.alt ??= "GIF";
|
||||||
if (props.alt !== "GIF") return props.alt;
|
if (props.alt !== "GIF") return props.alt;
|
||||||
|
|
||||||
let url: string = props.original || props.src;
|
let url: string = props.original || props.src;
|
||||||
|
@ -32,10 +32,16 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "hideNote:",
|
find: "hideNote:",
|
||||||
all: true,
|
all: true,
|
||||||
|
// Some modules match the find but the replacement is returned untouched
|
||||||
|
noWarn: true,
|
||||||
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
|
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /hideNote:.+?(?=[,}])/g,
|
match: /hideNote:.+?(?=([,}].*?\)))/g,
|
||||||
replace: "hideNote:true",
|
replace: (m, rest) => {
|
||||||
|
const destructuringMatch = rest.match(/}=.+/);
|
||||||
|
if (destructuringMatch == null) return "hideNote:!0";
|
||||||
|
return m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,8 +54,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".Messages.NOTE}",
|
find: ".Messages.NOTE}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\i)\.hideNote\?null/,
|
match: /(?<=return \i\?)null(?=:\(0,\i\.jsxs)/,
|
||||||
replace: "$1.hideNote?$self.patchPadding($1)"
|
replace: "$self.patchPadding(arguments[0])"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -38,6 +38,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '"dot"===',
|
find: '"dot"===',
|
||||||
all: true,
|
all: true,
|
||||||
|
noWarn: true,
|
||||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||||
|
@ -29,9 +29,8 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
// Discord merges multiple props here with Object.assign()
|
// Discord merges multiple props here with Object.assign()
|
||||||
// This patch passes a third object to it with which we override onClick and onContextMenu
|
// This patch passes a third object to it with which we override onClick and onContextMenu
|
||||||
match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0)\},(.{1,3})\)/,
|
match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0),\.\.\.(\i),/,
|
||||||
replace: (m, onDblClick, otherProps) =>
|
replace: "$&onClick:$1,onContextMenu:$2.onClick,",
|
||||||
`${m.slice(0, -1)},{onClick:${onDblClick},onContextMenu:${otherProps}.onClick})`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -45,11 +45,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".embedWrapper,embed",
|
find: ".embedWrapper,embed",
|
||||||
replacement: [{
|
replacement: [{
|
||||||
match: /(\.renderEmbed=.+?(.)=.\.props)(.+?\.embedWrapper)/g,
|
match: /\.embedWrapper/g,
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$&+(this.props.channel.nsfw?' vc-nsfw-img':'')"
|
||||||
}, {
|
|
||||||
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')"
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -73,9 +73,9 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".renderConnectionStatus=",
|
find: "renderConnectionStatus(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=renderConnectionStatus=.+\.channel,children:)\w/,
|
match: /(?<=renderConnectionStatus\(\)\{.+\.channel,children:)\i/,
|
||||||
replace: "[$&, $self.renderTimer(this.props.channel.id)]"
|
replace: "[$&, $self.renderTimer(this.props.channel.id)]"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
@ -34,8 +34,9 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".AVATAR_STATUS_MOBILE_16;",
|
find: ".AVATAR_STATUS_MOBILE_16;",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\.fromIsMobile,.+?)\i.status/,
|
match: /(?<=fromIsMobile:\i=!0,.+?)status:(\i)/,
|
||||||
replace: (_, rest) => `${rest}"online"`
|
// Rename field to force it to always use "online"
|
||||||
|
replace: 'status_$:$1="online"'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -22,23 +22,16 @@ import { Devs } from "@utils/constants";
|
|||||||
import { isTruthy } from "@utils/guards";
|
import { isTruthy } from "@utils/guards";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
const ActivityComponent = findByCodeLazy("onOpenGameProfile");
|
const ActivityComponent = findByCodeLazy("onOpenGameProfile");
|
||||||
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
||||||
const Colors = findByPropsLazy("profileColors");
|
const Colors = findByPropsLazy("profileColors");
|
||||||
|
|
||||||
const assetManager = mapMangledModuleLazy(
|
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
||||||
return (await assetManager.getAsset(settings.store.appID, [key, undefined]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActivityAssets {
|
interface ActivityAssets {
|
||||||
@ -403,7 +396,7 @@ export default definePlugin({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
Go to <Link href="https://discord.com/developers/applications">Discord Deverloper Portal</Link> to create an application and
|
Go to <Link href="https://discord.com/developers/applications">Discord Developer Portal</Link> to create an application and
|
||||||
get the application ID.
|
get the application ID.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
|
@ -63,7 +63,7 @@ async function embedDidMount(this: Component<Props>) {
|
|||||||
embed.rawTitle = titles[0].title;
|
embed.rawTitle = titles[0].title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thumbnails[0]?.votes >= 0) {
|
if (thumbnails[0]?.votes >= 0 && thumbnails[0].timestamp) {
|
||||||
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}`;
|
||||||
}
|
}
|
||||||
@ -147,8 +147,8 @@ export default definePlugin({
|
|||||||
replacement: [
|
replacement: [
|
||||||
// patch componentDidMount to replace embed thumbnail and title
|
// patch componentDidMount to replace embed thumbnail and title
|
||||||
{
|
{
|
||||||
match: /(\i).render=function.{0,50}\i\.embed/,
|
match: /render\(\)\{let\{embed:/,
|
||||||
replace: "$1.componentDidMount=$self.embedDidMount,$&"
|
replace: "componentDidMount=$self.embedDidMount;$&"
|
||||||
},
|
},
|
||||||
|
|
||||||
// add dearrow button
|
// add dearrow button
|
||||||
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=function \i\(\){)(?=.{1,100}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/,
|
match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/,
|
||||||
replace: "return;"
|
replace: "return;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { Promisable } from "type-fest";
|
import { Promisable } from "type-fest";
|
||||||
|
|
||||||
const StickersStore = findStoreLazy("StickersStore");
|
const StickersStore = findStoreLazy("StickersStore");
|
||||||
const uploadEmoji = findByCodeLazy('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(");
|
const EmojiManager = findByPropsLazy("fetchEmoji", "uploadEmoji", "deleteEmoji");
|
||||||
|
|
||||||
interface Sticker {
|
interface Sticker {
|
||||||
t: "Sticker";
|
t: "Sticker";
|
||||||
@ -106,7 +106,7 @@ async function cloneEmoji(guildId: string, emoji: Emoji) {
|
|||||||
reader.readAsDataURL(data);
|
reader.readAsDataURL(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
return uploadEmoji({
|
return EmojiManager.uploadEmoji({
|
||||||
guildId,
|
guildId,
|
||||||
name: emoji.name.split("~")[0],
|
name: emoji.name.split("~")[0],
|
||||||
image: dataUrl
|
image: dataUrl
|
||||||
@ -155,10 +155,15 @@ async function doClone(guildId: string, data: Sticker | Emoji) {
|
|||||||
type: Toasts.Type.SUCCESS,
|
type: Toasts.Type.SUCCESS,
|
||||||
id: Toasts.genId()
|
id: Toasts.genId()
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
|
let message = "Something went wrong (check console!)";
|
||||||
|
try {
|
||||||
|
message = JSON.parse(e.text).message;
|
||||||
|
} catch { }
|
||||||
|
|
||||||
new Logger("EmoteCloner").error("Failed to clone", data.name, "to", guildId, e);
|
new Logger("EmoteCloner").error("Failed to clone", data.name, "to", guildId, e);
|
||||||
Toasts.show({
|
Toasts.show({
|
||||||
message: "Oopsie something went wrong :( Check console!!!",
|
message: "Failed to clone: " + message,
|
||||||
type: Toasts.Type.FAILURE,
|
type: Toasts.Type.FAILURE,
|
||||||
id: Toasts.genId()
|
id: Toasts.genId()
|
||||||
});
|
});
|
||||||
|
@ -52,7 +52,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Object.defineProperties(this,{isDeveloper",
|
find: "Object.defineProperties(this,{isDeveloper",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<={isDeveloper:\{[^}]+?,get:function\(\)\{return )\w/,
|
match: /(?<={isDeveloper:\{[^}]+?,get:\(\)=>)\i/,
|
||||||
replace: "true"
|
replace: "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -64,26 +64,26 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".isStaff=function(){",
|
find: ".isStaff=()",
|
||||||
predicate: () => settings.store.enableIsStaff,
|
predicate: () => settings.store.enableIsStaff,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /return\s*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/,
|
match: /=>*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/,
|
||||||
replace: (_, user, flags) => `return Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}`
|
replace: (_, user, flags) => `=>Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /hasFreePremium=function\(\){return this.isStaff\(\)\s*?\|\|/,
|
match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/,
|
||||||
replace: "hasFreePremium=function(){return ",
|
replace: "hasFreePremium(){return ",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// Fix search history being disabled / broken with isStaff
|
// Fix search history being disabled / broken with isStaff
|
||||||
{
|
{
|
||||||
find: 'get("disable_new_search")',
|
find: '("showNewSearch")',
|
||||||
predicate: () => settings.store.enableIsStaff,
|
predicate: () => settings.store.enableIsStaff,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=showNewSearch"\);return)\s?!/,
|
match: /(?<=showNewSearch"\);return)\s?/,
|
||||||
replace: "!1&&!"
|
replace: "!1&&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -24,35 +24,33 @@ import { getCurrentGuild } from "@utils/discord";
|
|||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, EmojiStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
|
import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||||
import type { Message } from "discord-types/general";
|
import type { Message } from "discord-types/general";
|
||||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
import type { ReactElement, ReactNode } from "react";
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
|
||||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
|
||||||
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
|
||||||
const ReaderFactory = findByPropsLazy("readerFactory");
|
|
||||||
const StickerStore = findStoreLazy("StickersStore") as {
|
const StickerStore = findStoreLazy("StickersStore") as {
|
||||||
getPremiumPacks(): StickerPack[];
|
getPremiumPacks(): StickerPack[];
|
||||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||||
getStickerById(id: string): Sticker | undefined;
|
getStickerById(id: string): Sticker | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
function searchProtoClass(localName: string, parentProtoClass: any) {
|
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||||
if (!parentProtoClass) return;
|
const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS");
|
||||||
|
|
||||||
const field = parentProtoClass.fields.find(field => field.localName === localName);
|
function searchProtoClassField(localName: string, protoClass: any) {
|
||||||
|
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
|
||||||
if (!field) return;
|
if (!field) return;
|
||||||
|
|
||||||
const getter: any = Object.values(field).find(value => typeof value === "function");
|
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
|
||||||
return getter?.();
|
return fieldGetter?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppearanceSettingsProto = proxyLazy(() => searchProtoClass("appearance", PreloadedUserSettingsProtoHandler.ProtoClass));
|
const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||||
const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSettings", AppearanceSettingsProto));
|
const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||||
|
const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||||
|
|
||||||
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
||||||
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
||||||
@ -176,39 +174,46 @@ export default definePlugin({
|
|||||||
predicate: () => settings.store.enableEmojiBypass,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=(\i)=\i\.intention)/,
|
// Create a variable for the intention of listing the emoji
|
||||||
replace: (_, intention) => `,fakeNitroIntention=${intention}`
|
match: /(?<=,intention:(\i).+?;)/,
|
||||||
|
replace: (_, intention) => `let fakeNitroIntention=${intention};`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Send the intention of listing the emoji to the nitro permission check functions
|
||||||
match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
|
match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
|
||||||
replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
|
replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Disallow the emoji if the intention doesn't allow it
|
||||||
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
|
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
|
||||||
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
|
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Make the emoji always available if the intention allows it
|
||||||
match: /if\(!\i\.available/,
|
match: /if\(!\i\.available/,
|
||||||
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
|
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Allow emojis and animated emojis to be sent everywhere
|
||||||
{
|
{
|
||||||
find: "canUseAnimatedEmojis:function",
|
find: "canUseAnimatedEmojis:function",
|
||||||
predicate: () => settings.store.enableEmojiBypass,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
|
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))(?=})/g,
|
||||||
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 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;"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Make stickers always available
|
||||||
{
|
{
|
||||||
find: "\"SENDABLE\"",
|
find: "\"SENDABLE\"",
|
||||||
predicate: () => settings.store.enableStickerBypass,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
@ -217,6 +222,7 @@ export default definePlugin({
|
|||||||
replace: "true?"
|
replace: "true?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Allow streaming with high quality
|
||||||
{
|
{
|
||||||
find: "canUseHighVideoUploadQuality:function",
|
find: "canUseHighVideoUploadQuality:function",
|
||||||
predicate: () => settings.store.enableStreamQualityBypass,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
@ -230,14 +236,16 @@ export default definePlugin({
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// Remove boost requirements to stream with high quality
|
||||||
{
|
{
|
||||||
find: "STREAM_FPS_OPTION.format",
|
find: "STREAM_FPS_OPTION.format",
|
||||||
predicate: () => settings.store.enableStreamQualityBypass,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g,
|
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
|
||||||
replace: ""
|
replace: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Allow client themes to be changeable
|
||||||
{
|
{
|
||||||
find: "canUseClientThemes:function",
|
find: "canUseClientThemes:function",
|
||||||
replacement: {
|
replacement: {
|
||||||
@ -249,19 +257,22 @@ export default definePlugin({
|
|||||||
find: '.displayName="UserSettingsProtoStore"',
|
find: '.displayName="UserSettingsProtoStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Overwrite incoming connection settings proto with our local settings
|
||||||
match: /CONNECTION_OPEN:function\((\i)\){/,
|
match: /CONNECTION_OPEN:function\((\i)\){/,
|
||||||
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /=(\i)\.local;/,
|
// Overwrite non local proto changes with our local settings
|
||||||
replace: (m, props) => `${m}${props}.local||$self.handleProtoChange(${props}.settings.proto);`
|
match: /let{settings:/,
|
||||||
|
replace: "arguments[0].local||$self.handleProtoChange(arguments[0].settings.proto);$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Call our function to handle changing the gradient theme when selecting a new one
|
||||||
{
|
{
|
||||||
find: "updateTheme:function",
|
find: ",updateTheme(",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)(\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\))/,
|
match: /(function \i\(\i\){let{backgroundGradientPresetId:(\i).+?)(\i\.\i\.updateAsync.+?theme=(.+?),.+?},\i\))/,
|
||||||
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -269,11 +280,13 @@ export default definePlugin({
|
|||||||
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Call our function to decide whether the emoji link should be kept or not
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
||||||
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
|
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Patch the rendered message content to add fake nitro emojis or remove sticker links
|
||||||
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
||||||
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
|
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
|
||||||
@ -281,36 +294,41 @@ export default definePlugin({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "renderEmbeds=function",
|
find: "renderEmbeds(",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Call our function to decide whether the embed should be ignored or not
|
||||||
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
match: /(renderEmbeds=function\((\i)\){)(.+?embeds\.map\(\(function\((\i)\){)/,
|
match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/,
|
||||||
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
|
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Patch the stickers array to add fake nitro stickers
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
match: /renderStickersAccessories=function\((\i)\){var (\i)=\(0,\i\.\i\)\(\i\),/,
|
match: /(?<=renderStickersAccessories\((\i)\){let (\i)=\(0,\i\.\i\)\(\i\).+?;)/,
|
||||||
replace: (m, message, stickers) => `${m}${stickers}=$self.patchFakeNitroStickers(${stickers},${message}),`
|
replace: (_, message, stickers) => `${stickers}=$self.patchFakeNitroStickers(${stickers},${message});`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Filter attachments to remove fake nitro stickers or emojis
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
|
match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
|
||||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".STICKER_IN_MESSAGE_HOVER,",
|
find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format",
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /var (\i)=\i\.renderableSticker,.{0,50}closePopout.+?channel:\i,closePopout:\i,/,
|
// Export the renderable sticker to be used in the fake nitro sticker notice
|
||||||
replace: (m, renderableSticker) => `${m}renderableSticker:${renderableSticker},`
|
match: /let{renderableSticker:(\i).{0,250}isGuildSticker.+?channel:\i,/,
|
||||||
|
replace: (m, renderableSticker) => `${m}fakeNitroRenderableSticker:${renderableSticker},`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
// Add the fake nitro sticker notice
|
||||||
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!${props}.renderableSticker?.fake)`
|
match: /(let \i,{sticker:\i,channel:\i,closePopout:\i.+?}=(\i).+?;)(.+?description:)(\i)(?=,sticker:\i)/,
|
||||||
|
replace: (_, rest, props, rest2, reactNode) => `${rest}let{fakeNitroRenderableSticker}=${props};${rest2}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!fakeNitroRenderableSticker?.fake)`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -318,7 +336,8 @@ export default definePlugin({
|
|||||||
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
|
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<=(\i)=\i\.node.+?)/,
|
// Export the emoji node to be used in the fake nitro emoji notice
|
||||||
|
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<={node:(\i),.+?)/,
|
||||||
replace: (m, node) => `${m}fakeNitroNode:${node},`
|
replace: (m, node) => `${m}fakeNitroNode:${node},`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -326,8 +345,25 @@ export default definePlugin({
|
|||||||
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/,
|
// Add the fake nitro emoji notice
|
||||||
replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)`
|
match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
|
||||||
|
replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Allow using custom app icons
|
||||||
|
{
|
||||||
|
find: "canUsePremiumAppIcons:function",
|
||||||
|
replacement: {
|
||||||
|
match: /canUsePremiumAppIcons:function\(\i\){/,
|
||||||
|
replace: "$&return true;"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Separate patch for allowing using custom app icons
|
||||||
|
{
|
||||||
|
find: "location:\"AppIconHome\"",
|
||||||
|
replacement: {
|
||||||
|
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
|
||||||
|
replace: "true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -345,26 +381,30 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleProtoChange(proto: any, user: any) {
|
handleProtoChange(proto: any, user: any) {
|
||||||
if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || (!proto.appearance && !AppearanceSettingsProto)) return;
|
if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || !PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators) return;
|
||||||
|
|
||||||
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
|
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
|
|
||||||
if (premiumType !== 2) {
|
if (premiumType !== 2) {
|
||||||
proto.appearance ??= AppearanceSettingsProto.create();
|
proto.appearance ??= AppearanceSettingsActionCreators.create();
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
||||||
proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme;
|
const appearanceSettingsDummy = AppearanceSettingsActionCreators.create({
|
||||||
|
theme: UserSettingsProtoStore.settings.appearance.theme
|
||||||
|
});
|
||||||
|
|
||||||
|
proto.appearance.theme = appearanceSettingsDummy.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null && ClientThemeSettingsProto) {
|
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) {
|
||||||
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
|
||||||
backgroundGradientPresetId: {
|
backgroundGradientPresetId: {
|
||||||
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummy;
|
||||||
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -373,26 +413,26 @@ export default definePlugin({
|
|||||||
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
|
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
if (premiumType === 2 || backgroundGradientPresetId == null) return original();
|
if (premiumType === 2 || backgroundGradientPresetId == null) return original();
|
||||||
|
|
||||||
if (!AppearanceSettingsProto || !ClientThemeSettingsProto || !ReaderFactory) return;
|
if (!PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators || !ProtoUtils) return;
|
||||||
|
|
||||||
const currentAppearanceProto = PreloadedUserSettingsProtoHandler.getCurrentValue().appearance;
|
const currentAppearanceSettings = PreloadedUserSettingsActionCreators.getCurrentValue().appearance;
|
||||||
|
|
||||||
const newAppearanceProto = currentAppearanceProto != null
|
const newAppearanceProto = currentAppearanceSettings != null
|
||||||
? AppearanceSettingsProto.fromBinary(AppearanceSettingsProto.toBinary(currentAppearanceProto), ReaderFactory)
|
? AppearanceSettingsActionCreators.fromBinary(AppearanceSettingsActionCreators.toBinary(currentAppearanceSettings), ProtoUtils.BINARY_READ_OPTIONS)
|
||||||
: AppearanceSettingsProto.create();
|
: AppearanceSettingsActionCreators.create();
|
||||||
|
|
||||||
newAppearanceProto.theme = theme;
|
newAppearanceProto.theme = theme;
|
||||||
|
|
||||||
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
|
||||||
backgroundGradientPresetId: {
|
backgroundGradientPresetId: {
|
||||||
value: backgroundGradientPresetId
|
value: backgroundGradientPresetId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummy;
|
||||||
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
|
||||||
|
|
||||||
const proto = PreloadedUserSettingsProtoHandler.ProtoClass.create();
|
const proto = PreloadedUserSettingsActionCreators.ProtoClass.create();
|
||||||
proto.appearance = newAppearanceProto;
|
proto.appearance = newAppearanceProto;
|
||||||
|
|
||||||
FluxDispatcher.dispatch({
|
FluxDispatcher.dispatch({
|
||||||
@ -518,7 +558,7 @@ export default definePlugin({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return modifyChildren(window._.cloneDeep(content));
|
return modifyChildren(lodash.cloneDeep(content));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
new Logger("FakeNitro").error(err);
|
new Logger("FakeNitro").error(err);
|
||||||
return content;
|
return content;
|
||||||
@ -715,7 +755,7 @@ export default definePlugin({
|
|||||||
gif.finish();
|
gif.finish();
|
||||||
|
|
||||||
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
||||||
promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
||||||
},
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
@ -87,15 +87,15 @@ export default definePlugin({
|
|||||||
authors: [Devs.Alyxia, Devs.Remty],
|
authors: [Devs.Alyxia, Devs.Remty],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "getUserProfile=",
|
find: "UserProfileStore",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=getUserProfile=function\(\i\){return )(\i\[\i\])/,
|
match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/,
|
||||||
replace: "$self.colorDecodeHook($1)"
|
replace: "$self.colorDecodeHook($1)"
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
find: ".USER_SETTINGS_PROFILE_THEME_ACCENT",
|
find: ".USER_SETTINGS_PROFILE_THEME_ACCENT",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /RESET_PROFILE_THEME}\)(?<=},color:(\i).+?},color:(\i).+?)/,
|
match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||||
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,22 +39,27 @@ export default definePlugin({
|
|||||||
description: "Puts your favorite emoji first in the emoji autocomplete.",
|
description: "Puts your favorite emoji first in the emoji autocomplete.",
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".activeCommandOption",
|
find: "renderResults({results:",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// = someFunc(a.selectedIndex); ...trackEmojiSearch({ state: theState, isInPopoutExperimental: someBool })
|
// https://regex101.com/r/N7kpLM/1
|
||||||
match: /=\i\(\i\.selectedIndex\);(?=.+?state:(\i),isInPopoutExperiment:\i)/,
|
match: /let \i=.{1,100}renderResults\({results:(\i)\.query\.results,/,
|
||||||
// self.sortEmojis(theState)
|
replace: "$self.sortEmojis($1);$&"
|
||||||
replace: "$&$self.sortEmojis($1);"
|
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: "MAX_AUTOCOMPLETE_RESULTS+",
|
||||||
|
replacement: [
|
||||||
// set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10
|
// set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10
|
||||||
// and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later
|
// and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later
|
||||||
{
|
{
|
||||||
|
// https://regex101.com/r/x2mobQ/1
|
||||||
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
||||||
match: /,maxCount:(\i)(.+?)=(\i)\.slice\(0,(\1-\i\.length)\)/,
|
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/,
|
||||||
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
||||||
replace: ",maxCount:Infinity$2=($3.sliceTo=$4,$3)"
|
replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ interface Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchHeader", "searchInput");
|
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow");
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
searchOption: {
|
searchOption: {
|
||||||
@ -91,13 +91,13 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "renderCategoryExtras",
|
find: "renderHeaderContent()",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// https://regex101.com/r/4uHtTE/1
|
// https://regex101.com/r/07gpzP/1
|
||||||
// ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($<searchComp>), {...props}))
|
// ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($<searchComp>), {...props}))
|
||||||
match: /(renderHeaderContent=function.{1,150}FAVORITES:return)(.{1,150};)(case.{1,200}default:return\(0,\i\.jsx\)\((?<searchComp>\i\.\i))/,
|
match: /(renderHeaderContent\(\).{1,150}FAVORITES:return)(.{1,150});(case.{1,200}default:return\(0,\i\.jsx\)\((?<searchComp>\i\..{1,10}),)/,
|
||||||
replace: "$1 this.state.resultType === \"Favorites\" ? $self.renderSearchBar(this, $<searchComp>) : $2; $3"
|
replace: "$1 this.state.resultType === 'Favorites' ? $self.renderSearchBar(this, $<searchComp>) : $2;$3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// to persist filtered favorites when component re-renders.
|
// to persist filtered favorites when component re-renders.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -27,18 +27,17 @@ export default definePlugin({
|
|||||||
authors: [Devs.D3SOX, Devs.Nickyux],
|
authors: [Devs.D3SOX, Devs.Nickyux],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
// This is the logic where it decides whether to render the owner crown or not
|
find: "AVATAR_DECORATION_PADDING:",
|
||||||
find: ".MULTIPLE_AVATAR",
|
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\i)=(\i)\.isOwner,/,
|
match: /,isOwner:(\i),/,
|
||||||
replace: "$1=$self.isGuildOwner($2),"
|
replace: ",_isOwner:$1=$self.isGuildOwner(e),"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) {
|
isGuildOwner(props: { user: User, channel: Channel, isOwner: boolean, guildId?: string; }) {
|
||||||
if (!props?.user?.id) return false;
|
if (!props?.user?.id) return props.isOwner;
|
||||||
if (props.channel?.type === 3 /* GROUP_DM */)
|
if (props.channel?.type === 3 /* GROUP_DM */)
|
||||||
return false;
|
return props.isOwner;
|
||||||
|
|
||||||
// guild id is in props twice, fallback if the first is undefined
|
// guild id is in props twice, fallback if the first is undefined
|
||||||
const guildId = props.guildId ?? props.channel?.guild_id;
|
const guildId = props.guildId ?? props.channel?.guild_id;
|
||||||
|
@ -16,16 +16,15 @@
|
|||||||
* 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 { getSettingStoreLazy } from "@api/SettingsStore";
|
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
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 { findByCodeLazy } from "@webpack";
|
||||||
|
import { StatusSettingsStores } from "@webpack/common";
|
||||||
|
|
||||||
import style from "./style.css?managed";
|
import style from "./style.css?managed";
|
||||||
|
|
||||||
const ShowCurrentGame = getSettingStoreLazy<boolean>("status", "showCurrentGame");
|
|
||||||
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
|
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
|
||||||
|
|
||||||
function makeIcon(showCurrentGame?: boolean) {
|
function makeIcon(showCurrentGame?: boolean) {
|
||||||
@ -40,7 +39,7 @@ function makeIcon(showCurrentGame?: boolean) {
|
|||||||
{!showCurrentGame && <>
|
{!showCurrentGame && <>
|
||||||
<mask id="gameActivityMask" >
|
<mask id="gameActivityMask" >
|
||||||
<rect fill="white" x="0" y="0" width="24" height="24" />
|
<rect fill="white" x="0" y="0" width="24" height="24" />
|
||||||
<path fill="black" d="M23.27 4.54 19.46.73 .73 19.46 4.54 23.27 23.27 4.54Z"/>
|
<path fill="black" d="M23.27 4.54 19.46.73 .73 19.46 4.54 23.27 23.27 4.54Z" />
|
||||||
</mask>
|
</mask>
|
||||||
<path fill="var(--status-danger)" d="M23 2.27 21.73 1 1 21.73 2.27 23 23 2.27Z" />
|
<path fill="var(--status-danger)" d="M23 2.27 21.73 1 1 21.73 2.27 23 23 2.27Z" />
|
||||||
</>}
|
</>}
|
||||||
@ -50,7 +49,7 @@ function makeIcon(showCurrentGame?: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function GameActivityToggleButton() {
|
function GameActivityToggleButton() {
|
||||||
const showCurrentGame = ShowCurrentGame?.useSetting();
|
const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@ -58,7 +57,7 @@ function GameActivityToggleButton() {
|
|||||||
icon={makeIcon(showCurrentGame)}
|
icon={makeIcon(showCurrentGame)}
|
||||||
role="switch"
|
role="switch"
|
||||||
aria-checked={!showCurrentGame}
|
aria-checked={!showCurrentGame}
|
||||||
onClick={() => ShowCurrentGame?.updateSetting(old => !old)}
|
onClick={() => StatusSettingsStores.ShowCurrentGame.updateSetting(old => !old)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,7 +66,6 @@ export default definePlugin({
|
|||||||
name: "GameActivityToggle",
|
name: "GameActivityToggle",
|
||||||
description: "Adds a button next to the mic and deafen button to toggle game activity.",
|
description: "Adds a button next to the mic and deafen button to toggle game activity.",
|
||||||
authors: [Devs.Nuckyz, Devs.RuukuLada],
|
authors: [Devs.Nuckyz, Devs.RuukuLada],
|
||||||
dependencies: ["SettingsStoreAPI"],
|
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[class*="withTagAsButton"] {
|
[class*="withTagAsButton"] {
|
||||||
min-width: 88px;
|
min-width: 88px !important;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ export default definePlugin({
|
|||||||
patches: [{
|
patches: [{
|
||||||
find: ".handleSelectGIF=",
|
find: ".handleSelectGIF=",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.handleSelectGIF=function.+?\{/,
|
match: /\.handleSelectGIF=(\i)=>\{/,
|
||||||
replace: ".handleSelectGIF=function(gif){return $self.handleSelect(gif);"
|
replace: ".handleSelectGIF=$1=>{if (!this.props.className) return $self.handleSelect($1);"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByProps, findByPropsLazy } from "@webpack";
|
||||||
import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common";
|
import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common";
|
||||||
import { Channel, Message } from "discord-types/general";
|
import { Channel, Message } from "discord-types/general";
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ const settings = definePluginSettings({
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const MessageActions = findByPropsLazy("sendGreetMessage");
|
const MessageActions = findByPropsLazy("sendGreetMessage");
|
||||||
|
const WELCOME_STICKERS = proxyLazy(() => findByProps("WELCOME_STICKERS")?.WELCOME_STICKERS);
|
||||||
|
|
||||||
function greet(channel: Channel, message: Message, stickers: string[]) {
|
function greet(channel: Channel, message: Message, stickers: string[]) {
|
||||||
const options = MessageActions.getSendMessageOptionsForReply({
|
const options = MessageActions.getSendMessageOptionsForReply({
|
||||||
@ -75,7 +77,7 @@ function greet(channel: Channel, message: Message, stickers: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], message: Message, channel: Channel; }) {
|
function GreetMenu({ channel, message }: { message: Message, channel: Channel; }) {
|
||||||
const s = settings.use(["greetMode", "multiGreetChoices"]);
|
const s = settings.use(["greetMode", "multiGreetChoices"]);
|
||||||
const { greetMode, multiGreetChoices = [] } = s;
|
const { greetMode, multiGreetChoices = [] } = s;
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], messag
|
|||||||
<Menu.MenuGroup
|
<Menu.MenuGroup
|
||||||
label="Greet Stickers"
|
label="Greet Stickers"
|
||||||
>
|
>
|
||||||
{stickers.map(sticker => (
|
{WELCOME_STICKERS.map(sticker => (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
key={sticker.id}
|
key={sticker.id}
|
||||||
id={"greet-" + sticker.id}
|
id={"greet-" + sticker.id}
|
||||||
@ -123,7 +125,7 @@ function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], messag
|
|||||||
label="Unholy Multi-Greet"
|
label="Unholy Multi-Greet"
|
||||||
id="unholy-multi-greet"
|
id="unholy-multi-greet"
|
||||||
>
|
>
|
||||||
{stickers.map(sticker => {
|
{WELCOME_STICKERS.map(sticker => {
|
||||||
const checked = multiGreetChoices.some(s => s === sticker.id);
|
const checked = multiGreetChoices.some(s => s === sticker.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -168,21 +170,20 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Messages.WELCOME_CTA_LABEL",
|
find: "Messages.WELCOME_CTA_LABEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /innerClassName:\i\(\).welcomeCTAButton,(?<=%\i\.length;return (\i)\[\i\].+?)/,
|
match: /innerClassName:\i\.welcomeCTAButton,(?<={channel:\i,message:\i}=(\i).{0,400}?)/,
|
||||||
replace: "$&onContextMenu:(e)=>$self.pickSticker(e,$1,arguments[0]),"
|
replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1),"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
pickSticker(
|
pickSticker(
|
||||||
event: React.UIEvent,
|
event: React.UIEvent,
|
||||||
stickers: Sticker[],
|
|
||||||
props: {
|
props: {
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
message: Message;
|
message: Message;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (!(props.message as any).deleted)
|
if (!(props.message as any).deleted)
|
||||||
ContextMenu.open(event, () => <GreetMenu stickers={stickers} {...props} />);
|
ContextMenu.open(event, () => <GreetMenu {...props} />);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -25,10 +25,10 @@ export default definePlugin({
|
|||||||
authors: [Devs.botato, Devs.Animal],
|
authors: [Devs.botato, Devs.Animal],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "),{hasFlag:",
|
find: "hasFlag:{writable",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(if\((.{1,2})<=1<<30\)return)/,
|
match: /if\((\i)<=(?:1<<30|1073741824)\)return/,
|
||||||
replace: "if($2===(1<<20)){return false};$1",
|
replace: "if($1===(1<<20))return false;$&",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
import * as DataStore from "@api/DataStore";
|
import * as DataStore from "@api/DataStore";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { getSettingStoreLazy } from "@api/SettingsStore";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { useForceUpdater } from "@utils/react";
|
import { useForceUpdater } from "@utils/react";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findStoreLazy } from "@webpack";
|
import { findStoreLazy } from "@webpack";
|
||||||
import { Tooltip } from "webpack/common";
|
import { StatusSettingsStores, Tooltip } from "webpack/common";
|
||||||
|
|
||||||
const enum ActivitiesTypes {
|
const enum ActivitiesTypes {
|
||||||
Game,
|
Game,
|
||||||
@ -26,7 +25,6 @@ interface IgnoredActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RunningGameStore = findStoreLazy("RunningGameStore");
|
const RunningGameStore = findStoreLazy("RunningGameStore");
|
||||||
const ShowCurrentGame = getSettingStoreLazy<boolean>("status", "showCurrentGame");
|
|
||||||
|
|
||||||
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {
|
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {
|
||||||
const forceUpdate = useForceUpdater();
|
const forceUpdate = useForceUpdater();
|
||||||
@ -68,7 +66,7 @@ function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|||||||
else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex);
|
else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex);
|
||||||
|
|
||||||
// Trigger activities recalculation
|
// Trigger activities recalculation
|
||||||
ShowCurrentGame?.updateSetting(old => old);
|
StatusSettingsStores.ShowCurrentGame.updateSetting(old => old);
|
||||||
forceUpdateButton();
|
forceUpdateButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +83,6 @@ export default definePlugin({
|
|||||||
authors: [Devs.Nuckyz],
|
authors: [Devs.Nuckyz],
|
||||||
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are ignored from the Registered Games and Activities tabs.",
|
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are ignored from the Registered Games and Activities tabs.",
|
||||||
|
|
||||||
dependencies: ["SettingsStoreAPI"],
|
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
@ -93,30 +90,31 @@ export default definePlugin({
|
|||||||
find: '.displayName="LocalActivityStore"',
|
find: '.displayName="LocalActivityStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /LISTENING.+?\)\);(?<=(\i)\.push.+?)/,
|
match: /LISTENING.+?}\),(?<=(\i)\.push.+?)/,
|
||||||
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);`
|
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored),`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY",
|
find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\(\)\.removeGame.+?null(?<=(\i)\?\i=\i\.\i\.Messages\.SETTINGS_GAMES_NOW_PLAYING_STATE.+?=(\i)\.overlay.+?)/,
|
match: /\.Messages\.SETTINGS_GAMES_TOGGLE_OVERLAY.+?}\(\),(?<={overlay:\i,.+?=(\i),.+?)(?=!(\i))/,
|
||||||
replace: (m, nowPlaying, props) => `${m},$self.renderToggleGameActivityButton(${props},${nowPlaying})`
|
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Messages.EMBEDDED_ACTIVITIES_HAVE_PLAYED_ONE_KNOWN",
|
find: ".activityTitleText,variant",
|
||||||
replacement: [
|
replacement: {
|
||||||
{
|
match: /(?<=\i\.activityTitleText.+?children:(\i)\.name.*?}\),)/,
|
||||||
match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/,
|
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
||||||
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=\(\)\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/,
|
find: ".activityCardDetails,children",
|
||||||
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
replacement: {
|
||||||
}
|
match: /(?<=\i\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/,
|
||||||
]
|
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -37,13 +37,6 @@ export const settings = definePluginSettings({
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
preventCarouselFromClosingOnClick: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
// Thanks chat gpt
|
|
||||||
description: "Allow the image modal in the image slideshow thing / carousel to remain open when clicking on the image",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
invertScroll: {
|
invertScroll: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Invert scroll",
|
description: "Invert scroll",
|
||||||
@ -101,7 +94,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
|||||||
/>
|
/>
|
||||||
<Menu.MenuCheckboxItem
|
<Menu.MenuCheckboxItem
|
||||||
id="vc-nearest-neighbour"
|
id="vc-nearest-neighbour"
|
||||||
label="Nearset Neighbour"
|
label="Nearest Neighbour"
|
||||||
checked={settings.store.nearestNeighbour}
|
checked={settings.store.nearestNeighbour}
|
||||||
action={() => {
|
action={() => {
|
||||||
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
||||||
@ -163,10 +156,14 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '"renderLinkComponent","maxWidth"',
|
find: "Messages.OPEN_IN_BROWSER",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(return\(.{1,100}\(\)\.wrapper.{1,200})(src)/,
|
// there are 2 image thingies. one for carosuel and one for the single image.
|
||||||
replace: `$1id: '${ELEMENT_ID}',$2`
|
// so thats why i added global flag.
|
||||||
|
// also idk if this patch is good, should it be more specific?
|
||||||
|
// https://regex101.com/r/xfvNvV/1
|
||||||
|
match: /return.{1,200}\.wrapper.{1,200}src:\i,/g,
|
||||||
|
replace: `$&id: '${ELEMENT_ID}',`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -174,26 +171,26 @@ export default definePlugin({
|
|||||||
find: "handleImageLoad=",
|
find: "handleImageLoad=",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /showThumbhashPlaceholder:/,
|
match: /showThumbhashPlaceholder:\i,/,
|
||||||
replace: "...$self.makeProps(this),$&"
|
replace: "...$self.makeProps(this),$&"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
match: /componentDidMount=function\(\){/,
|
match: /componentDidMount\(\){/,
|
||||||
replace: "$&$self.renderMagnifier(this);",
|
replace: "$&$self.renderMagnifier(this);",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
match: /componentWillUnmount=function\(\){/,
|
match: /componentWillUnmount\(\){/,
|
||||||
replace: "$&$self.unMountMagnifier();"
|
replace: "$&$self.unMountMagnifier();"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".carouselModal,",
|
find: ".carouselModal",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /onClick:(\i),/,
|
match: /(?<=\.carouselModal.{0,100}onClick:)\i,/,
|
||||||
replace: "onClick:$self.settings.store.preventCarouselFromClosingOnClick ? () => {} : $1,"
|
replace: "()=>{},"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -15,25 +15,17 @@
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-imgzoom-nearest-neighbor > img {
|
.vc-imgzoom-nearest-neighbor>img {
|
||||||
image-rendering: pixelated; /* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
image-rendering: pixelated;
|
||||||
|
|
||||||
|
/* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make the carousel take up less space so we can click the backdrop and exit out of it */
|
/* make the carousel take up less space so we can click the backdrop and exit out of it */
|
||||||
[class|="carouselModal"] {
|
[class*="modalCarouselWrapper_"] {
|
||||||
height: fit-content;
|
top: 0 !important;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="modalCarouselWrapper"] {
|
[class*="carouselModal_"] {
|
||||||
height: fit-content;
|
height: 0 !important;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
[class|="wrapper"]:has(> #vc-imgzoom-magnify-modal) {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
}
|
||||||
|
@ -131,15 +131,15 @@ export default definePlugin({
|
|||||||
// Indicator
|
// Indicator
|
||||||
find: ".Messages.MESSAGE_EDITED,",
|
find: ".Messages.MESSAGE_EDITED,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /var .,.,.=(.)\.className,.=.\.message,.=.\.children,.=.\.content,.=.\.onUpdate/gm,
|
match: /let\{className:\i,message:\i[^}]*\}=(\i)/,
|
||||||
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
|
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".activeCommandOption",
|
find: "ChannelTextAreaButtons",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
match: /(\i)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||||
replace: "$&;try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}",
|
replace: "$&,(()=>{try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}})()",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -21,8 +21,8 @@ import { Link } from "@components/Link";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||||
|
|
||||||
interface ActivityAssets {
|
interface ActivityAssets {
|
||||||
large_image?: string;
|
large_image?: string;
|
||||||
@ -86,15 +86,9 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
|||||||
const logger = new Logger("LastFMRichPresence");
|
const logger = new Logger("LastFMRichPresence");
|
||||||
|
|
||||||
const presenceStore = findByPropsLazy("getLocalPresence");
|
const presenceStore = findByPropsLazy("getLocalPresence");
|
||||||
const assetManager = mapMangledModuleLazy(
|
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActivity(activity: Activity | null) {
|
function setActivity(activity: Activity | null) {
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
* 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 { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
// These are Xor encrypted to prevent you from spoiling yourself when you read the source code.
|
// These are Xor encrypted to prevent you from spoiling yourself when you read the source code.
|
||||||
// don't worry about it :P
|
// don't worry about it :P
|
||||||
@ -60,17 +61,35 @@ const quotes = [
|
|||||||
"Wdn`khc'|f*eghl{%"
|
"Wdn`khc'|f*eghl{%"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
replaceEvents: {
|
||||||
|
description: "Replace Event Quotes too",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "LoadingQuotes",
|
name: "LoadingQuotes",
|
||||||
description: "Replace Discords loading quotes",
|
description: "Replace Discords loading quotes",
|
||||||
authors: [Devs.Ven, Devs.KraXen72],
|
authors: [Devs.Ven, Devs.KraXen72],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".LOADING_DID_YOU_KNOW",
|
find: ".LOADING_DID_YOU_KNOW}",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /\._loadingText=.+?random\(.+?;/s,
|
{
|
||||||
replace: "._loadingText=$self.quote;",
|
match: /\._loadingText=function\(\)\{/,
|
||||||
},
|
replace: "$&return $self.quote;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /\._eventLoadingText=function\(\)\{/,
|
||||||
|
replace: "$&return $self.quote;",
|
||||||
|
predicate: () => settings.store.replaceEvents
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -105,10 +105,10 @@ export default definePlugin({
|
|||||||
authors: [Devs.Ven, Devs.Commandtechno],
|
authors: [Devs.Ven, Devs.Commandtechno],
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".isSidebarVisible,",
|
find: "{isSidebarVisible:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(var (\i)=\i\.className.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
|
match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
|
||||||
replace: "$1:[$2?.startsWith('members')?$self.render():null,$3"
|
replace: ":[$1?.startsWith('members')?$self.render():null,$2"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
import { addAccessory } from "@api/MessageAccessories";
|
import { addAccessory } from "@api/MessageAccessories";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { getSettingStoreLazy } from "@api/SettingsStore";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants.js";
|
import { Devs } from "@utils/constants.js";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
@ -36,6 +35,7 @@ import {
|
|||||||
PermissionStore,
|
PermissionStore,
|
||||||
RestAPI,
|
RestAPI,
|
||||||
Text,
|
Text,
|
||||||
|
TextAndImagesSettingsStores,
|
||||||
UserStore
|
UserStore
|
||||||
} from "@webpack/common";
|
} from "@webpack/common";
|
||||||
import { Channel, Guild, Message } from "discord-types/general";
|
import { Channel, Guild, Message } from "discord-types/general";
|
||||||
@ -46,12 +46,11 @@ const messageCache = new Map<string, {
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed"));
|
const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed"));
|
||||||
const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes('["message","compact","className",')));
|
const AutoModEmbed = LazyComponent(() => findByCode(".withFooter]:", "childrenMessageContent:"));
|
||||||
|
const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes("renderSimpleAccessories)")));
|
||||||
|
|
||||||
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
||||||
|
|
||||||
let AutoModEmbed: React.ComponentType<any> = () => null;
|
|
||||||
|
|
||||||
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
||||||
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
|
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
|
||||||
|
|
||||||
@ -319,10 +318,9 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe
|
|||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const compactModeEnabled = getSettingStoreLazy<boolean>("textAndImages", "messageDisplayCompact")!;
|
|
||||||
|
|
||||||
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
||||||
const { message, channel, guildID } = props;
|
const { message, channel, guildID } = props;
|
||||||
|
const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting();
|
||||||
const isDM = guildID === "@me";
|
const isDM = guildID === "@me";
|
||||||
const images = getImages(message);
|
const images = getImages(message);
|
||||||
const { parse } = Parser;
|
const { parse } = Parser;
|
||||||
@ -338,7 +336,7 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
|||||||
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
compact={compactModeEnabled.getSetting()}
|
compact={compact}
|
||||||
content={
|
content={
|
||||||
<>
|
<>
|
||||||
{message.content || message.attachments.length <= images.length
|
{message.content || message.attachments.length <= images.length
|
||||||
@ -365,20 +363,7 @@ export default definePlugin({
|
|||||||
name: "MessageLinkEmbeds",
|
name: "MessageLinkEmbeds",
|
||||||
description: "Adds a preview to messages that link another message",
|
description: "Adds a preview to messages that link another message",
|
||||||
authors: [Devs.TheSun, Devs.Ven, Devs.RyanCaoDev],
|
authors: [Devs.TheSun, Devs.Ven, Devs.RyanCaoDev],
|
||||||
dependencies: ["MessageAccessoriesAPI", "SettingsStoreAPI"],
|
dependencies: ["MessageAccessoriesAPI"],
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: ".embedCard",
|
|
||||||
replacement: [{
|
|
||||||
match: /function (\i)\(\i\){var \i=\i\.message,\i=\i\.channel.{0,200}\.hideTimestamp/,
|
|
||||||
replace: "$self.AutoModEmbed=$1;$&"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
set AutoModEmbed(e: any) {
|
|
||||||
AutoModEmbed = e;
|
|
||||||
},
|
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/* Message content highlighting */
|
/* Message content highlighting */
|
||||||
.messagelogger-deleted [class*="contents-"] > :is(div, h1, h2, h3, p) {
|
.messagelogger-deleted [class*="contents"] > :is(div, h1, h2, h3, p) {
|
||||||
color: #f04747 !important;
|
color: #f04747 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bot "thinking" text highlighting */
|
/* Bot "thinking" text highlighting */
|
||||||
.messagelogger-deleted [class*="colorStandard-"] {
|
.messagelogger-deleted [class*="colorStandard"] {
|
||||||
color: #f04747 !important;
|
color: #f04747 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ export default definePlugin({
|
|||||||
ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id);
|
ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
// Based on canary 63b8f1b4f2025213c5cf62f0966625bee3d53136
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
// MessageStore
|
// MessageStore
|
||||||
@ -219,7 +219,7 @@ export default definePlugin({
|
|||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Add deleted=true to all target messages in the MESSAGE_DELETE event
|
// Add deleted=true to all target messages in the MESSAGE_DELETE event
|
||||||
match: /MESSAGE_DELETE:function\((\w)\){var .+?((?:\w{1,2}\.){2})getOrCreate.+?},/,
|
match: /MESSAGE_DELETE:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||||
replace:
|
replace:
|
||||||
"MESSAGE_DELETE:function($1){" +
|
"MESSAGE_DELETE:function($1){" +
|
||||||
" var cache = $2getOrCreate($1.channelId);" +
|
" var cache = $2getOrCreate($1.channelId);" +
|
||||||
@ -229,7 +229,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
|
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
|
||||||
match: /MESSAGE_DELETE_BULK:function\((\w)\){var .+?((?:\w{1,2}\.){2})getOrCreate.+?},/,
|
match: /MESSAGE_DELETE_BULK:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||||
replace:
|
replace:
|
||||||
"MESSAGE_DELETE_BULK:function($1){" +
|
"MESSAGE_DELETE_BULK:function($1){" +
|
||||||
" var cache = $2getOrCreate($1.channelId);" +
|
" var cache = $2getOrCreate($1.channelId);" +
|
||||||
@ -239,7 +239,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Add current cached content + new edit time to cached message's editHistory
|
// Add current cached content + new edit time to cached message's editHistory
|
||||||
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
match: /(MESSAGE_UPDATE:function\((\i)\).+?)\.update\((\i)/,
|
||||||
replace: "$1" +
|
replace: "$1" +
|
||||||
".update($3,m =>" +
|
".update($3,m =>" +
|
||||||
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message)) ? m :" +
|
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message)) ? m :" +
|
||||||
@ -251,8 +251,8 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// fix up key (edit last message) attempting to edit a deleted message
|
// fix up key (edit last message) attempting to edit a deleted message
|
||||||
match: /(?<=getLastEditableMessage=.{0,200}\.find\(\(function\((\i)\)\{)return/,
|
match: /(?<=getLastEditableMessage\(\i\)\{.{0,200}\.find\((\i)=>)/,
|
||||||
replace: "return !$1.deleted &&"
|
replace: "!$1.deleted &&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -260,13 +260,13 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Message domain model
|
// Message domain model
|
||||||
// Module 451
|
// Module 451
|
||||||
find: "isFirstMessageInForumPost=function",
|
find: "}addReaction(",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(\w)\.customRenderedContent=(\w)\.customRenderedContent;/,
|
match: /this\.customRenderedContent=(\i)\.customRenderedContent,/,
|
||||||
replace: "$1.customRenderedContent = $2.customRenderedContent;" +
|
replace: "this.customRenderedContent = $1.customRenderedContent," +
|
||||||
"$1.deleted = $2.deleted || false;" +
|
"this.deleted = $1.deleted || false," +
|
||||||
"$1.editHistory = $2.editHistory || [];"
|
"this.editHistory = $1.editHistory || [],"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -283,7 +283,7 @@ export default definePlugin({
|
|||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
// Pass through editHistory & deleted & original attachments to the "edited message" transformer
|
// Pass through editHistory & deleted & original attachments to the "edited message" transformer
|
||||||
match: /interactionData:(\w)\.interactionData/,
|
match: /interactionData:(\i)\.interactionData/,
|
||||||
replace:
|
replace:
|
||||||
"interactionData:$1.interactionData," +
|
"interactionData:$1.interactionData," +
|
||||||
"deleted:$1.deleted," +
|
"deleted:$1.deleted," +
|
||||||
@ -299,7 +299,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Construct new edited message and add editHistory & deleted (ref above)
|
// Construct new edited message and add editHistory & deleted (ref above)
|
||||||
// Pass in custom data to attachment parser to mark attachments deleted as well
|
// Pass in custom data to attachment parser to mark attachments deleted as well
|
||||||
match: /attachments:(\w{1,2})\((\w)\)/,
|
match: /attachments:(\i)\((\i)\)/,
|
||||||
replace:
|
replace:
|
||||||
"attachments: $1((() => {" +
|
"attachments: $1((() => {" +
|
||||||
" let old = arguments[1]?.attachments;" +
|
" let old = arguments[1]?.attachments;" +
|
||||||
@ -315,7 +315,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Preserve deleted attribute on attachments
|
// Preserve deleted attribute on attachments
|
||||||
match: /(\((\w)\){return null==\2\.attachments.+?)spoiler:/,
|
match: /(\((\i)\){return null==\2\.attachments.+?)spoiler:/,
|
||||||
replace:
|
replace:
|
||||||
"$1deleted: arguments[0]?.deleted," +
|
"$1deleted: arguments[0]?.deleted," +
|
||||||
"spoiler:"
|
"spoiler:"
|
||||||
@ -326,15 +326,15 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Attachment renderer
|
// Attachment renderer
|
||||||
// Module 96063
|
// Module 96063
|
||||||
find: "().removeAttachmentHoverButton",
|
find: ".removeAttachmentHoverButton",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /((\w)\.className,\w=\2\.attachment),/,
|
match: /(className:\i,attachment:\i),/,
|
||||||
replace: "$1,deleted=$2.attachment?.deleted,"
|
replace: "$1,attachment: {deleted},"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\["className","attachment".+?className:/,
|
match: /\[\i\.obscured\]:.+?,/,
|
||||||
replace: "$& (deleted ? 'messagelogger-deleted-attachment ' : '') +"
|
replace: "$& 'messagelogger-deleted-attachment': deleted,"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -360,7 +360,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Render editHistory in the deepest div for message content
|
// Render editHistory in the deepest div for message content
|
||||||
match: /(\)\("div",\{id:.+?children:\[)/,
|
match: /(\)\("div",\{id:.+?children:\[)/,
|
||||||
replace: "$1 (arguments[0].message.editHistory.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
|
replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -371,11 +371,11 @@ export default definePlugin({
|
|||||||
find: "displayName=\"ReferencedMessageStore\"",
|
find: "displayName=\"ReferencedMessageStore\"",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /MESSAGE_DELETE:function\((\w)\).+?},/,
|
match: /MESSAGE_DELETE:function\((\i)\).+?},/,
|
||||||
replace: "MESSAGE_DELETE:function($1){},"
|
replace: "MESSAGE_DELETE:function($1){},"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /MESSAGE_DELETE_BULK:function\((\w)\).+?},/,
|
match: /MESSAGE_DELETE_BULK:function\((\i)\).+?},/,
|
||||||
replace: "MESSAGE_DELETE_BULK:function($1){},"
|
replace: "MESSAGE_DELETE_BULK:function($1){},"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -384,7 +384,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Message context base menu
|
// Message context base menu
|
||||||
// Module 600300
|
// Module 600300
|
||||||
find: "id:\"remove-reactions\"",
|
find: "useMessageMenu:",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Remove the first section if message is deleted
|
// Remove the first section if message is deleted
|
||||||
|
@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
|
|||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findLazy } from "@webpack";
|
import { findByPropsLazy, findLazy } from "@webpack";
|
||||||
import { Card, ChannelStore, Forms, GuildStore, Switch, TextInput, Tooltip, useState } from "@webpack/common";
|
import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip, useState } from "@webpack/common";
|
||||||
import { RC } from "@webpack/types";
|
import { RC } from "@webpack/types";
|
||||||
import { Channel, Message, User } from "discord-types/general";
|
import { Channel, Message, User } from "discord-types/general";
|
||||||
|
|
||||||
@ -58,7 +58,6 @@ const PermissionUtil = findByPropsLazy("computePermissions", "canEveryoneRole")
|
|||||||
computePermissions({ ...args }): bigint;
|
computePermissions({ ...args }): bigint;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Permissions = findByPropsLazy("SEND_MESSAGES", "VIEW_CREATOR_MONETIZATION_ANALYTICS") as Record<PermissionName, bigint>;
|
|
||||||
const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
|
const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
|
||||||
|
|
||||||
const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot();
|
const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot();
|
||||||
@ -188,17 +187,14 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
// add tags to the tag list
|
// add tags to the tag list
|
||||||
{
|
{
|
||||||
find: '.BOT=0]="BOT"',
|
find: "BotTagTypes:",
|
||||||
replacement: [
|
replacement: {
|
||||||
// add tags to the exported tags list (Tag.Types)
|
match: /\((\i)=\{\}\)\)\[(\i)\.BOT/,
|
||||||
{
|
replace: "($1=$self.getTagTypes()))[$2.BOT"
|
||||||
match: /(\i)\[.\.BOT=0\]="BOT";/,
|
}
|
||||||
replace: "$&$1=$self.addTagVariants($1);"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP;",
|
find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP,",
|
||||||
replacement: [
|
replacement: [
|
||||||
// make the tag show the right text
|
// make the tag show the right text
|
||||||
{
|
{
|
||||||
@ -213,25 +209,25 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// add HTML data attributes (for easier theming)
|
// add HTML data attributes (for easier theming)
|
||||||
{
|
{
|
||||||
match: /children:\[(?=\i\?null:\i,\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/,
|
match: /.botText,children:(\i)}\)]/,
|
||||||
replace: "'data-tag':$1.toLowerCase(),children:["
|
replace: "$&,'data-tag':$1.toLowerCase()"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// in messages
|
// in messages
|
||||||
{
|
{
|
||||||
find: ".Types.ORIGINAL_POSTER",
|
find: "renderSystemTag:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /return null==(\i)\?null:\(0,/,
|
match: /;return\((\(null==\i\?void 0:\i\.isSystemDM\(\).+?.Types.ORIGINAL_POSTER\)),null==(\i)\)/,
|
||||||
replace: "$1=$self.getTag({...arguments[0],origType:$1,location:'chat'});$&"
|
replace: ";$1;$2=$self.getTag({...arguments[0],origType:$2,location:'chat'});return $2 == null"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// in the member list
|
// in the member list
|
||||||
{
|
{
|
||||||
find: ".Messages.GUILD_OWNER,",
|
find: ".Messages.GUILD_OWNER,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<type>\i)=\(null==.{0,50}\.BOT,null!=(?<user>\i)&&\i\.bot/,
|
match: /(?<type>\i)=\(null==.{0,100}\.BOT;return null!=(?<user>\i)&&\i\.bot/,
|
||||||
replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }), typeof $<type> === 'number'"
|
replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }); return typeof $<type> === 'number'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// pass channel id down props to be used in profiles
|
// pass channel id down props to be used in profiles
|
||||||
@ -251,11 +247,18 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// in profiles
|
// in profiles
|
||||||
{
|
{
|
||||||
find: "showStreamerModeTooltip:",
|
find: ",overrideDiscriminator:",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /,botType:(\i\((\i)\)),/g,
|
{
|
||||||
replace: ",botType:$self.getTag({user:$2,channelId:arguments[0].moreTags_channelId,origType:$1,location:'not-chat'}),"
|
// prevent channel id from getting ghosted
|
||||||
}
|
// it's either this or extremely long lookbehind
|
||||||
|
match: /user:\i,nick:\i,/,
|
||||||
|
replace: "$&moreTags_channelId,"
|
||||||
|
}, {
|
||||||
|
match: /,botType:(\i\((\i)\)),/g,
|
||||||
|
replace: ",botType:$self.getTag({user:$2,channelId:moreTags_channelId,origType:$1,location:'not-chat'}),"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -295,24 +298,25 @@ export default definePlugin({
|
|||||||
if (!guild) return [];
|
if (!guild) return [];
|
||||||
|
|
||||||
const permissions = PermissionUtil.computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites });
|
const permissions = PermissionUtil.computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites });
|
||||||
return Object.entries(Permissions)
|
return Object.entries(PermissionsBits)
|
||||||
.map(([perm, permInt]) =>
|
.map(([perm, permInt]) =>
|
||||||
permissions & permInt ? perm : ""
|
permissions & permInt ? perm : ""
|
||||||
)
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
},
|
},
|
||||||
|
|
||||||
addTagVariants(tagConstant) {
|
getTagTypes() {
|
||||||
|
const obj = {};
|
||||||
let i = 100;
|
let i = 100;
|
||||||
tags.forEach(({ name }) => {
|
tags.forEach(({ name }) => {
|
||||||
tagConstant[name] = ++i;
|
obj[name] = ++i;
|
||||||
tagConstant[i] = name;
|
obj[i] = name;
|
||||||
tagConstant[`${name}-BOT`] = ++i;
|
obj[`${name}-BOT`] = ++i;
|
||||||
tagConstant[i] = `${name}-BOT`;
|
obj[i] = `${name}-BOT`;
|
||||||
tagConstant[`${name}-OP`] = ++i;
|
obj[`${name}-OP`] = ++i;
|
||||||
tagConstant[i] = `${name}-OP`;
|
obj[i] = `${name}-OP`;
|
||||||
});
|
});
|
||||||
return tagConstant;
|
return obj;
|
||||||
},
|
},
|
||||||
|
|
||||||
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
|
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
|
||||||
@ -377,7 +381,6 @@ export default definePlugin({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -45,16 +45,16 @@ export default definePlugin({
|
|||||||
authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince],
|
authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ",acceptInvite:function",
|
find: ",acceptInvite(",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /INVITE_ACCEPT_SUCCESS.+?;(\i)=null.+?;/,
|
match: /INVITE_ACCEPT_SUCCESS.+?,(\i)=null!==.+?;/,
|
||||||
replace: (m, guildId) => `${m}$self.handleMute(${guildId});`
|
replace: (m, guildId) => `${m}$self.handleMute(${guildId});`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "{joinGuild:function",
|
find: "{joinGuild:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /guildId:(\w+),lurker:(\w+).{0,20}\)}\)\);/,
|
match: /guildId:(\i),lurker:(\i).{0,20}}\)\);/,
|
||||||
replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.handleMute(${guildId});`
|
replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.handleMute(${guildId});`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,16 +47,17 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".Messages.USER_PROFILE_MODAL", // Note: the module is lazy-loaded
|
find: ".Messages.USER_PROFILE_MODAL", // Note: the module is lazy-loaded
|
||||||
replacement: [
|
replacement: {
|
||||||
{
|
match: /(?<=\.MUTUAL_GUILDS\}\),)(?=(\i\.bot).{0,20}(\(0,\i\.jsx\)\(.{0,100}id:))/,
|
||||||
match: /(?<=\.MUTUAL_GUILDS\}\),)(?=(\i\.bot).{0,20}(\(0,\i\.jsx\)\(.{0,100}id:))/,
|
replace: '($1||arguments[0].isCurrentUser)?null:$2"MUTUAL_GDMS",children:"Mutual Groups"}),'
|
||||||
replace: '$1?null:$2"MUTUAL_GDMS",children:"Mutual Groups"}),'
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<={user:(\i),onClose:(\i)}\);)(?=case \i\.\i\.MUTUAL_FRIENDS)/,
|
find: ".UserProfileSections.USER_INFO_CONNECTIONS:",
|
||||||
replace: "case \"MUTUAL_GDMS\":return $self.renderMutualGDMs($1,$2);"
|
replacement: {
|
||||||
}
|
match: /(?<={user:(\i),onClose:(\i)}\);)(?=case \i\.\i\.MUTUAL_FRIENDS)/,
|
||||||
]
|
replace: "case \"MUTUAL_GDMS\":return $self.renderMutualGDMs($1,$2);"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -29,11 +29,11 @@ export default definePlugin({
|
|||||||
authors: [Devs.rushii, Devs.Samu],
|
authors: [Devs.rushii, Devs.Samu],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: 'safety_prompt:"DMSpamExperiment",response:"show_redacted_messages"',
|
find: "Messages.BLOCKED_MESSAGES_HIDE",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\.collapsedReason;return/,
|
match: /let\{[^}]*collapsedReason[^}]*\}/,
|
||||||
replace: ".collapsedReason;return null;return;"
|
replace: "return null;$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -26,8 +26,8 @@ export default definePlugin({
|
|||||||
patches: [{
|
patches: [{
|
||||||
find: "setDevtoolsCallbacks",
|
find: "setDevtoolsCallbacks",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /if\(.{0,10}\|\|"0.0.0"!==.{0,2}\.remoteApp\.getVersion\(\)\)/,
|
match: /if\(null!=\i&&"0.0.0"===\i\.remoteApp\.getVersion\(\)\)/,
|
||||||
replace: "if(false)"
|
replace: "if(true)"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
@ -4,37 +4,76 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
import style from "./styles.css?managed";
|
import style from "./styles.css?managed";
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
inlineVideo: {
|
||||||
|
description: "Play videos without carousel modal",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
mediaLayoutType: {
|
||||||
|
description: "Choose media layout type",
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
restartNeeded: true,
|
||||||
|
options: [
|
||||||
|
{ label: "STATIC, render loading image but image isn't resposive, no problem unless discord window width is too small", value: "STATIC", default: true },
|
||||||
|
{ label: "RESPONSIVE, image is responsive but not render loading image, cause messages shift when loaded", value: "RESPONSIVE" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "NoMosaic",
|
name: "NoMosaic",
|
||||||
authors: [Devs.AutumnVN],
|
authors: [Devs.AutumnVN],
|
||||||
description: "Removes Discord new image mosaic",
|
description: "Removes Discord new image mosaic",
|
||||||
tags: ["image", "mosaic", "media"],
|
tags: ["image", "mosaic", "media"],
|
||||||
patches: [{
|
|
||||||
find: "Media Mosaic",
|
settings,
|
||||||
replacement: [
|
|
||||||
{
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".oneByTwoLayoutThreeGrid",
|
||||||
|
replacement: [{
|
||||||
match: /mediaLayoutType:\i\.\i\.MOSAIC/,
|
match: /mediaLayoutType:\i\.\i\.MOSAIC/,
|
||||||
replace: 'mediaLayoutType:"RESPONSIVE"',
|
replace: "mediaLayoutType:$self.mediaLayoutType()",
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /\i===\i\.\i\.MOSAIC/,
|
|
||||||
replace: "true",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/,
|
match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/,
|
||||||
replace: '"INVALID"',
|
replace: '"INVALID"',
|
||||||
},
|
}]
|
||||||
],
|
},
|
||||||
}],
|
{
|
||||||
|
find: "renderAttachments(",
|
||||||
|
predicate: () => settings.store.inlineVideo,
|
||||||
|
replacement: {
|
||||||
|
match: /url:(\i)\.url\}\);return /,
|
||||||
|
replace: "$&$1.content_type?.startsWith('image/')&&"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "Messages.REMOVE_ATTACHMENT_TOOLTIP_TEXT",
|
||||||
|
replacement: {
|
||||||
|
match: /\i===\i\.\i\.MOSAIC/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
mediaLayoutType() {
|
||||||
|
return settings.store.mediaLayoutType;
|
||||||
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
enableStyle(style);
|
enableStyle(style);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
disableStyle(style);
|
disableStyle(style);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[class^="nonMediaAttachmentsContainer-"] [class*="messageAttachment-"] {
|
[class^="nonMediaAttachmentsContainer_"] [class*="messageAttachment_"] {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -55,18 +55,18 @@ export default definePlugin({
|
|||||||
// or by searching for "showProgressBadge:"
|
// or by searching for "showProgressBadge:"
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".getPendingCount=",
|
find: "getPendingCount(){",
|
||||||
predicate: () => settings.store.hideFriendRequestsCount,
|
predicate: () => settings.store.hideFriendRequestsCount,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.getPendingCount=function\(\)\{)/,
|
match: /(?<=getPendingCount\(\)\{)/,
|
||||||
replace: "return 0;"
|
replace: "return 0;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".getMessageRequestsCount=",
|
find: "getMessageRequestsCount(){",
|
||||||
predicate: () => settings.store.hideMessageRequestsCount,
|
predicate: () => settings.store.hideMessageRequestsCount,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.getMessageRequestsCount=function\(\)\{)/,
|
match: /(?<=getMessageRequestsCount\(\)\{)/,
|
||||||
replace: "return 0;"
|
replace: "return 0;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -84,8 +84,10 @@ export default definePlugin({
|
|||||||
find: "showProgressBadge:",
|
find: "showProgressBadge:",
|
||||||
predicate: () => settings.store.hidePremiumOffersCount,
|
predicate: () => settings.store.hidePremiumOffersCount,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\(function\(\){return \i\.\i\.getUnacknowledgedOffers\(\i\)\.length}\)/,
|
// The two groups inside the first group grab the minified names of the variables,
|
||||||
replace: "(function(){return 0})"
|
// they are then referenced later to find unviewedTrialCount + unviewedDiscountCount.
|
||||||
|
match: /(?<=\{unviewedTrialCount:(\i),unviewedDiscountCount:(\i)\}.{0,200}\i=)\1\+\2/,
|
||||||
|
replace: "0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -30,23 +30,23 @@ export default definePlugin({
|
|||||||
// = isPremiumAtLeast(user.premiumType, TIER_2)
|
// = isPremiumAtLeast(user.premiumType, TIER_2)
|
||||||
match: /=(?=\i\.\i\.isPremiumAtLeast\(null==(\i))/,
|
match: /=(?=\i\.\i\.isPremiumAtLeast\(null==(\i))/,
|
||||||
// = user.banner && isPremiumAtLeast(user.premiumType, TIER_2)
|
// = user.banner && isPremiumAtLeast(user.premiumType, TIER_2)
|
||||||
replace: "=$1?.banner&&"
|
replace: "=(arguments[0]?.bannerSrc||$1?.banner)&&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "().avatarPositionPremiumNoBanner,default:",
|
find: ".avatarPositionPremiumNoBanner,default:",
|
||||||
replacement: {
|
replacement: {
|
||||||
// premiumUserWithoutBanner: foo().avatarPositionPremiumNoBanner, default: foo().avatarPositionNormal
|
// premiumUserWithoutBanner: foo().avatarPositionPremiumNoBanner, default: foo().avatarPositionNormal
|
||||||
match: /\.avatarPositionPremiumNoBanner(?=,default:\i\(\)\.(\i))/,
|
match: /\.avatarPositionPremiumNoBanner(?=,default:\i\.(\i))/,
|
||||||
// premiumUserWithoutBanner: foo().avatarPositionNormal...
|
// premiumUserWithoutBanner: foo().avatarPositionNormal...
|
||||||
replace: ".$1"
|
replace: ".$1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".hasThemeColors=function(){",
|
find: "hasThemeColors(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=key:"canUsePremiumProfileCustomization",get:function\(\){return)/,
|
match: /get canUsePremiumProfileCustomization\(\){return /,
|
||||||
replace: " false;"
|
replace: "$&false &&"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -25,14 +25,11 @@ export default definePlugin({
|
|||||||
authors: [Devs.Nuckyz],
|
authors: [Devs.Nuckyz],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '("ApplicationStreamPreviewUploadManager")',
|
find: '"ApplicationStreamPreviewUploadManager"',
|
||||||
replacement: [
|
replacement: {
|
||||||
String.raw`\i\.\i\.makeChunkedRequest\(`,
|
match: /await \i\.\i\.(makeChunkedRequest\(|post\(\{url:)\i\.\i\.STREAM_PREVIEW.+?\}\)/g,
|
||||||
String.raw`\i\.\i\.post\({url:`
|
replace: "0"
|
||||||
].map(match => ({
|
}
|
||||||
match: new RegExp(String.raw`(?=return\[(\d),${match}\i\.\i\.STREAM_PREVIEW.+?}\)\];)`),
|
|
||||||
replace: (_, code) => `return[${code},Promise.resolve({body:"",status:204})];`
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -25,15 +25,15 @@ export default definePlugin({
|
|||||||
authors: [Devs.rushii],
|
authors: [Devs.rushii],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "setSystemTrayApplications:function",
|
find: ",setSystemTrayApplications",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /setBadge:function.+?},/,
|
match: /setBadge\(\i\).+?},/,
|
||||||
replace: "setBadge:function(){},"
|
replace: "setBadge(){},"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /setSystemTrayIcon:function.+?},/,
|
match: /setSystemTrayIcon\(\i\).+?},/,
|
||||||
replace: "setSystemTrayIcon:function(){},"
|
replace: "setSystemTrayIcon(){},"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "renderJumpButton=function()",
|
find: "renderJumpButton()",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}UNBLOCK_TO_JUMP_TITLE)/,
|
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}UNBLOCK_TO_JUMP_TITLE)/,
|
||||||
replace: "if(false)$1"
|
replace: "if(false)$1"
|
||||||
|
@ -4,35 +4,65 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { ChannelStore, ReadStateStore } from "@webpack/common";
|
import { ChannelStore, ReadStateStore, UserStore } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { MessageJSON } from "discord-types/general";
|
||||||
|
|
||||||
const enum ChannelType {
|
const enum ChannelType {
|
||||||
DM = 1,
|
DM = 1,
|
||||||
GROUP_DM = 3
|
GROUP_DM = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
channelToAffect: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: "Select the type of DM for the plugin to affect",
|
||||||
|
options: [
|
||||||
|
{ label: "Both", value: "both_dms", default: true },
|
||||||
|
{ label: "User DMs", value: "user_dm" },
|
||||||
|
{ label: "Group DMs", value: "group_dm" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
allowMentions: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Receive audio pings for @mentions",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
allowEveryone: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Receive audio pings for @everyone and @here in group DMs",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "OnePingPerDM",
|
name: "OnePingPerDM",
|
||||||
description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit",
|
description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit",
|
||||||
authors: [Devs.ProffDea],
|
authors: [Devs.ProffDea],
|
||||||
|
settings,
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".getDesktopType()===",
|
find: ".getDesktopType()===",
|
||||||
replacement: [{
|
replacement: [{
|
||||||
match: /if\((\i\.\i\.getDesktopType\(\)===\i\.\i\.NEVER)\){/,
|
match: /(\i\.\i\.getDesktopType\(\)===\i\.\i\.NEVER)\)/,
|
||||||
replace: "if($1){if(!$self.isPrivateChannelRead(arguments[0]?.message))return;"
|
replace: "$&if(!$self.isPrivateChannelRead(arguments[0]?.message))return;else "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /sound:(\i\?\i:void 0,volume:\i,onClick:)/,
|
match: /sound:(\i\?\i:void 0,volume:\i,onClick)/,
|
||||||
replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1"
|
replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1"
|
||||||
}]
|
}]
|
||||||
}],
|
}],
|
||||||
isPrivateChannelRead(message: Message) {
|
isPrivateChannelRead(message: MessageJSON) {
|
||||||
const channelType = ChannelStore.getChannel(message.channel_id)?.type;
|
const channelType = ChannelStore.getChannel(message.channel_id)?.type;
|
||||||
if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) {
|
if (
|
||||||
return false;
|
(channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) ||
|
||||||
|
(channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") ||
|
||||||
|
(channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") ||
|
||||||
|
(settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) ||
|
||||||
|
(settings.store.allowEveryone && message.mention_everyone)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id;
|
return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id;
|
||||||
},
|
},
|
||||||
|
@ -18,12 +18,12 @@
|
|||||||
|
|
||||||
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";
|
||||||
|
|
||||||
const ShortUrlMatcher = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
const ShortUrlMatcher = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
||||||
const SpotifyMatcher = /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user)\/(.+)(?:\?.+?)?$/;
|
const SpotifyMatcher = /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/;
|
||||||
const SteamMatcher = /^https:\/\/(steamcommunity\.com|(?:help|store)\.steampowered\.com)\/.+$/;
|
const SteamMatcher = /^https:\/\/(steamcommunity\.com|(?:help|store)\.steampowered\.com)\/.+$/;
|
||||||
const EpicMatcher = /^https:\/\/store\.epicgames\.com\/(.+)$/;
|
const EpicMatcher = /^https:\/\/store\.epicgames\.com\/(.+)$/;
|
||||||
|
|
||||||
@ -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",
|
||||||
@ -53,10 +55,10 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '"MaskedLinkStore"',
|
find: "trackAnnouncementMessageLinkClicked({",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /return ((\i)\.apply\(this,arguments\))(?=\}function \i.{0,250}\.trusted)/,
|
match: /(?<=handleClick:function\(\)\{return (\i)\}.+?)function \1\(.+?\)\{/,
|
||||||
replace: "return $self.handleLink(...arguments).then(handled => handled||$1)"
|
replace: "async $& if(await $self.handleLink(...arguments)) return;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Make Spotify profile activity links open in app on web
|
// Make Spotify profile activity links open in app on web
|
||||||
@ -71,7 +73,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".CONNECTED_ACCOUNT_VIEWED,",
|
find: ".CONNECTED_ACCOUNT_VIEWED,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=href:\i,onClick:function\(\i\)\{)(?=\i=(\i)\.type,.{0,50}CONNECTED_ACCOUNT_VIEWED)/,
|
match: /(?<=href:\i,onClick:\i=>\{)(?=.{0,10}\i=(\i)\.type,.{0,100}CONNECTED_ACCOUNT_VIEWED)/,
|
||||||
replace: "$self.handleAccountView(arguments[0],$1.type,$1.id);"
|
replace: "$self.handleAccountView(arguments[0],$1.type,$1.id);"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
|
}
|
@ -31,7 +31,7 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
// Permission lockout, just set the check to true
|
// Permission lockout, just set the check to true
|
||||||
{
|
{
|
||||||
find: "Messages.SELF_DENY_PERMISSION_BODY",
|
find: ".showPermissionLockoutModal(",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /case"DENY":.{0,50}if\((?=\i\.\i\.can)/,
|
match: /case"DENY":.{0,50}if\((?=\i\.\i\.can)/,
|
||||||
@ -42,11 +42,12 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// Onboarding, same thing but we need to prevent the check
|
// Onboarding, same thing but we need to prevent the check
|
||||||
{
|
{
|
||||||
find: "Messages.ONBOARDING_CHANNEL_THRESHOLD_WARNING",
|
find: ".ONBOARDING_CHANNEL_THRESHOLD_WARNING",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /case 1:if\((?=!\i\.sent.{20,30}Messages\.CANNOT_CHANGE_CHANNEL_PERMS)/,
|
// are we java yet?
|
||||||
replace: "$&false&&"
|
match: /(?<=(?:isDefaultChannelThresholdMetAfterDelete|checkDefaultChannelThresholdMetAfterChannelPermissionDeny):function\(\)\{)return \i(?=\})/g,
|
||||||
|
replace: "return () => true"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
predicate: () => settings.store.onboarding
|
predicate: () => settings.store.onboarding
|
||||||
|
@ -135,9 +135,9 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||||||
<Text variant="text-md/normal">
|
<Text variant="text-md/normal">
|
||||||
{
|
{
|
||||||
permission.type === PermissionType.Role
|
permission.type === PermissionType.Role
|
||||||
? role?.name || "Unknown Role"
|
? role?.name ?? "Unknown Role"
|
||||||
: permission.type === PermissionType.User
|
: permission.type === PermissionType.User
|
||||||
? (user && getUniqueUsername(user)) || "Unknown User"
|
? (user && getUniqueUsername(user)) ?? "Unknown User"
|
||||||
: (
|
: (
|
||||||
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
||||||
@owner
|
@owner
|
||||||
|
@ -161,7 +161,7 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".Messages.BOT_PROFILE_SLASH_COMMANDS",
|
find: ".popularApplicationCommandIds,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /showBorder:.{0,60}}\),(?<=guild:(\i),guildMember:(\i),.+?)/,
|
match: /showBorder:.{0,60}}\),(?<=guild:(\i),guildMember:(\i),.+?)/,
|
||||||
replace: (m, guild, guildMember) => `${m}$self.UserPermissions(${guild},${guildMember}),`
|
replace: (m, guild, guildMember) => `${m}$self.UserPermissions(${guild},${guildMember}),`
|
||||||
|
@ -20,7 +20,8 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, Co
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { makeLazy } from "@utils/lazy";
|
import { makeLazy } from "@utils/lazy";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
import { UploadHandler, UserUtils } from "@webpack/common";
|
||||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
@ -35,8 +36,6 @@ const getFrames = makeLazy(() => Promise.all(
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchUser = findByCodeLazy(".USER(");
|
|
||||||
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
|
||||||
const UploadStore = findByPropsLazy("getUploads");
|
const UploadStore = findByPropsLazy("getUploads");
|
||||||
|
|
||||||
function loadImage(source: File | string) {
|
function loadImage(source: File | string) {
|
||||||
@ -70,7 +69,7 @@ async function resolveImage(options: Argument[], ctx: CommandContext, noServerPf
|
|||||||
return opt.value;
|
return opt.value;
|
||||||
case "user":
|
case "user":
|
||||||
try {
|
try {
|
||||||
const user = await fetchUser(opt.value);
|
const user = await UserUtils.getUser(opt.value);
|
||||||
return user.getAvatarURL(noServerPfp ? void 0 : ctx.guild?.id, 2048).replace(/\?size=\d+$/, "?size=2048");
|
return user.getAvatarURL(noServerPfp ? void 0 : ctx.guild?.id, 2048).replace(/\?size=\d+$/, "?size=2048");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[petpet] Failed to fetch user\n", err);
|
console.error("[petpet] Failed to fetch user\n", err);
|
||||||
@ -175,7 +174,7 @@ export default definePlugin({
|
|||||||
const file = new File([gif.bytesView()], "petpet.gif", { type: "image/gif" });
|
const file = new File([gif.bytesView()], "petpet.gif", { type: "image/gif" });
|
||||||
// Immediately after the command finishes, Discord clears all input, including pending attachments.
|
// Immediately after the command finishes, Discord clears all input, including pending attachments.
|
||||||
// Thus, setTimeout is needed to make this execute after Discord cleared the input
|
// Thus, setTimeout is needed to make this execute after Discord cleared the input
|
||||||
setTimeout(() => promptToUpload([file], cmdCtx.channel, DRAFT_TYPE), 10);
|
setTimeout(() => UploadHandler.promptToUpload([file], cmdCtx.channel, DRAFT_TYPE), 10);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -26,13 +26,12 @@ export default definePlugin({
|
|||||||
description: "Adds picture in picture to videos (next to the Download button)",
|
description: "Adds picture in picture to videos (next to the Download button)",
|
||||||
authors: [Devs.Lumap],
|
authors: [Devs.Lumap],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".onRemoveAttachment,",
|
find: ".nonMediaAttachment]",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.nonMediaAttachment,!(\i).{0,7}children:\[(\i),/,
|
match: /\.nonMediaAttachment\]:!(\i).{0,10}children:\[(\S)/,
|
||||||
replace: "$&$1&&$2&&$self.renderPiPButton(),"
|
replace: "$&,$1&&$2&&$self.renderPiPButton(),"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -66,7 +66,7 @@ export default definePlugin({
|
|||||||
// filter Discord's privateChannelIds list to remove pins, and pass
|
// filter Discord's privateChannelIds list to remove pins, and pass
|
||||||
// pinCount as prop. This needs to be here so that the entire DM list receives
|
// pinCount as prop. This needs to be here so that the entire DM list receives
|
||||||
// updates on pin/unpin
|
// updates on pin/unpin
|
||||||
match: /privateChannelIds:(\i),/,
|
match: /(?<=\i,{channels:\i,)privateChannelIds:(\i),/,
|
||||||
replace: "privateChannelIds:$1.filter(c=>!$self.isPinned(c)),pinCount:$self.usePinCount($1),"
|
replace: "privateChannelIds:$1.filter(c=>!$self.isPinned(c)),pinCount:$self.usePinCount($1),"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -75,39 +75,39 @@ export default definePlugin({
|
|||||||
// - Section 1: buttons for pages like Friends & Library
|
// - Section 1: buttons for pages like Friends & Library
|
||||||
// - Section 2: our pinned dms
|
// - Section 2: our pinned dms
|
||||||
// - Section 3: the normal dm list
|
// - Section 3: the normal dm list
|
||||||
match: /(?<=renderRow:(\i)\.renderRow,)sections:\[\i,/,
|
match: /(?<=renderRow:this\.renderRow,)sections:\[\i,/,
|
||||||
// For some reason, adding our sections when no private channels are ready yet
|
// For some reason, adding our sections when no private channels are ready yet
|
||||||
// makes DMs infinitely load. Thus usePinCount returns either a single element
|
// makes DMs infinitely load. Thus usePinCount returns either a single element
|
||||||
// array with the count, or an empty array. Due to spreading, only in the former
|
// array with the count, or an empty array. Due to spreading, only in the former
|
||||||
// case will an element be added to the outer array
|
// case will an element be added to the outer array
|
||||||
// Thanks for the fix, Strencher!
|
// Thanks for the fix, Strencher!
|
||||||
replace: "$&...$1.props.pinCount,"
|
replace: "$&...this.props.pinCount??[],"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages"
|
// Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages"
|
||||||
// lookbehind is used to lookup parameter name. We could use arguments[0], but
|
// lookbehind is used to lookup parameter name. We could use arguments[0], but
|
||||||
// if children ever is wrapped in an iife, it will break
|
// if children ever is wrapped in an iife, it will break
|
||||||
match: /children:(\i\.\i\.Messages.DIRECT_MESSAGES)(?<=renderSection=function\((\i)\).+?)/,
|
match: /children:(\i\.\i\.Messages.DIRECT_MESSAGES)(?<=renderSection=(\i)=>{.+?)/,
|
||||||
replace: "children:$2.section===1?'Pinned DMs':$1"
|
replace: "children:$2.section===1?'Pinned DMs':$1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Patch channel lookup inside renderDM
|
// Patch channel lookup inside renderDM
|
||||||
// channel=channels[channelIds[row]];
|
// channel=channels[channelIds[row]];
|
||||||
match: /(?<=preRenderedChildren,(\i)=)((\i)\[\i\[\i\]\]);/,
|
match: /(?<=renderDM=\((\i),(\i)\)=>{.*?this.state,\i=\i\[\i\],\i=)((\i)\[\i\]);/,
|
||||||
// section 1 is us, manually get our own channel
|
// section 1 is us, manually get our own channel
|
||||||
// section === 1 ? getChannel(channels, row) : channels[channelIds[row]];
|
// section === 1 ? getChannel(channels, row) : channels[channelIds[row]];
|
||||||
replace: "arguments[0]===1?$self.getChannel($3,arguments[1]):$2;"
|
replace: "$1===1?$self.getChannel($4,$2):$3;"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Fix getRowHeight's check for whether this is the DMs section
|
// Fix getRowHeight's check for whether this is the DMs section
|
||||||
// section === DMS
|
// section === DMS
|
||||||
match: /===\i.DMS&&0/,
|
match: /===\i\.DMS&&0/,
|
||||||
// section -1 === DMS
|
// section -1 === DMS
|
||||||
replace: "-1$&"
|
replace: "-1$&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Override scrollToChannel to properly account for pinned channels
|
// Override scrollToChannel to properly account for pinned channels
|
||||||
match: /(?<=else\{\i\+=)(\i)\*\(.+?(?=;)/,
|
match: /(?<=scrollTo\(\{to:\i\}\):\(\i\+=)(\d+)\*\(.+?(?=,)/,
|
||||||
replace: "$self.getScrollOffset(arguments[0],$1,this.props.padding,this.state.preRenderedChildren,$&)"
|
replace: "$self.getScrollOffset(arguments[0],$1,this.props.padding,this.state.preRenderedChildren,$&)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -115,19 +115,19 @@ export default definePlugin({
|
|||||||
|
|
||||||
// Fix Alt Up/Down navigation
|
// Fix Alt Up/Down navigation
|
||||||
{
|
{
|
||||||
find: '"mod+alt+right"',
|
find: ".Routes.APPLICATION_STORE&&",
|
||||||
replacement: {
|
replacement: {
|
||||||
// channelIds = __OVERLAY__ ? stuff : toArray(getStaticPaths()).concat(toArray(channelIds))
|
// channelIds = __OVERLAY__ ? stuff : [...getStaticPaths(),...channelIds)]
|
||||||
match: /(?<=(\i)=__OVERLAY__\?\i:.{0,10})\.concat\((.{0,10})\)/,
|
match: /(?<=\i=__OVERLAY__\?\i:\[\.\.\.\i\(\),\.\.\.)\i/,
|
||||||
// ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c)))
|
// ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c)))
|
||||||
replace: ".concat($self.getSnapshot()).concat($2.filter(c=>!$self.isPinned(c)))"
|
replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// fix alt+shift+up/down
|
// fix alt+shift+up/down
|
||||||
{
|
{
|
||||||
find: '"alt+shift+down"',
|
find: ".getFlattenedGuildIds()],",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=return \i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/,
|
match: /(?<=\i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/,
|
||||||
replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))"
|
replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -23,20 +23,20 @@ import { Settings } from "@api/Settings";
|
|||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
|
import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
const SessionsStore = findStoreLazy("SessionsStore");
|
const SessionsStore = findStoreLazy("SessionsStore");
|
||||||
|
|
||||||
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
|
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
|
||||||
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
|
return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => (
|
||||||
<Tooltip text={tooltip} >
|
<Tooltip text={tooltip} >
|
||||||
{(tooltipProps: any) => (
|
{(tooltipProps: any) => (
|
||||||
<svg
|
<svg
|
||||||
{...tooltipProps}
|
{...tooltipProps}
|
||||||
height={opts?.height ?? 20}
|
height={(opts?.height ?? 20) - (small ? 3 : 0)}
|
||||||
width={opts?.width ?? 20}
|
width={(opts?.width ?? 20) - (small ? 3 : 0)}
|
||||||
viewBox={opts?.viewBox ?? "0 0 24 24"}
|
viewBox={opts?.viewBox ?? "0 0 24 24"}
|
||||||
fill={color}
|
fill={color}
|
||||||
>
|
>
|
||||||
@ -55,18 +55,18 @@ const Icons = {
|
|||||||
};
|
};
|
||||||
type Platform = keyof typeof Icons;
|
type Platform = keyof typeof Icons;
|
||||||
|
|
||||||
const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE");
|
const StatusUtils = findByPropsLazy("getStatusColor", "StatusTypes");
|
||||||
|
|
||||||
const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => {
|
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
|
||||||
const tooltip = platform[0].toUpperCase() + platform.slice(1);
|
const tooltip = platform[0].toUpperCase() + platform.slice(1);
|
||||||
const Icon = Icons[platform] ?? Icons.desktop;
|
const Icon = Icons[platform] ?? Icons.desktop;
|
||||||
|
|
||||||
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
|
return <Icon color={`var(--${StatusUtils.getStatusColor(status)}`} tooltip={tooltip} small={small} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
|
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
|
||||||
|
|
||||||
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => {
|
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, small = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; small?: boolean; }) => {
|
||||||
if (!user || user.bot) return null;
|
if (!user || user.bot) return null;
|
||||||
|
|
||||||
if (user.id === UserStore.getCurrentUser().id) {
|
if (user.id === UserStore.getCurrentUser().id) {
|
||||||
@ -99,6 +99,7 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: {
|
|||||||
key={platform}
|
key={platform}
|
||||||
platform={platform as Platform}
|
platform={platform as Platform}
|
||||||
status={status}
|
status={status}
|
||||||
|
small={small}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -137,7 +138,7 @@ const indicatorLocations = {
|
|||||||
description: "In the member list",
|
description: "In the member list",
|
||||||
onEnable: () => addDecorator("platform-indicator", props =>
|
onEnable: () => addDecorator("platform-indicator", props =>
|
||||||
<ErrorBoundary noop>
|
<ErrorBoundary noop>
|
||||||
<PlatformIndicator user={props.user} />
|
<PlatformIndicator user={props.user} small={true} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
),
|
),
|
||||||
onDisable: () => removeDecorator("platform-indicator")
|
onDisable: () => removeDecorator("platform-indicator")
|
||||||
@ -197,13 +198,13 @@ export default definePlugin({
|
|||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
|
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
|
||||||
match: /(?<=return \i\.\i\.Masks\.STATUS_TYPING;)(.+?)(\i)\?(\i\.\i\.Masks\.STATUS_ONLINE_MOBILE):/,
|
match: /\.STATUS_TYPING;switch(?=.+?(if\(\i\)return \i\.\i\.Masks\.STATUS_ONLINE_MOBILE))/,
|
||||||
replace: (_, rest, isMobile, mobileMask) => `if(${isMobile})return ${mobileMask};${rest}`
|
replace: ".STATUS_TYPING;$1;switch"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
|
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
|
||||||
match: /(switch\(\i\){case \i\.\i\.ONLINE:return )(\i)\?({.+?}):/,
|
match: /switch\(\i\)\{case \i\.\i\.ONLINE:(if\(\i\)return\{[^}]+\})/,
|
||||||
replace: (_, rest, isMobile, component) => `if(${isMobile})return${component};${rest}`
|
replace: "$1;$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -229,7 +230,7 @@ export default definePlugin({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "isMobileOnline=function",
|
find: "}isMobileOnline(",
|
||||||
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator,
|
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator,
|
||||||
replacement: {
|
replacement: {
|
||||||
// Make isMobileOnline return true no matter what is the user status
|
// Make isMobileOnline return true no matter what is the user status
|
||||||
|
@ -129,13 +129,13 @@ export default definePlugin({
|
|||||||
authors: [Devs.Aria],
|
authors: [Devs.Aria],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".activeCommandOption",
|
find: "ChannelTextAreaButtons",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
match: /(\i)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||||
replace: "$&;try{$2||$1.unshift($self.previewIcon(arguments[0]))}catch{}",
|
replace: "$&,(()=>{try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}})()",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
previewIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }),
|
chatBarIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }),
|
||||||
});
|
});
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
* 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 ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
@ -39,17 +40,17 @@ function shouldShow(message: Message): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PronounsChatComponentWrapper({ message }: { message: Message; }) {
|
export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
return shouldShow(message)
|
return shouldShow(message)
|
||||||
? <PronounsChatComponent message={message} />
|
? <PronounsChatComponent message={message} />
|
||||||
: null;
|
: null;
|
||||||
}
|
}, { noop: true });
|
||||||
|
|
||||||
export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) {
|
export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
return shouldShow(message)
|
return shouldShow(message)
|
||||||
? <CompactPronounsChatComponent message={message} />
|
? <CompactPronounsChatComponent message={message} />
|
||||||
: null;
|
: null;
|
||||||
}
|
}, { noop: true });
|
||||||
|
|
||||||
function PronounsChatComponent({ message }: { message: Message; }) {
|
function PronounsChatComponent({ message }: { message: Message; }) {
|
||||||
const [result] = useFormattedPronouns(message.author.id);
|
const [result] = useFormattedPronouns(message.author.id);
|
||||||
@ -63,7 +64,7 @@ function PronounsChatComponent({ message }: { message: Message; }) {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CompactPronounsChatComponent({ message }: { message: Message; }) {
|
export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
const [result] = useFormattedPronouns(message.author.id);
|
const [result] = useFormattedPronouns(message.author.id);
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -73,4 +74,4 @@ export function CompactPronounsChatComponent({ message }: { message: Message; })
|
|||||||
>• {result}</span>
|
>• {result}</span>
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
}
|
}, { noop: true });
|
||||||
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||||||
find: "showCommunicationDisabledStyles",
|
find: "showCommunicationDisabledStyles",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
||||||
replace: "$1, $self.CompactPronounsChatComponentWrapper(e)"
|
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Patch the chat timestamp element (normal mode)
|
// Patch the chat timestamp element (normal mode)
|
||||||
@ -49,7 +49,7 @@ export default definePlugin({
|
|||||||
find: "showCommunicationDisabledStyles",
|
find: "showCommunicationDisabledStyles",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
||||||
replace: "[$1, $self.PronounsChatComponentWrapper(e)]"
|
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
|
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
|
||||||
@ -57,15 +57,15 @@ export default definePlugin({
|
|||||||
find: ".userTagNoNickname",
|
find: ".userTagNoNickname",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /,(\i)=(\i)\.pronouns/,
|
match: /{user:(\i),[^}]*,pronouns:(\i),[^}]*}=\i;/,
|
||||||
replace: ",[$1,vcPronounSource]=$self.useProfilePronouns($2.user.id)"
|
replace: "$&let vcPronounSource;[$2,vcPronounSource]=$self.useProfilePronouns($1.id);"
|
||||||
},
|
},
|
||||||
PRONOUN_TOOLTIP_PATCH
|
PRONOUN_TOOLTIP_PATCH
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// Patch the profile modal username header to use our pronoun hook instead of Discord's pronouns
|
// Patch the profile modal username header to use our pronoun hook instead of Discord's pronouns
|
||||||
{
|
{
|
||||||
find: ".USER_PROFILE_ACTIVITY",
|
find: ".nameTagSmall)",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\.getName\(\i\);(?<=displayProfile.{0,200})/,
|
match: /\.getName\(\i\);(?<=displayProfile.{0,200})/,
|
||||||
|
@ -37,7 +37,7 @@ export async function onRelationshipRemove({ relationship: { type, id } }: Relat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await UserUtils.fetchUser(id)
|
const user = await UserUtils.getUser(id)
|
||||||
.catch(() => null);
|
.catch(() => null);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user