mirror of
https://github.com/System-End/Vencord.git
synced 2026-04-19 16:28:16 +00:00
Merge branch 'dev' into strict-csp
This commit is contained in:
commit
2d739f0dd5
48 changed files with 296 additions and 623 deletions
|
|
@ -17,9 +17,10 @@
|
|||
*/
|
||||
|
||||
function parseHeaders(headers) {
|
||||
const result = new Headers();
|
||||
if (!headers)
|
||||
return {};
|
||||
const result = {};
|
||||
return result;
|
||||
|
||||
const headersArr = headers.trim().split("\n");
|
||||
for (var i = 0; i < headersArr.length; i++) {
|
||||
var row = headersArr[i];
|
||||
|
|
@ -27,13 +28,7 @@ function parseHeaders(headers) {
|
|||
, key = row.slice(0, index).trim().toLowerCase()
|
||||
, value = row.slice(index + 1).trim();
|
||||
|
||||
if (result[key] === undefined) {
|
||||
result[key] = value;
|
||||
} else if (Array.isArray(result[key])) {
|
||||
result[key].push(value);
|
||||
} else {
|
||||
result[key] = [result[key], value];
|
||||
}
|
||||
result.append(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// @license GPL-3.0
|
||||
// @match *://*.discord.com/*
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @grant unsafeWindow
|
||||
// @run-at document-start
|
||||
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
|
||||
// @compatible firefox Firefox Tampermonkey
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.12.0",
|
||||
"version": "1.12.2",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ const buildConfigs = ([
|
|||
...nodeCommonOpts,
|
||||
entryPoints: ["src/main/index.ts"],
|
||||
outfile: "dist/patcher.js",
|
||||
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
|
||||
footer: { js: "//# sourceURL=file:///VencordPatcher\n" + sourceMapFooter("patcher") },
|
||||
sourcemap,
|
||||
plugins: [
|
||||
// @ts-ignore this is never undefined
|
||||
|
|
@ -131,7 +131,7 @@ const buildConfigs = ([
|
|||
outfile: "dist/renderer.js",
|
||||
format: "iife",
|
||||
target: ["esnext"],
|
||||
footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
|
||||
footer: { js: "//# sourceURL=file:///VencordRenderer\n" + sourceMapFooter("renderer") },
|
||||
globalName: "Vencord",
|
||||
sourcemap,
|
||||
plugins: [
|
||||
|
|
@ -148,7 +148,7 @@ const buildConfigs = ([
|
|||
...nodeCommonOpts,
|
||||
entryPoints: ["src/preload.ts"],
|
||||
outfile: "dist/preload.js",
|
||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
|
||||
footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("preload") },
|
||||
sourcemap,
|
||||
define: {
|
||||
...defines,
|
||||
|
|
@ -162,7 +162,7 @@ const buildConfigs = ([
|
|||
...nodeCommonOpts,
|
||||
entryPoints: ["src/main/index.ts"],
|
||||
outfile: "dist/vencordDesktopMain.js",
|
||||
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
|
||||
footer: { js: "//# sourceURL=file:///VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
|
||||
sourcemap,
|
||||
plugins: [
|
||||
...nodeCommonOpts.plugins,
|
||||
|
|
@ -180,7 +180,7 @@ const buildConfigs = ([
|
|||
outfile: "dist/vencordDesktopRenderer.js",
|
||||
format: "iife",
|
||||
target: ["esnext"],
|
||||
footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") },
|
||||
footer: { js: "//# sourceURL=file:///VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") },
|
||||
globalName: "Vencord",
|
||||
sourcemap,
|
||||
plugins: [
|
||||
|
|
@ -197,7 +197,7 @@ const buildConfigs = ([
|
|||
...nodeCommonOpts,
|
||||
entryPoints: ["src/preload.ts"],
|
||||
outfile: "dist/vencordDesktopPreload.js",
|
||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") },
|
||||
footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") },
|
||||
sourcemap,
|
||||
define: {
|
||||
...defines,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const buildConfigs = [
|
|||
{
|
||||
...commonOptions,
|
||||
outfile: "dist/browser.js",
|
||||
footer: { js: "//# sourceURL=VencordWeb" }
|
||||
footer: { js: "//# sourceURL=file:///VencordWeb" }
|
||||
},
|
||||
{
|
||||
...commonOptions,
|
||||
|
|
@ -91,7 +91,7 @@ const buildConfigs = [
|
|||
...commonOptions.define,
|
||||
IS_EXTENSION: "true"
|
||||
},
|
||||
footer: { js: "//# sourceURL=VencordWeb" }
|
||||
footer: { js: "//# sourceURL=file:///VencordWeb" }
|
||||
},
|
||||
{
|
||||
...commonOptions,
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ export const globPlugins = kind => ({
|
|||
const mod = `p${i}`;
|
||||
code += `import ${mod} from "./${dir}/${fileName.replace(/\.tsx?$/, "")}";\n`;
|
||||
pluginsCode += `[${mod}.name]:${mod},\n`;
|
||||
metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI?
|
||||
metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ page.on("console", async e => {
|
|||
const [, tag, message, otherMessage] = args as Array<string>;
|
||||
|
||||
switch (tag) {
|
||||
case "WebpackInterceptor:":
|
||||
case "WebpackPatcher:":
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/);
|
||||
const patchSlowMatch = message.match(/Patch by (.+?) (took [\d.]+?ms) \(Module id is (.+?)\): (.+)/);
|
||||
const match = patchFailMatch ?? patchSlowMatch;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { LazyComponent, LazyComponentWrapper } from "@utils/lazyReact";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { LazyComponent, LazyComponentWrapper } from "@utils/react";
|
||||
import { React } from "@webpack/common";
|
||||
import type { React } from "@webpack/common";
|
||||
|
||||
import { ErrorCard } from "./ErrorCard";
|
||||
|
||||
|
|
@ -46,7 +46,9 @@ const NO_ERROR = {};
|
|||
// We might want to import this in a place where React isn't ready yet.
|
||||
// Thus, wrap in a LazyComponent
|
||||
const ErrorBoundary = LazyComponent(() => {
|
||||
return class ErrorBoundary extends React.PureComponent<React.PropsWithChildren<Props>> {
|
||||
// This component is used in a lot of files which end up importing other Webpack commons and causing circular imports.
|
||||
// For this reason, use a non import access here.
|
||||
return class ErrorBoundary extends Vencord.Webpack.Common.React.PureComponent<React.PropsWithChildren<Props>> {
|
||||
state = {
|
||||
error: NO_ERROR as any,
|
||||
stack: "",
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ async function runReporter() {
|
|||
|
||||
for (const patch of patches) {
|
||||
if (!patch.all) {
|
||||
new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`);
|
||||
new Logger("WebpackPatcher").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [plugin, moduleId, match, totalTime] of patchTimings) {
|
||||
if (totalTime > 5) {
|
||||
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||
new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { openContributorModal } from "@components/PluginSettings/ContributorModa
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { isPluginDev } from "@utils/misc";
|
||||
import { shouldShowContributorBadge } from "@utils/misc";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Forms, Toasts, UserStore } from "@webpack/common";
|
||||
|
|
@ -39,7 +39,7 @@ const ContributorBadge: ProfileBadge = {
|
|||
description: "Vencord Contributor",
|
||||
image: CONTRIBUTOR_BADGE,
|
||||
position: BadgePosition.START,
|
||||
shouldShow: ({ userId }) => isPluginDev(userId),
|
||||
shouldShow: ({ userId }) => shouldShowContributorBadge(userId),
|
||||
onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId))
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ export default definePlugin({
|
|||
replacement: [
|
||||
// Main setting definition
|
||||
{
|
||||
match: /(?<=INFREQUENT_USER_ACTION.{0,20},)useSetting:/,
|
||||
replace: "userSettingsAPIGroup:arguments[0],userSettingsAPIName:arguments[1],$&"
|
||||
match: /\.updateAsync\(.+?(?=,useSetting:)/,
|
||||
replace: "$&,userSettingsAPIGroup:arguments[0],userSettingsAPIName:arguments[1]"
|
||||
},
|
||||
// Selective wrapper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export default definePlugin({
|
|||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
match: /let{speaking:\i/,
|
||||
match: /let{ref:\i,speaking:\i/,
|
||||
replace: "$self.useAccountPanelRef();$&"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,12 +21,10 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
|
||||
type AnonUpload = Upload & { anonymise?: boolean; };
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { useState } from "@webpack/common";
|
||||
|
||||
const ActionBarIcon = findByCodeLazy(".actionBarIcon)");
|
||||
const UploadDraft = findByPropsLazy("popFirstFile", "update");
|
||||
|
||||
const enum Methods {
|
||||
Random,
|
||||
|
|
@ -34,6 +32,7 @@ const enum Methods {
|
|||
Timestamp,
|
||||
}
|
||||
|
||||
const ANONYMISE_UPLOAD_SYMBOL = Symbol("vcAnonymise");
|
||||
const tarExtMatcher = /\.tar\.\w+$/;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
|
|
@ -69,41 +68,44 @@ export default definePlugin({
|
|||
name: "AnonymiseFileNames",
|
||||
authors: [Devs.fawn],
|
||||
description: "Anonymise uploaded file names",
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "instantBatchUpload:",
|
||||
find: 'type:"UPLOAD_START"',
|
||||
replacement: {
|
||||
match: /uploadFiles:(\i),/,
|
||||
replace:
|
||||
"uploadFiles:(...args)=>(args[0].uploads.forEach(f=>f.filename=$self.anonymise(f)),$1(...args)),",
|
||||
match: /await \i\.uploadFiles\((\i),/,
|
||||
replace: "$1.forEach($self.anonymise),$&"
|
||||
},
|
||||
},
|
||||
{
|
||||
find: 'addFilesTo:"message.attachments"',
|
||||
replacement: {
|
||||
match: /(\i.uploadFiles\((\i),)/,
|
||||
replace: "$2.forEach(f=>f.filename=$self.anonymise(f)),$1"
|
||||
match: /\i.uploadFiles\((\i),/,
|
||||
replace: "$1.forEach($self.anonymise),$&"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",
|
||||
replacement: {
|
||||
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,
|
||||
replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null,"
|
||||
replace: "arguments[0].canEdit!==false?$self.AnonymiseUploadButton(arguments[0]):null,"
|
||||
},
|
||||
},
|
||||
],
|
||||
settings,
|
||||
|
||||
renderIcon: ErrorBoundary.wrap(({ upload, channelId, draftType }: { upload: AnonUpload; draftType: unknown; channelId: string; }) => {
|
||||
const anonymise = upload.anonymise ?? settings.store.anonymiseByDefault;
|
||||
AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: Upload; }) => {
|
||||
const [anonymise, setAnonymise] = useState(upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault);
|
||||
|
||||
function onToggleAnonymise() {
|
||||
upload[ANONYMISE_UPLOAD_SYMBOL] = !anonymise;
|
||||
setAnonymise(!anonymise);
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionBarIcon
|
||||
tooltip={anonymise ? "Using anonymous file name" : "Using normal file name"}
|
||||
onClick={() => {
|
||||
upload.anonymise = !anonymise;
|
||||
UploadDraft.update(channelId, upload.id, draftType, {}); // dummy update so component rerenders
|
||||
}}
|
||||
onClick={onToggleAnonymise}
|
||||
>
|
||||
{anonymise
|
||||
? <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M17.06 13C15.2 13 13.64 14.33 13.24 16.1C12.29 15.69 11.42 15.8 10.76 16.09C10.35 14.31 8.79 13 6.94 13C4.77 13 3 14.79 3 17C3 19.21 4.77 21 6.94 21C9 21 10.68 19.38 10.84 17.32C11.18 17.08 12.07 16.63 13.16 17.34C13.34 19.39 15 21 17.06 21C19.23 21 21 19.21 21 17C21 14.79 19.23 13 17.06 13M6.94 19.86C5.38 19.86 4.13 18.58 4.13 17S5.39 14.14 6.94 14.14C8.5 14.14 9.75 15.42 9.75 17S8.5 19.86 6.94 19.86M17.06 19.86C15.5 19.86 14.25 18.58 14.25 17S15.5 14.14 17.06 14.14C18.62 14.14 19.88 15.42 19.88 17S18.61 19.86 17.06 19.86M22 10.5H2V12H22V10.5M15.53 2.63C15.31 2.14 14.75 1.88 14.22 2.05L12 2.79L9.77 2.05L9.72 2.04C9.19 1.89 8.63 2.17 8.43 2.68L6 9H18L15.56 2.68L15.53 2.63Z" /></svg>
|
||||
|
|
@ -113,25 +115,31 @@ export default definePlugin({
|
|||
);
|
||||
}, { noop: true }),
|
||||
|
||||
anonymise(upload: AnonUpload) {
|
||||
if ((upload.anonymise ?? settings.store.anonymiseByDefault) === false) return upload.filename;
|
||||
|
||||
const file = upload.filename;
|
||||
const tarMatch = tarExtMatcher.exec(file);
|
||||
const extIdx = tarMatch?.index ?? file.lastIndexOf(".");
|
||||
const ext = extIdx !== -1 ? file.slice(extIdx) : "";
|
||||
|
||||
switch (settings.store.method) {
|
||||
case Methods.Random:
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
return Array.from(
|
||||
{ length: settings.store.randomisedLength },
|
||||
() => chars[Math.floor(Math.random() * chars.length)]
|
||||
).join("") + ext;
|
||||
case Methods.Consistent:
|
||||
return settings.store.consistent + ext;
|
||||
case Methods.Timestamp:
|
||||
return Date.now() + ext;
|
||||
anonymise(upload: Upload) {
|
||||
if ((upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault) === false) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
const originalFileName = upload.filename;
|
||||
const tarMatch = tarExtMatcher.exec(originalFileName);
|
||||
const extIdx = tarMatch?.index ?? originalFileName.lastIndexOf(".");
|
||||
const ext = extIdx !== -1 ? originalFileName.slice(extIdx) : "";
|
||||
|
||||
const newFilename = (() => {
|
||||
switch (settings.store.method) {
|
||||
case Methods.Random:
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
return Array.from(
|
||||
{ length: settings.store.randomisedLength },
|
||||
() => chars[Math.floor(Math.random() * chars.length)]
|
||||
).join("") + ext;
|
||||
case Methods.Consistent:
|
||||
return settings.store.consistent + ext;
|
||||
case Methods.Timestamp:
|
||||
return Date.now() + ext;
|
||||
}
|
||||
})();
|
||||
|
||||
upload.filename = newFilename;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export default definePlugin({
|
|||
description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)",
|
||||
authors: [Devs.Ducko],
|
||||
reporterTestable: ReporterTestable.None,
|
||||
hidden: IS_VESKTOP || "legcord" in window,
|
||||
|
||||
settingsAboutComponent: () => (
|
||||
<>
|
||||
|
|
@ -73,9 +74,6 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async start() {
|
||||
// Legcord comes with its own arRPC implementation, so this plugin just confuses users
|
||||
if ("legcord" in window) return;
|
||||
|
||||
if (ws) ws.close();
|
||||
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,22 @@ function makeShortcuts() {
|
|||
};
|
||||
}
|
||||
|
||||
function findStoreWrapper(findStore: typeof Webpack.findStore) {
|
||||
const cache = new Map<string, unknown>();
|
||||
|
||||
return function (storeName: string) {
|
||||
const cacheKey = String(storeName);
|
||||
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
||||
|
||||
let store: unknown;
|
||||
try {
|
||||
store = findStore(storeName);
|
||||
} catch { }
|
||||
if (store) cache.set(cacheKey, store);
|
||||
return store;
|
||||
};
|
||||
}
|
||||
|
||||
let fakeRenderWin: WeakRef<Window> | undefined;
|
||||
const find = newFindWrapper(f => f);
|
||||
const findByProps = newFindWrapper(filters.byProps);
|
||||
|
|
@ -98,7 +114,7 @@ function makeShortcuts() {
|
|||
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
|
||||
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
|
||||
findStore: newFindWrapper(filters.byStoreName),
|
||||
findStore: findStoreWrapper(Webpack.findStore),
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
plugins: { getter: () => Vencord.Plugins.plugins },
|
||||
Settings: { getter: () => Vencord.Settings },
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
|
|
@ -165,7 +166,7 @@ async function doClone(guildId: string, data: Sticker | Emoji) {
|
|||
message = JSON.parse(e.text).message;
|
||||
} catch { }
|
||||
|
||||
new Logger("EmoteCloner").error("Failed to clone", data.name, "to", guildId, e);
|
||||
new Logger("ExpressionCloner").error("Failed to clone", data.name, "to", guildId, e);
|
||||
Toasts.show({
|
||||
message: "Failed to clone: " + message,
|
||||
type: Toasts.Type.FAILURE,
|
||||
|
|
@ -364,10 +365,11 @@ const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { t
|
|||
}
|
||||
};
|
||||
|
||||
migratePluginSettings("ExpressionCloner", "EmoteCloner");
|
||||
export default definePlugin({
|
||||
name: "EmoteCloner",
|
||||
name: "ExpressionCloner",
|
||||
description: "Allows you to clone Emotes & Stickers to your own server (right click them)",
|
||||
tags: ["StickerCloner"],
|
||||
tags: ["StickerCloner", "EmoteCloner", "EmojiCloner"],
|
||||
authors: [Devs.Ven, Devs.Nuckyz],
|
||||
contextMenus: {
|
||||
"message": messageContextMenuPatch,
|
||||
|
|
@ -110,7 +110,7 @@ export default definePlugin({
|
|||
patches: [
|
||||
{
|
||||
// Indicator
|
||||
find: "#{intl::MESSAGE_EDITED}",
|
||||
find: ".SEND_FAILED,",
|
||||
replacement: {
|
||||
match: /let\{className:\i,message:\i[^}]*\}=(\i)/,
|
||||
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import { proxyLazy } from "@utils/lazy";
|
||||
import { sleep } from "@utils/misc";
|
||||
import { Queue } from "@utils/Queue";
|
||||
import { Flux, FluxDispatcher, GuildChannelStore, PrivateChannelsStore } from "@webpack/common";
|
||||
import { ChannelActionCreators, Flux, FluxDispatcher, GuildChannelStore } from "@webpack/common";
|
||||
|
||||
export const OnlineMemberCountStore = proxyLazy(() => {
|
||||
const preloadQueue = new Queue();
|
||||
|
|
@ -22,7 +22,7 @@ export const OnlineMemberCountStore = proxyLazy(() => {
|
|||
async _ensureCount(guildId: string) {
|
||||
if (onlineMemberMap.has(guildId)) return;
|
||||
|
||||
await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
|
||||
await ChannelActionCreators.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
|
||||
}
|
||||
|
||||
ensureCount(guildId?: string) {
|
||||
|
|
|
|||
|
|
@ -464,19 +464,21 @@ export default definePlugin({
|
|||
|
||||
{
|
||||
// Message content renderer
|
||||
find: ".SEND_FAILED,",
|
||||
replacement: {
|
||||
// Render editHistory behind the message content
|
||||
match: /\.isFailed]:.+?children:\[/,
|
||||
replace: "$&arguments[0]?.message?.editHistory?.length>0&&$self.renderEdits(arguments[0]),"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
find: "#{intl::MESSAGE_EDITED}",
|
||||
replacement: [
|
||||
{
|
||||
// Render editHistory in the deepest div for message content
|
||||
match: /(\)\("div",\{id:.+?children:\[)/,
|
||||
replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0])),"
|
||||
},
|
||||
{
|
||||
// Make edit marker clickable
|
||||
match: /"span",\{(?=className:\i\.edited,)/,
|
||||
replace: "$self.EditMarker,{message:arguments[0].message,"
|
||||
}
|
||||
]
|
||||
replacement: {
|
||||
// Make edit marker clickable
|
||||
match: /"span",\{(?=className:\i\.edited,)/,
|
||||
replace: "$self.EditMarker,{message:arguments[0].message,"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated, Samu 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 { ApplicationCommandInputType, findOption, OptionalMessageOption, RequiredMessageOption, sendBotMessage } from "@api/Commands";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
|
||||
function mock(input: string): string {
|
||||
let output = "";
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output += i % 2 ? input[i].toUpperCase() : input[i].toLowerCase();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "MoreCommands",
|
||||
description: "echo, lenny, mock",
|
||||
authors: [Devs.Arjix, Devs.echo, Devs.Samu],
|
||||
commands: [
|
||||
{
|
||||
name: "echo",
|
||||
description: "Sends a message as Clyde (locally)",
|
||||
options: [OptionalMessageOption],
|
||||
inputType: ApplicationCommandInputType.BOT,
|
||||
execute: (opts, ctx) => {
|
||||
const content = findOption(opts, "message", "");
|
||||
|
||||
sendBotMessage(ctx.channel.id, { content });
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "lenny",
|
||||
description: "Sends a lenny face",
|
||||
options: [OptionalMessageOption],
|
||||
execute: opts => ({
|
||||
content: findOption(opts, "message", "") + " ( ͡° ͜ʖ ͡°)"
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "mock",
|
||||
description: "mOcK PeOpLe",
|
||||
options: [RequiredMessageOption],
|
||||
execute: opts => ({
|
||||
content: mock(findOption(opts, "message", ""))
|
||||
}),
|
||||
},
|
||||
]
|
||||
});
|
||||
|
|
@ -1,47 +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 { findOption, OptionalMessageOption } from "@api/Commands";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "MoreKaomoji",
|
||||
description: "Adds more Kaomoji to discord. ヽ(´▽`)/",
|
||||
authors: [Devs.JacobTm],
|
||||
commands: [
|
||||
{ name: "dissatisfaction", description: " >﹏<" },
|
||||
{ name: "smug", description: "ಠ_ಠ" },
|
||||
{ name: "happy", description: "ヽ(´▽`)/" },
|
||||
{ name: "crying", description: "ಥ_ಥ" },
|
||||
{ name: "angry", description: "ヽ(`Д´)ノ" },
|
||||
{ name: "anger", description: "ヽ(o`皿′o)ノ" },
|
||||
{ name: "joy", description: "<( ̄︶ ̄)>" },
|
||||
{ name: "blush", description: "૮ ˶ᵔ ᵕ ᵔ˶ ა" },
|
||||
{ name: "confused", description: "(•ิ_•ิ)?" },
|
||||
{ name: "sleeping", description: "(ᴗ_ᴗ)" },
|
||||
{ name: "laughing", description: "o(≧▽≦)o" },
|
||||
|
||||
].map(data => ({
|
||||
...data,
|
||||
options: [OptionalMessageOption],
|
||||
execute: opts => ({
|
||||
content: findOption(opts, "message", "") + " " + data.description
|
||||
})
|
||||
}))
|
||||
});
|
||||
|
|
@ -1,177 +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 { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components/SettingSliderComponent";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { sleep } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { Message, ReactionEmoji } from "discord-types/general";
|
||||
|
||||
interface IMessageCreate {
|
||||
type: "MESSAGE_CREATE";
|
||||
optimistic: boolean;
|
||||
isPushNotification: boolean;
|
||||
channelId: string;
|
||||
message: Message;
|
||||
}
|
||||
|
||||
interface IReactionAdd {
|
||||
type: "MESSAGE_REACTION_ADD";
|
||||
optimistic: boolean;
|
||||
channelId: string;
|
||||
messageId: string;
|
||||
messageAuthorId: string;
|
||||
userId: "195136840355807232";
|
||||
emoji: ReactionEmoji;
|
||||
}
|
||||
|
||||
interface IVoiceChannelEffectSendEvent {
|
||||
type: string;
|
||||
emoji?: ReactionEmoji; // Just in case...
|
||||
channelId: string;
|
||||
userId: string;
|
||||
animationType: number;
|
||||
animationId: number;
|
||||
}
|
||||
|
||||
const MOYAI = "🗿";
|
||||
const MOYAI_URL =
|
||||
"https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai.mp3";
|
||||
const MOYAI_URL_HD =
|
||||
"https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai_hd.wav";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
volume: {
|
||||
description: "Volume of the 🗿🗿🗿",
|
||||
type: OptionType.SLIDER,
|
||||
markers: makeRange(0, 1, 0.1),
|
||||
default: 0.5,
|
||||
stickToMarkers: false
|
||||
},
|
||||
quality: {
|
||||
description: "Quality of the 🗿🗿🗿",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{ label: "Normal", value: "Normal", default: true },
|
||||
{ label: "HD", value: "HD" }
|
||||
],
|
||||
},
|
||||
triggerWhenUnfocused: {
|
||||
description: "Trigger the 🗿 even when the window is unfocused",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBots: {
|
||||
description: "Ignore bots",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBlocked: {
|
||||
description: "Ignore blocked users",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "Moyai",
|
||||
authors: [Devs.Megu, Devs.Nuckyz],
|
||||
description: "🗿🗿🗿🗿🗿🗿🗿🗿",
|
||||
settings,
|
||||
|
||||
flux: {
|
||||
async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) {
|
||||
if (optimistic || type !== "MESSAGE_CREATE") return;
|
||||
if (message.state === "SENDING") return;
|
||||
if (settings.store.ignoreBots && message.author?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(message.author?.id)) return;
|
||||
if (!message.content) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
const moyaiCount = getMoyaiCount(message.content);
|
||||
|
||||
for (let i = 0; i < moyaiCount; i++) {
|
||||
boom();
|
||||
await sleep(300);
|
||||
}
|
||||
},
|
||||
|
||||
MESSAGE_REACTION_ADD({ optimistic, type, channelId, userId, messageAuthorId, emoji }: IReactionAdd) {
|
||||
if (optimistic || type !== "MESSAGE_REACTION_ADD") return;
|
||||
if (settings.store.ignoreBots && UserStore.getUser(userId)?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(messageAuthorId)) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
},
|
||||
|
||||
VOICE_CHANNEL_EFFECT_SEND({ emoji }: IVoiceChannelEffectSendEvent) {
|
||||
if (!emoji?.name) return;
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function countOccurrences(sourceString: string, subString: string) {
|
||||
let i = 0;
|
||||
let lastIdx = 0;
|
||||
while ((lastIdx = sourceString.indexOf(subString, lastIdx) + 1) !== 0)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
function countMatches(sourceString: string, pattern: RegExp) {
|
||||
if (!pattern.global)
|
||||
throw new Error("pattern must be global");
|
||||
|
||||
let i = 0;
|
||||
while (pattern.test(sourceString))
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
const customMoyaiRe = /<a?:\w*moy?ai\w*:\d{17,20}>/gi;
|
||||
|
||||
function getMoyaiCount(message: string) {
|
||||
const count = countOccurrences(message, MOYAI)
|
||||
+ countMatches(message, customMoyaiRe);
|
||||
|
||||
return Math.min(count, 10);
|
||||
}
|
||||
|
||||
function boom() {
|
||||
if (!settings.store.triggerWhenUnfocused && !document.hasFocus()) return;
|
||||
const audioElement = document.createElement("audio");
|
||||
|
||||
audioElement.src = settings.store.quality === "HD"
|
||||
? MOYAI_URL_HD
|
||||
: MOYAI_URL;
|
||||
|
||||
audioElement.volume = settings.store.volume;
|
||||
audioElement.play();
|
||||
}
|
||||
|
|
@ -1,35 +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: "NoRPC",
|
||||
description: "Disables Discord's RPC server.",
|
||||
authors: [Devs.Cyn],
|
||||
patches: [
|
||||
{
|
||||
find: '.ensureModule("discord_rpc")',
|
||||
replacement: {
|
||||
match: /\.ensureModule\("discord_rpc"\)\.then\(\(.+?\)}\)}/,
|
||||
replace: '.ensureModule("discord_rpc")}',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -30,7 +30,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "}searchWithoutFetchingLatest(",
|
||||
replacement: {
|
||||
match: /searchWithoutFetchingLatest.{20,300}get\((\i).{10,40}?reduce\(\((\i),(\i)\)=>\{/,
|
||||
match: /\.get\((\i)\)\.nameMatchesChain\(\i\)\.reduce\(\((\i),(\i)\)=>\{/,
|
||||
replace: "$& if ($self.shouldSkip($1, $3)) return $2;"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# No Unblock To Jump
|
||||
|
||||
Removes the popup preventing you to jump to a message from a blocked/ignored user (eg: in search results)
|
||||
Removes the popup preventing you to jump to a message from a blocked/ignored user or likely spammer (eg: in search results)
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import definePlugin from "@utils/types";
|
|||
|
||||
export default definePlugin({
|
||||
name: "NoUnblockToJump",
|
||||
description: "Allows you to jump to messages of blocked users without unblocking them",
|
||||
description: "Allows you to jump to messages of blocked or ignored users and likely spammers without unblocking them",
|
||||
authors: [Devs.dzshn],
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -106,13 +106,15 @@ export default definePlugin({
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".CONNECTED_ACCOUNT_VIEWED,",
|
||||
|
||||
// User Profile Modal & User Profile Modal v2
|
||||
...[".__invalid_connectedAccountOpenIconContainer", ".BLUESKY||"].map(find => ({
|
||||
find,
|
||||
replacement: {
|
||||
match: /(?<=href:\i,onClick:(\i)=>\{)(?=.{0,10}\i=(\i)\.type,.{0,100}CONNECTED_ACCOUNT_VIEWED)/,
|
||||
match: /(?<=onClick:(\i)=>\{)(?=.{0,100}\.CONNECTED_ACCOUNT_VIEWED)(?<==(\i)\.metadata.+?)/,
|
||||
replace: "if($self.handleAccountView($1,$2.type,$2.id)) return;"
|
||||
}
|
||||
}
|
||||
}))
|
||||
],
|
||||
|
||||
async handleLink(data: { href: string; }, event?: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -1,109 +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 { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
import { FluxDispatcher } from "@webpack/common";
|
||||
|
||||
const enum Intensity {
|
||||
Normal,
|
||||
Better,
|
||||
ProjectX,
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
superIntensePartyMode: {
|
||||
description: "Party intensity",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{ label: "Normal", value: Intensity.Normal, default: true },
|
||||
{ label: "Better", value: Intensity.Better },
|
||||
{ label: "Project X", value: Intensity.ProjectX },
|
||||
],
|
||||
restartNeeded: false,
|
||||
onChange: setSettings
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "PartyMode",
|
||||
description: "Allows you to use party mode cause the party never ends ✨",
|
||||
authors: [Devs.UwUDev],
|
||||
reporterTestable: ReporterTestable.None,
|
||||
settings,
|
||||
|
||||
start() {
|
||||
setPoggerState(true);
|
||||
setSettings(settings.store.superIntensePartyMode);
|
||||
},
|
||||
|
||||
stop() {
|
||||
setPoggerState(false);
|
||||
},
|
||||
});
|
||||
|
||||
function setPoggerState(state: boolean) {
|
||||
FluxDispatcher.dispatch({
|
||||
type: "POGGERMODE_SETTINGS_UPDATE",
|
||||
settings: {
|
||||
enabled: state,
|
||||
settingsVisible: state
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setSettings(intensity: Intensity) {
|
||||
const state = {
|
||||
screenshakeEnabledLocations: { 0: true, 1: true, 2: true },
|
||||
shakeIntensity: 1,
|
||||
confettiSize: 16,
|
||||
confettiCount: 5,
|
||||
combosRequiredCount: 1
|
||||
};
|
||||
|
||||
switch (intensity) {
|
||||
case Intensity.Normal: {
|
||||
Object.assign(state, {
|
||||
screenshakeEnabledLocations: { 0: true, 1: false, 2: false },
|
||||
combosRequiredCount: 5
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Intensity.Better: {
|
||||
Object.assign(state, {
|
||||
confettiSize: 12,
|
||||
confettiCount: 8,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Intensity.ProjectX: {
|
||||
Object.assign(state, {
|
||||
shakeIntensity: 20,
|
||||
confettiSize: 25,
|
||||
confettiCount: 15,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FluxDispatcher.dispatch({
|
||||
type: "POGGERMODE_SETTINGS_UPDATE",
|
||||
settings: state
|
||||
});
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ import { Devs } from "@utils/constants";
|
|||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common";
|
||||
import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, useRef, UserStore } from "@webpack/common";
|
||||
import type { Guild, GuildMember } from "discord-types/general";
|
||||
|
||||
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions";
|
||||
|
|
@ -173,32 +173,38 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
|
||||
ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => (
|
||||
<Popout
|
||||
position="bottom"
|
||||
align="center"
|
||||
renderPopout={({ closePopout }) => (
|
||||
<Dialog className={PopoutClasses.container} style={{ width: "500px" }}>
|
||||
<UserPermissions guild={guild} guildMember={guildMember} closePopout={closePopout} />
|
||||
</Dialog>
|
||||
)}
|
||||
>
|
||||
{popoutProps => (
|
||||
<TooltipContainer text="View Permissions">
|
||||
<Button
|
||||
{...popoutProps}
|
||||
color={Button.Colors.CUSTOM}
|
||||
look={Button.Looks.FILLED}
|
||||
size={Button.Sizes.NONE}
|
||||
innerClassName={classes(RoleButtonClasses.buttonInner, RoleButtonClasses.icon)}
|
||||
className={classes(RoleButtonClasses.button, RoleButtonClasses.icon, "vc-permviewer-role-button")}
|
||||
>
|
||||
<SafetyIcon height="16" width="16" />
|
||||
</Button>
|
||||
</TooltipContainer>
|
||||
)}
|
||||
</Popout>
|
||||
), { noop: true }),
|
||||
ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => {
|
||||
const buttonRef = useRef(null);
|
||||
|
||||
return (
|
||||
<Popout
|
||||
position="bottom"
|
||||
align="center"
|
||||
targetElementRef={buttonRef}
|
||||
renderPopout={({ closePopout }) => (
|
||||
<Dialog className={PopoutClasses.container} style={{ width: "500px" }}>
|
||||
<UserPermissions guild={guild} guildMember={guildMember} closePopout={closePopout} />
|
||||
</Dialog>
|
||||
)}
|
||||
>
|
||||
{popoutProps => (
|
||||
<TooltipContainer text="View Permissions">
|
||||
<Button
|
||||
{...popoutProps}
|
||||
buttonRef={buttonRef}
|
||||
color={Button.Colors.CUSTOM}
|
||||
look={Button.Looks.FILLED}
|
||||
size={Button.Sizes.NONE}
|
||||
innerClassName={classes(RoleButtonClasses.buttonInner, RoleButtonClasses.icon)}
|
||||
className={classes(RoleButtonClasses.button, RoleButtonClasses.icon, "vc-permviewer-role-button")}
|
||||
>
|
||||
<SafetyIcon height="16" width="16" />
|
||||
</Button>
|
||||
</TooltipContainer>
|
||||
)}
|
||||
</Popout>
|
||||
);
|
||||
}, { noop: true }),
|
||||
|
||||
contextMenus: {
|
||||
"user-context": makeContextMenuPatch("roles", MenuItemParentType.User),
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ export default definePlugin({
|
|||
},
|
||||
// Messages
|
||||
{
|
||||
find: "#{intl::MESSAGE_EDITED}",
|
||||
find: ".SEND_FAILED,",
|
||||
replacement: {
|
||||
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
|
||||
replace: "style:$self.useMessageColorsStyle($1),"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import { ILanguageRegistration } from "@vap/shiki";
|
||||
|
||||
export const VPC_REPO = "Vap0r1ze/vapcord";
|
||||
export const VPC_REPO_COMMIT = "88a7032a59cca40da170926651b08201ea3b965a";
|
||||
export const VPC_REPO_COMMIT = "4d0e4b420fb1e4358852bbd18c804a6f5e54c0d7";
|
||||
export const vpcRepoAssets = `https://raw.githubusercontent.com/${VPC_REPO}/${VPC_REPO_COMMIT}/assets/shiki-codeblocks`;
|
||||
export const vpcRepoGrammar = (fileName: string) => `${vpcRepoAssets}/${fileName}`;
|
||||
export const vpcRepoLanguages = `${vpcRepoAssets}/languages.json`;
|
||||
|
|
@ -46,7 +46,7 @@ export interface LanguageJson {
|
|||
export const languages: Record<string, Language> = {};
|
||||
|
||||
export const loadLanguages = async () => {
|
||||
const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.json());
|
||||
const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.ok ? res.json() : []);
|
||||
const loadedLanguages = Object.fromEntries(
|
||||
langsJson.map(lang => [lang.id, {
|
||||
...lang,
|
||||
|
|
|
|||
|
|
@ -356,15 +356,10 @@ export default definePlugin({
|
|||
find: "#{intl::CHANNEL_CALL_CURRENT_SPEAKER}",
|
||||
replacement: [
|
||||
{
|
||||
// Remove the divider and the open chat button for the HiddenChannelLockScreen
|
||||
match: /"more-options-popout"\)\),(?<=channel:(\i).+?inCall:(\i).+?)/,
|
||||
replace: (m, channel, inCall) => `${m}${inCall}||!$self.isHiddenChannel(${channel},true)&&`
|
||||
},
|
||||
{
|
||||
// Remove invite users button for the HiddenChannelLockScreen
|
||||
match: /"popup".{0,100}?if\((?<=channel:(\i).+?inCall:(\i).+?)/,
|
||||
replace: (m, channel, inCall) => `${m}(${inCall}||!$self.isHiddenChannel(${channel},true))&&`
|
||||
},
|
||||
// Remove the open chat button for the HiddenChannelLockScreen
|
||||
match: /(?<=&&)\i\.push\(.{0,120}"chat-spacer"/,
|
||||
replace: "(arguments[0]?.inCall||!$self.isHiddenChannel(arguments[0]?.channel,true))&&$&"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -397,8 +392,8 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Render our HiddenChannelLockScreen component instead of the main stage channel component
|
||||
match: /"124px".+?children:(?<=let \i,{channel:(\i).+?)(?=.{0,20}?}\)}function)/,
|
||||
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):`
|
||||
match: /screenMessage:(\i)\?.+?children:(?=!\1)(?<=let \i,{channel:(\i).+?)/,
|
||||
replace: (m, _isPopoutOpen, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):`
|
||||
},
|
||||
{
|
||||
// Disable useless components for the HiddenChannelLockScreen of stage channels
|
||||
|
|
@ -427,8 +422,8 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
// Remove the open chat button for the HiddenChannelLockScreen
|
||||
match: /"recents".+?&&(?=\(.+?channelId:(\i)\.id,showRequestToSpeakSidebar)/,
|
||||
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`
|
||||
match: /(?<=&&)\(0,\i\.jsxs?\).{0,180}\.buttonIcon/,
|
||||
replace: "!$self.isHiddenChannel(arguments[0]?.channel,true)&&$&"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Menu, Popout, useState } from "@webpack/common";
|
||||
import { Menu, Popout, useRef, useState } from "@webpack/common";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"');
|
||||
|
|
@ -95,6 +95,7 @@ function VencordPopoutIcon(isShown: boolean) {
|
|||
}
|
||||
|
||||
function VencordPopoutButton() {
|
||||
const buttonRef = useRef(null);
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -104,10 +105,12 @@ function VencordPopoutButton() {
|
|||
animation={Popout.Animation.NONE}
|
||||
shouldShow={show}
|
||||
onRequestClose={() => setShow(false)}
|
||||
targetElementRef={buttonRef}
|
||||
renderPopout={() => VencordPopout(() => setShow(false))}
|
||||
>
|
||||
{(_, { isShown }) => (
|
||||
<HeaderBarIcon
|
||||
ref={buttonRef}
|
||||
className="vc-toolbox-btn"
|
||||
onClick={() => setShow(v => !v)}
|
||||
tooltip={isShown ? null : "Vencord Toolbox"}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@ export default definePlugin({
|
|||
replace: '"x-google-max-bitrate=".concat("80_000")'
|
||||
},
|
||||
{
|
||||
match: /;level-asymmetry-allowed=1/,
|
||||
match: ";level-asymmetry-allowed=1",
|
||||
replace: ";b=AS:800000;level-asymmetry-allowed=1"
|
||||
},
|
||||
{
|
||||
match: "useinbandfec=1",
|
||||
replace: "useinbandfec=1;stereo=1;sprop-stereo=1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { MessageObject } from "@api/MessageEvents";
|
||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { Channel, Guild, Message, User } from "discord-types/general";
|
||||
import { Except } from "type-fest";
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ export function getCurrentGuild(): Guild | undefined {
|
|||
}
|
||||
|
||||
export function openPrivateChannel(userId: string) {
|
||||
PrivateChannelsStore.openPrivateChannel(userId);
|
||||
ChannelActionCreators.openPrivateChannel(userId);
|
||||
}
|
||||
|
||||
export const enum Theme {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ export function identity<T>(value: T): T {
|
|||
export const isMobile = navigator.userAgent.includes("Mobi");
|
||||
|
||||
export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id);
|
||||
export const shouldShowContributorBadge = (id: string) => isPluginDev(id) && DevsById[id].badge !== false;
|
||||
|
||||
export function pluralise(amount: number, singular: string, plural = singular + "s") {
|
||||
return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
import { Settings, SettingsStore } from "@api/Settings";
|
||||
import { ThemeStore } from "@webpack/common";
|
||||
|
||||
|
||||
let style: HTMLStyleElement;
|
||||
let themesStyle: HTMLStyleElement;
|
||||
|
||||
|
|
@ -61,7 +60,10 @@ async function initThemes() {
|
|||
const { themeLinks, enabledThemes } = Settings;
|
||||
|
||||
// "darker" and "midnight" both count as dark
|
||||
const activeTheme = ThemeStore.theme === "light" ? "light" : "dark";
|
||||
// This function is first called on DOMContentLoaded, so ThemeStore may not have been loaded yet
|
||||
const activeTheme = ThemeStore == null
|
||||
? undefined
|
||||
: ThemeStore.theme === "light" ? "light" : "dark";
|
||||
|
||||
const links = themeLinks
|
||||
.map(rawLink => {
|
||||
|
|
@ -98,6 +100,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
SettingsStore.addChangeListener("themeLinks", initThemes);
|
||||
SettingsStore.addChangeListener("enabledThemes", initThemes);
|
||||
|
||||
if (!IS_WEB) {
|
||||
VencordNative.quickCss.addThemeChangeListener(initThemes);
|
||||
}
|
||||
});
|
||||
|
||||
export function initQuickCssThemeStore() {
|
||||
initThemes();
|
||||
|
||||
let currentTheme = ThemeStore.theme;
|
||||
ThemeStore.addChangeListener(() => {
|
||||
if (currentTheme === ThemeStore.theme) return;
|
||||
|
|
@ -105,7 +115,4 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
currentTheme = ThemeStore.theme;
|
||||
initThemes();
|
||||
});
|
||||
|
||||
if (!IS_WEB)
|
||||
VencordNative.quickCss.addThemeChangeListener(initThemes);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@
|
|||
*/
|
||||
|
||||
import { LazyComponent, LazyComponentWrapper } from "@utils/react";
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack";
|
||||
import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "@webpack";
|
||||
|
||||
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]) {
|
||||
if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { filters, mapMangledModuleLazy, waitFor, wreq } from "../webpack";
|
||||
import { filters, mapMangledModuleLazy, waitFor, wreq } from "@webpack";
|
||||
|
||||
import type * as t from "./types/menu";
|
||||
|
||||
export const Menu = {} as t.Menu;
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { findByCodeLazy, findByPropsLazy, waitFor } from "../webpack";
|
||||
import { findByCodeLazy, findByPropsLazy, waitFor } from "@webpack";
|
||||
|
||||
export let React: typeof import("react");
|
||||
export let useState: typeof React.useState;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import type * as Stores from "discord-types/stores";
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { findByCodeLazy, findByPropsLazy } from "../webpack";
|
||||
import { waitForStore } from "./internal";
|
||||
import * as t from "./types/stores";
|
||||
|
||||
|
|
@ -33,7 +32,7 @@ export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & GenericStore
|
|||
getMessages(chanId: string): any;
|
||||
};
|
||||
|
||||
// this is not actually a FluxStore
|
||||
// TODO: The correct name for this is ChannelActionCreators and it has already been exported again from utils. Remove this export once enough time has passed
|
||||
export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel");
|
||||
export let PermissionStore: GenericStore;
|
||||
export let GuildChannelStore: GenericStore;
|
||||
|
|
@ -86,4 +85,8 @@ waitForStore("GuildChannelStore", m => GuildChannelStore = m);
|
|||
waitForStore("MessageStore", m => MessageStore = m);
|
||||
waitForStore("WindowStore", m => WindowStore = m);
|
||||
waitForStore("EmojiStore", m => EmojiStore = m);
|
||||
waitForStore("ThemeStore", m => ThemeStore = m);
|
||||
waitForStore("ThemeStore", m => {
|
||||
ThemeStore = m;
|
||||
// Importing this directly can easily cause circular imports. For this reason, use a non import access here.
|
||||
Vencord.QuickCss.initQuickCssThemeStore();
|
||||
});
|
||||
|
|
|
|||
3
src/webpack/common/types/components.d.ts
vendored
3
src/webpack/common/types/components.d.ts
vendored
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref } from "react";
|
||||
import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref, RefObject } from "react";
|
||||
|
||||
|
||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||
|
|
@ -426,6 +426,7 @@ export type Popout = ComponentType<{
|
|||
}
|
||||
): ReactNode;
|
||||
shouldShow?: boolean;
|
||||
targetElementRef: RefObject<any>;
|
||||
renderPopout(args: {
|
||||
closePopout(): void;
|
||||
isPositioned: boolean;
|
||||
|
|
|
|||
|
|
@ -16,16 +16,15 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "@webpack";
|
||||
import type { Channel } from "discord-types/general";
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
||||
import type * as t from "./types/utils";
|
||||
|
||||
export let FluxDispatcher: t.FluxDispatcher;
|
||||
waitFor(["dispatch", "subscribe"], m => {
|
||||
FluxDispatcher = m;
|
||||
// Non import call to avoid circular dependency
|
||||
// Non import access to avoid circular dependency
|
||||
Vencord.Plugins.subscribeAllPluginsFluxEvents(m);
|
||||
|
||||
const cb = () => {
|
||||
|
|
@ -35,7 +34,7 @@ waitFor(["dispatch", "subscribe"], m => {
|
|||
m.subscribe("CONNECTION_OPEN", cb);
|
||||
});
|
||||
|
||||
export let ComponentDispatch;
|
||||
export let ComponentDispatch: any;
|
||||
waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m);
|
||||
|
||||
export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', {
|
||||
|
|
@ -177,6 +176,7 @@ export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
|
|||
export const MessageCache = findByPropsLazy("clearCache", "_channelMessages");
|
||||
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
|
||||
export const InviteActions = findByPropsLazy("resolveInvite");
|
||||
export const ChannelActionCreators = findByPropsLazy("openPrivateChannel");
|
||||
|
||||
export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq
|
|||
return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY];
|
||||
}
|
||||
|
||||
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
||||
const logger = new Logger("WebpackPatcher", "#8caaee");
|
||||
|
||||
/** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */
|
||||
let wreqFallbackApplied = false;
|
||||
|
|
@ -106,6 +106,13 @@ define(Function.prototype, "m", {
|
|||
|
||||
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
|
||||
|
||||
// Currently, sentry and libdiscore Webpack instances are not meant to be patched.
|
||||
// As an extra measure, take advatange of the fact their files include the names and return early if it's one of them.
|
||||
// Later down we also include other measures to avoid patching them.
|
||||
if (["sentry", "libdiscore"].some(name => fileName?.toLowerCase()?.includes(name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality,
|
||||
// like the main Discord Webpack, have this property.
|
||||
// So if the setter is called with the Discord bundlePath, this means we should patch this instance and initialize the internal references to WebpackRequire.
|
||||
|
|
@ -116,7 +123,10 @@ define(Function.prototype, "m", {
|
|||
define(this, "p", { value: bundlePath });
|
||||
clearTimeout(bundlePathTimeout);
|
||||
|
||||
if (bundlePath !== "/assets/") {
|
||||
// libdiscore init Webpack instance always returns a constant string for the js filename of a chunk.
|
||||
// In that case, avoid patching this instance,
|
||||
// as it runs before the main Webpack instance and will make the WebpackRequire fallback not work properly, or init an wrongful main WebpackRequire.
|
||||
if (bundlePath !== "/assets/" || /(?:=>|{return)"[^"]/.exec(String(this.u))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -129,9 +139,9 @@ define(Function.prototype, "m", {
|
|||
}
|
||||
});
|
||||
|
||||
// In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry.
|
||||
// In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initing sentry.
|
||||
// This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property.
|
||||
// To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p.
|
||||
// To keep backwards compability, if this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p.
|
||||
// Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included,
|
||||
// which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry.
|
||||
|
||||
|
|
@ -436,12 +446,21 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno
|
|||
callback(exports, module.id);
|
||||
continue;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"Error while filtering or firing callback for Webpack waitFor subscription:\n", err,
|
||||
"\n\nModule exports:", exports,
|
||||
"\n\nFilter:", filter,
|
||||
"\n\nCallback:", callback
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof exports !== "object") {
|
||||
continue;
|
||||
}
|
||||
if (typeof exports !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const exportKey in exports) {
|
||||
for (const exportKey in exports) {
|
||||
try {
|
||||
// Some exports might have not been initialized yet due to circular imports, so try catch it.
|
||||
try {
|
||||
var exportValue = exports[exportKey];
|
||||
|
|
@ -454,9 +473,14 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno
|
|||
callback(exportValue, module.id);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"Error while filtering or firing callback for Webpack waitFor subscription:\n", err,
|
||||
"\n\nExport value:", exports,
|
||||
"\n\nFilter:", filter,
|
||||
"\n\nCallback:", callback
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -567,7 +591,7 @@ function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory):
|
|||
}
|
||||
|
||||
code = newCode;
|
||||
patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${pluginsList.join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(moduleId)}`;
|
||||
patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${pluginsList.join(", ")}\n${newCode}\n//# sourceURL=file:///WebpackModule${String(moduleId)}`;
|
||||
patchedFactory = (0, eval)(patchedSource);
|
||||
|
||||
if (!patchedBy.has(patch.plugin)) {
|
||||
|
|
|
|||
|
|
@ -145,9 +145,17 @@ function makePropertyNonEnumerable(target: Record<PropertyKey, any>, key: Proper
|
|||
}
|
||||
|
||||
export function _blacklistBadModules(requireCache: NonNullable<AnyWebpackRequire["c"]>, exports: ModuleExports, moduleId: PropertyKey) {
|
||||
if (shouldIgnoreValue(exports)) {
|
||||
makePropertyNonEnumerable(requireCache, moduleId);
|
||||
return true;
|
||||
try {
|
||||
if (shouldIgnoreValue(exports)) {
|
||||
makePropertyNonEnumerable(requireCache, moduleId);
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"Error while blacklisting module:\n", err,
|
||||
"\n\nModule id:", moduleId,
|
||||
"\n\nModule exports:", exports,
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof exports !== "object") {
|
||||
|
|
@ -156,17 +164,25 @@ export function _blacklistBadModules(requireCache: NonNullable<AnyWebpackRequire
|
|||
|
||||
let hasOnlyBadProperties = true;
|
||||
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;
|
||||
}
|
||||
// Some exports might have not been initialized yet due to circular imports, so try catch it.
|
||||
try {
|
||||
var exportValue = exports[exportKey];
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shouldIgnoreValue(exportValue)) {
|
||||
makePropertyNonEnumerable(exports, exportKey);
|
||||
} else {
|
||||
hasOnlyBadProperties = false;
|
||||
if (shouldIgnoreValue(exportValue)) {
|
||||
makePropertyNonEnumerable(exports, exportKey);
|
||||
} else {
|
||||
hasOnlyBadProperties = false;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"Error while blacklistng module:\n", err,
|
||||
"\n\nModule id:", moduleId,
|
||||
"\n\nExport value:", exportValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +468,29 @@ export function findStore(name: StoreNameFilter) {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const getLibdiscore = findByCode("libdiscoreWasm is not initialized");
|
||||
const libdiscoreExports = getLibdiscore();
|
||||
|
||||
for (const libdiscoreExportName in libdiscoreExports) {
|
||||
if (!libdiscoreExportName.endsWith("Store")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const storeName = libdiscoreExportName;
|
||||
const store = libdiscoreExports[storeName];
|
||||
|
||||
if (storeName === name) {
|
||||
res = store;
|
||||
}
|
||||
|
||||
if (fluxStores[storeName] == null) {
|
||||
fluxStores[storeName] = store;
|
||||
}
|
||||
}
|
||||
|
||||
} catch { }
|
||||
|
||||
if (res == null) {
|
||||
res = find(filters.byStoreName(name), { isIndirect: true });
|
||||
}
|
||||
|
|
@ -735,7 +774,7 @@ export function extract(id: string | number) {
|
|||
// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!!
|
||||
|
||||
0,${mod.toString()}
|
||||
//# sourceURL=ExtractedWebpackModule${id}
|
||||
//# sourceURL=file:///ExtractedWebpackModule${id}
|
||||
`;
|
||||
const extracted = (0, eval)(code);
|
||||
return extracted as Function;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue