fix context menu crashes & some plugins (#4139) - sadan4
Co-Authored-By: sadan4 <117494111+sadan4@users.noreply.github.com>
This commit is contained in:
parent
6ee6033beb
commit
66562976d1
9 changed files with 114 additions and 51 deletions
|
|
@ -131,6 +131,8 @@ interface ContextMenuProps {
|
|||
}
|
||||
|
||||
export function _usePatchContextMenu(props: ContextMenuProps) {
|
||||
if (!Menu.MenuItem) return props; // Prevent crashes in case we fail to acquire menu items for some reason
|
||||
|
||||
props = {
|
||||
...props,
|
||||
children: cloneMenuChildren(props.children),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
import { Devs } from "@utils/constants";
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
// duplicate values have multiple branches with different types. Just include all to be safe
|
||||
const nameMap = {
|
||||
|
|
@ -31,38 +32,40 @@ export default definePlugin({
|
|||
{
|
||||
find: "Menu API only allows Items",
|
||||
replacement: {
|
||||
match: /function.{0,80}type===(\i\.\i)\).{0,50}navigable:.+?Menu API/s,
|
||||
replace: (m, mod) => {
|
||||
const nameAssignments = [] as string[];
|
||||
match: /(?<=(\(\i\.type===(\i\.\i)\).{0,50}?navigable:.+Menu API).+?)}$/s,
|
||||
replace: (_, m) => {
|
||||
const registerCalls = [] as string[];
|
||||
|
||||
// if (t.type === m.MenuItem)
|
||||
const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g);
|
||||
// push({type:"item"})
|
||||
const pushTypeRe = /type:"(\w+)"/g;
|
||||
const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g); // if (t.type === m.MenuItem)
|
||||
const pushTypeRe = /type:"(\w+)"/g; // push({type:"item"})
|
||||
|
||||
let typeMatch: RegExpExecArray | null;
|
||||
// for each if (t.type === ...)
|
||||
while ((typeMatch = typeCheckRe.exec(m)) !== null) {
|
||||
// extract the current menu item
|
||||
const item = typeMatch[1];
|
||||
const component = typeMatch[1];
|
||||
// Set the starting index of the second regex to that of the first to start
|
||||
// matching from after the if
|
||||
pushTypeRe.lastIndex = typeCheckRe.lastIndex;
|
||||
|
||||
// extract the first type: "..."
|
||||
const type = pushTypeRe.exec(m)?.[1];
|
||||
if (type && type in nameMap) {
|
||||
const name = nameMap[type];
|
||||
nameAssignments.push(`Object.defineProperty(${item},"name",{value:"${name}"})`);
|
||||
registerCalls.push(`$self.registerMenuItem("${name}",${component})`);
|
||||
}
|
||||
}
|
||||
if (nameAssignments.length < 6) {
|
||||
console.warn("[MenuItemDemanglerAPI] Expected to at least remap 6 items, only remapped", nameAssignments.length);
|
||||
|
||||
if (registerCalls.length < 6) {
|
||||
console.warn("[MenuItemDemanglerAPI] Expected to remap 6 items, only remapped", registerCalls.length);
|
||||
}
|
||||
|
||||
// Merge all our redefines with the actual module
|
||||
return `${nameAssignments.join(";")};${m}`;
|
||||
return `${registerCalls.join(";")};}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
registerMenuItem(name: string, component: any) {
|
||||
Object.defineProperty(component, "name", { value: name });
|
||||
Menu[name] = component;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -86,14 +86,14 @@ export default definePlugin({
|
|||
// It has everything it needs preloaded, so, it doesn't include any chunk loading functionality.
|
||||
// Because of that, its WebpackInstance doesnt export wreq.m or wreq.c
|
||||
|
||||
// To circuvent this and disable Sentry we are gonna hook when wreq.g of its WebpackInstance is set.
|
||||
// To circuvent this and disable Sentry we are gonna hook when wreq.d of its WebpackInstance is set.
|
||||
// When that happens we are gonna forcefully throw an error and abort everything.
|
||||
Object.defineProperty(Function.prototype, "g", {
|
||||
Object.defineProperty(Function.prototype, "d", {
|
||||
configurable: true,
|
||||
|
||||
set(this: WebpackRequire, globalObj: WebpackRequire["g"]) {
|
||||
Object.defineProperty(this, "g", {
|
||||
value: globalObj,
|
||||
set(this: WebpackRequire, esmDeclareFunc: WebpackRequire["d"]) {
|
||||
Object.defineProperty(this, "d", {
|
||||
value: esmDeclareFunc,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
|
|
@ -123,7 +123,7 @@ export default definePlugin({
|
|||
|
||||
new Logger("NoTrack", "#8caaee").info("Disabling Sentry by erroring its WebpackInstance");
|
||||
|
||||
Reflect.deleteProperty(Function.prototype, "g");
|
||||
Reflect.deleteProperty(Function.prototype, "d");
|
||||
Reflect.deleteProperty(window, "DiscordSentry");
|
||||
|
||||
throw new Error("Sentry successfully disabled");
|
||||
|
|
@ -136,7 +136,7 @@ export default definePlugin({
|
|||
set() {
|
||||
new Logger("NoTrack", "#8caaee").error("Failed to disable Sentry. Falling back to deleting window.DiscordSentry");
|
||||
|
||||
Reflect.deleteProperty(Function.prototype, "g");
|
||||
Reflect.deleteProperty(Function.prototype, "d");
|
||||
Reflect.deleteProperty(window, "DiscordSentry");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export default definePlugin({
|
|||
find: "#{intl::COPY_VERSION}",
|
||||
replacement: [
|
||||
{
|
||||
match: /"text-xxs\/normal".{0,300}?(?=null!=(\i)&&(.{0,20}\i\.Text.{0,200}?,children:).{0,15}?("span"),({className:\i\.\i,children:\["Build Override: ",\1\.id\]\})\)\}\))/,
|
||||
match: /"text-xxs\/normal".{0,300}?(?=null!=(\i)&&(.{0,20}\i\.\i.{0,200}?,children:).{0,15}?("span"),({className:\i\.\i,children:\["Build Override: ",\1\.id\]\})\)\}\))/,
|
||||
replace: (m, _buildOverride, makeRow, component, props) => {
|
||||
props = props.replace(/children:\[.+\]/, "");
|
||||
return `${m},$self.makeInfoElements(${component},${props}).map(e=>${makeRow}e})),`;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".selectPreviousCommandOption(",
|
||||
replacement: {
|
||||
match: /(?<=(\i)\.which!==\i\.\i.ENTER\|\|).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=\|\|\(\i\.preventDefault)/,
|
||||
match: /(?<=(\i)\.key!==\i\.\i.ENTER\|\|).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=\|\|\(\i\.preventDefault)/,
|
||||
replace: "!$self.shouldSubmit($1,$2)"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ function makeBypassPatches(): Omit<Patch, "plugin"> {
|
|||
replacement: mapping.map(({ func, predicate }) => ({
|
||||
match: new RegExp(String.raw`(?<=${func}:)\i`),
|
||||
replace: "() => true",
|
||||
noWarn: true,
|
||||
predicate
|
||||
}))
|
||||
};
|
||||
|
|
@ -180,8 +181,36 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
patches: [
|
||||
// General bypass patches
|
||||
// TODO: remove
|
||||
makeBypassPatches(),
|
||||
{
|
||||
find: "canUseCustomStickersEverywhere:",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=canUseCustomStickersEverywhere:function\(\i\)\{)/,
|
||||
replace: "return true;",
|
||||
predicate: () => settings.store.enableStickerBypass
|
||||
},
|
||||
{
|
||||
match: /(?<=canUseHighVideoUploadQuality:function\(\i\)\{)/,
|
||||
replace: "return true;",
|
||||
predicate: () => settings.store.enableStreamQualityBypass
|
||||
},
|
||||
{
|
||||
match: /(?<=canStreamQuality:function\(\i,\i\)\{)/,
|
||||
replace: "return true;",
|
||||
predicate: () => settings.store.enableStreamQualityBypass
|
||||
},
|
||||
{
|
||||
match: /(?<=canUseClientThemes:function\(\i\)\{)/,
|
||||
replace: "return true;"
|
||||
},
|
||||
{
|
||||
match: /(?<=canUsePremiumAppIcons:function\(\i\)\{)/,
|
||||
replace: "return true;"
|
||||
}
|
||||
],
|
||||
},
|
||||
// Patch the emoji picker in voice calls to not be bypassed by fake nitro
|
||||
{
|
||||
find: '.getByName("fork_and_knife")',
|
||||
|
|
@ -254,8 +283,13 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Overwrite incoming connection settings proto with our local settings
|
||||
match: /(?<=CONNECTION_OPEN:function\((\i)\){)/,
|
||||
replace: (_, props) => `$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
},
|
||||
{
|
||||
match: /function (\i)\((\i)\){(?=.*CONNECTION_OPEN:\1)/,
|
||||
replace: (m, funcName, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
replace: (m, funcName, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`,
|
||||
noWarn: true
|
||||
},
|
||||
{
|
||||
// Overwrite non local proto changes with our local settings
|
||||
|
|
@ -352,7 +386,7 @@ export default definePlugin({
|
|||
predicate: () => settings.store.transformEmojis,
|
||||
replacement: {
|
||||
// Add the fake nitro emoji notice
|
||||
match: /(?<=emojiDescription:)(\i)(?<=\1=\i\((\i)\).+?)/,
|
||||
match: /(?<=emojiDescription:)(\i)(?<=\1=\(\i=>\{.+?\}\)\((\i)\)[,;].+?)/,
|
||||
replace: (_, reactNode, props) => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!${props}?.fakeNitroNode?.fake)`
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -350,11 +350,20 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
// MessageStore
|
||||
find: '"MessageStore"',
|
||||
replacement: [
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE event
|
||||
match: /(?<=MESSAGE_DELETE:function\((\i)\)\{)(?=let.{0,100}(\i\.\i)\.getOrCreate)/,
|
||||
replace: `
|
||||
let cache = $2.getOrCreate($1.channelId);
|
||||
cache = $self.handleDelete(cache, $1, false);
|
||||
$2.commit(cache);
|
||||
return;
|
||||
`
|
||||
},
|
||||
{
|
||||
noWarn: true,
|
||||
match: /function (?=.+?MESSAGE_DELETE:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
replace:
|
||||
"function $1($2){" +
|
||||
|
|
@ -365,7 +374,17 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
|
||||
match: /(?<=MESSAGE_DELETE_BULK:function\((\i)\){)(?=let.{0,100}(\i\.\i)\.getOrCreate)/,
|
||||
replace: `
|
||||
let cache = $2.getOrCreate($1.channelId);
|
||||
cache = $self.handleDelete(cache, $1, true);
|
||||
$2.commit(cache);
|
||||
return;
|
||||
`
|
||||
},
|
||||
{
|
||||
match: /function (?=.+?MESSAGE_DELETE_BULK:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
noWarn: true,
|
||||
replace:
|
||||
"function $1($2){" +
|
||||
" var cache = $3getOrCreate($2.channelId);" +
|
||||
|
|
@ -375,6 +394,20 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
// Add current cached content + new edit time to cached message's editHistory
|
||||
match: /(MESSAGE_UPDATE:function\((\i)\).+?)\.update\((\i)/,
|
||||
replace: `
|
||||
$1
|
||||
.update($3, m =>
|
||||
(($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :
|
||||
$2.message.edited_timestamp && $2.message.content !== m.content ?
|
||||
m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :
|
||||
m
|
||||
)
|
||||
.update($3
|
||||
`
|
||||
},
|
||||
{
|
||||
noWarn: true,
|
||||
match: /(function (\i)\((\i)\).+?)\.update\((\i)(?=.*MESSAGE_UPDATE:\2)/,
|
||||
replace: "$1" +
|
||||
".update($4,m =>" +
|
||||
|
|
@ -493,13 +526,23 @@ export default definePlugin({
|
|||
// ReferencedMessageStore
|
||||
find: '"ReferencedMessageStore"',
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=MESSAGE_DELETE:function\(\i\)\{)/,
|
||||
replace: "return;"
|
||||
},
|
||||
{
|
||||
match: /(?<=MESSAGE_DELETE_BULK:function\(\i\)\{)/,
|
||||
replace: "return;"
|
||||
},
|
||||
{
|
||||
match: /MESSAGE_DELETE:\i,/,
|
||||
replace: "MESSAGE_DELETE:()=>{},"
|
||||
replace: "MESSAGE_DELETE:()=>{},",
|
||||
noWarn: true
|
||||
},
|
||||
{
|
||||
match: /MESSAGE_DELETE_BULK:\i,/,
|
||||
replace: "MESSAGE_DELETE_BULK:()=>{},"
|
||||
replace: "MESSAGE_DELETE_BULK:()=>{},",
|
||||
noWarn: true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "SpotifyCrack",
|
||||
description: "Free listen along, no auto-pausing in voice chat, and allows activity to continue playing when idling",
|
||||
tags: ["Media", "Utility"],
|
||||
tags: ["Media", "Utility", "Activity"],
|
||||
authors: [Devs.Cyn, Devs.Nuckyz],
|
||||
settings,
|
||||
|
||||
|
|
|
|||
|
|
@ -17,29 +17,10 @@
|
|||
*/
|
||||
|
||||
import type * as t from "@vencord/discord-types";
|
||||
import { filters, mapMangledModuleLazy, waitFor, wreq } from "@webpack";
|
||||
import { filters, mapMangledModuleLazy, waitFor } from "@webpack";
|
||||
|
||||
export const Menu = {} as t.Menu;
|
||||
|
||||
// Relies on .name properties added by the MenuItemDemanglerAPI
|
||||
waitFor(m => m.name === "MenuCheckboxItem", (_, id) => {
|
||||
// We have to do this manual require by ID because m in this case is the MenuCheckBoxItem instead of the entire module
|
||||
const exports = wreq(id);
|
||||
|
||||
for (const exportKey in exports) {
|
||||
// Some exports might have not been initialized yet due to circular imports, so try catch it.
|
||||
try {
|
||||
var exportValue = exports[exportKey];
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof exportValue === "function" && exportValue.name.startsWith("Menu")) {
|
||||
Menu[exportValue.name] = exportValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
waitFor(filters.componentByCode('path:["empty"]'), m => Menu.Menu = m);
|
||||
waitFor(filters.componentByCode("SLIDER)", "handleSize:16"), m => Menu.MenuSliderControl = m);
|
||||
waitFor(filters.componentByCode(".SEARCH)", ".focus()", "query:"), m => Menu.MenuSearchControl = m);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue