diff --git a/eslint.config.mjs b/eslint.config.mjs index 54f53f745..94e80d072 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -90,7 +90,13 @@ export default tseslint.config( "no-invalid-regexp": "error", "no-constant-condition": ["error", { "checkLoops": false }], "no-duplicate-imports": "error", - "dot-notation": "error", + "@typescript-eslint/dot-notation": [ + "error", + { + "allowPrivateClassPropertyAccess": true, + "allowProtectedClassPropertyAccess": true + } + ], "no-useless-escape": [ "error", { diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index e88f1e2b9..5e71c8c5f 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -312,7 +312,7 @@ export const commonOpts = { logLevel: "info", bundle: true, watch, - minify: !watch, + minify: !watch && !IS_REPORTER, sourcemap: watch ? "inline" : "", legalComments: "linked", banner, diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 2ec9fba7c..03f803633 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -26,7 +26,7 @@ import { readFileSync } from "fs"; import pup, { JSHandle } from "puppeteer-core"; -for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) { +for (const variable of ["CHROMIUM_BIN"]) { if (!process.env[variable]) { console.error(`Missing environment variable ${variable}`); process.exit(1); @@ -214,7 +214,7 @@ page.on("console", async e => { switch (tag) { case "WebpackInterceptor:": - const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; + const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!; if (!patchFailMatch) break; console.error(await getText()); @@ -225,7 +225,7 @@ page.on("console", async e => { plugin, type, id, - match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), + match: regex, error: await maybeGetError(e.args()[3]) }); @@ -291,7 +291,7 @@ page.on("error", e => console.error("[Error]", e.message)); page.on("pageerror", e => { if (e.message.includes("Sentry successfully disabled")) return; - if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { + if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module") && !/^.{1,2}$/.test(e.message)) { console.error("[Page Error]", e.message); report.otherErrors.push(e.message); } else { @@ -299,20 +299,9 @@ page.on("pageerror", e => { } }); -async function reporterRuntime(token: string) { - Vencord.Webpack.waitFor( - "loginToken", - m => { - console.log("[PUP_DEBUG]", "Logging in with token..."); - m.loginToken(token); - } - ); -} - await page.evaluateOnNewDocument(` if (location.host.endsWith("discord.com")) { ${readFileSync("./dist/browser.js", "utf-8")}; - (${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)}); } `); diff --git a/src/Vencord.ts b/src/Vencord.ts index c4c6d4705..ef1c96cd9 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -23,10 +23,10 @@ export * as Util from "./utils"; export * as QuickCss from "./utils/quickCss"; export * as Updater from "./utils/updater"; export * as Webpack from "./webpack"; +export * as WebpackPatcher from "./webpack/patchWebpack"; export { PlainSettings, Settings }; import "./utils/quickCss"; -import "./webpack/patchWebpack"; import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab"; import { StartAt } from "@utils/types"; @@ -39,7 +39,7 @@ import { localStorage } from "./utils/localStorage"; import { relaunch } from "./utils/native"; import { getCloudSettings, putCloudSettings } from "./utils/settingsSync"; import { checkForUpdates, update, UpdateLogger } from "./utils/updater"; -import { onceReady } from "./webpack"; +import { onceDiscordLoaded } from "./webpack"; import { SettingsRouter } from "./webpack/common"; if (IS_REPORTER) { @@ -86,7 +86,7 @@ async function syncSettings() { } async function init() { - await onceReady; + await onceDiscordLoaded; startAllPlugins(StartAt.WebpackReady); syncSettings(); @@ -125,7 +125,7 @@ async function init() { const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false); if (pendingPatches.length) PMLogger.warn( - "Webpack has finished initialising, but some patches haven't been applied yet.", + "Webpack has finished initializing, but some patches haven't been applied yet.", "This might be expected since some Modules are lazy loaded, but please verify", "that all plugins are working as intended.", "You are seeing this warning because this is a Development build of Vencord.", diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 7a041f1ee..471d9c1d6 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -17,7 +17,6 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { ComponentType, HTMLProps } from "react"; import Plugins from "~plugins"; @@ -30,7 +29,7 @@ export interface ProfileBadge { /** The tooltip to show on hover. Required for image badges */ description?: string; /** Custom component for the badge (tooltip not included) */ - component?: ComponentType; + component?: React.ComponentType; /** The custom image to use */ image?: string; link?: string; @@ -39,7 +38,7 @@ export interface ProfileBadge { /** Should the user display this badge? */ shouldShow?(userInfo: BadgeUserArgs): boolean; /** Optional props (e.g. style) for the badge, ignored for component badges */ - props?: HTMLProps; + props?: React.ComponentPropsWithoutRef<"img">; /** Insert at start or end? */ position?: BadgePosition; /** The badge name to display, Discord uses this. Required for component badges */ diff --git a/src/api/ChatButtons.tsx b/src/api/ChatButtons.tsx index fcb76fffc..b44ee29ad 100644 --- a/src/api/ChatButtons.tsx +++ b/src/api/ChatButtons.tsx @@ -8,13 +8,12 @@ import "./ChatButton.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Logger } from "@utils/Logger"; -import { waitFor } from "@webpack"; +import { findByProps } from "@webpack"; import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common"; import { Channel } from "discord-types/general"; import { HTMLProps, MouseEventHandler, ReactNode } from "react"; -let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>; -waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m); +const ChannelTextAreaClasses = findByProps>("buttonContainer", "channelTextArea"); export interface ChatBarProps { channel: Channel; diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index 4ae022c59..b41aa7d5f 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/mergeDefaults"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { MessageActions, SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const createBotMessage = findByCodeLazy('username:"Clyde"'); +const createBotMessage = findByCode('username:"Clyde"'); export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; diff --git a/src/api/MessagePopover.tsx b/src/api/MessagePopover.tsx index eb68ed2d6..decb4ffd1 100644 --- a/src/api/MessagePopover.tsx +++ b/src/api/MessagePopover.tsx @@ -19,18 +19,17 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Logger } from "@utils/Logger"; import { Channel, Message } from "discord-types/general"; -import type { ComponentType, MouseEventHandler } from "react"; const logger = new Logger("MessagePopover"); export interface ButtonItem { key?: string, label: string, - icon: ComponentType, + icon: React.ComponentType, message: Message, channel: Channel, - onClick?: MouseEventHandler, - onContextMenu?: MouseEventHandler; + onClick?: React.MouseEventHandler, + onContextMenu?: React.MouseEventHandler; } export type getButtonItem = (message: Message) => ButtonItem | null; diff --git a/src/api/Notices.ts b/src/api/Notices.ts index 6d20087a7..9b53d8567 100644 --- a/src/api/Notices.ts +++ b/src/api/Notices.ts @@ -16,23 +16,22 @@ * along with this program. If not, see . */ -import { waitFor } from "@webpack"; +import { find } from "@webpack"; -let NoticesModule: any; -waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m); +const Notices = find(m => m.show && m.dismiss && !m.suppressAll); export const noticesQueue = [] as any[]; export let currentNotice: any = null; export function popNotice() { - NoticesModule.dismiss(); + Notices.dismiss(); } export function nextNotice() { currentNotice = noticesQueue.shift(); if (currentNotice) { - NoticesModule.show(...currentNotice, "VencordNotice"); + Notices.show(...currentNotice, "VencordNotice"); } } diff --git a/src/api/Settings.ts b/src/api/Settings.ts index ac116f547..6ac029984 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -32,9 +32,10 @@ export interface Settings { autoUpdate: boolean; autoUpdateNotification: boolean, useQuickCss: boolean; - enableReactDevtools: boolean; themeLinks: string[]; + eagerPatches: boolean; enabledThemes: string[]; + enableReactDevtools: boolean; frameless: boolean; transparent: boolean; winCtrlQ: boolean; @@ -81,6 +82,7 @@ const DefaultSettings: Settings = { autoUpdateNotification: true, useQuickCss: true, themeLinks: [], + eagerPatches: IS_REPORTER, enabledThemes: [], enableReactDevtools: false, frameless: false, @@ -116,7 +118,6 @@ const saveSettingsOnFrequentAction = debounce(async () => { } }, 60_000); - export const SettingsStore = new SettingsStoreClass(settings, { readOnly: true, getDefaultValue({ diff --git a/src/api/Styles.ts b/src/api/Styles.ts index 6b0ac2cdf..023299c17 100644 --- a/src/api/Styles.ts +++ b/src/api/Styles.ts @@ -89,8 +89,8 @@ export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnec * // -- plugin.ts -- * import pluginStyle from "./plugin.css?managed"; * import { setStyleVars } from "@api/Styles"; - * import { findByPropsLazy } from "@webpack"; - * const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... } + * import { findByProps } from "@webpack"; + * const classNames = findByProps("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... } * * // Inside some plugin method like "start()" * setStyleClassNames(pluginStyle, classNames); diff --git a/src/api/UserSettings.ts b/src/api/UserSettings.ts index 4de92a81a..18d360485 100644 --- a/src/api/UserSettings.ts +++ b/src/api/UserSettings.ts @@ -17,8 +17,7 @@ */ import { proxyLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; +import { findByFactoryCode } from "@webpack"; interface UserSettingDefinition { /** @@ -43,12 +42,7 @@ interface UserSettingDefinition { userSettingsAPIName: string; } -export const UserSettings: Record> | undefined = proxyLazyWebpack(() => { - const modId = findModuleId('"textAndImages","renderSpoilers"'); - if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module."); - - return wreq(modId as any); -}); +export const UserSettings = findByFactoryCode>>('"textAndImages","renderSpoilers"'); /** * Get the setting with the given setting group and name. @@ -56,7 +50,7 @@ export const UserSettings: Record> | und * @param group The setting group * @param name The name of the setting */ -export function getUserSetting(group: string, name: string): UserSettingDefinition | undefined { +export function getUserSetting(group: string, name: string): UserSettingDefinition { if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency."); for (const key in UserSettings) { @@ -66,10 +60,12 @@ export function getUserSetting(group: string, name: string): UserSettin return userSetting; } } + + throw new Error(`UserSettingsAPI: Setting ${group}.${name} not found.`); } /** - * {@link getUserSettingDefinition}, lazy. + * Lazy version of {@link getUserSetting} * * Get the setting with the given setting group and name. * diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx index 41c5ef0c1..03ec96fc1 100644 --- a/src/components/CodeBlock.tsx +++ b/src/components/CodeBlock.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Parser } from "@webpack/common"; -const CodeContainerClasses = findByPropsLazy("markup", "codeContainer"); +const CodeContainerClasses = findByProps("markup", "codeContainer"); /** * Renders code in a Discord codeblock diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index ea2e02b51..2afe2fbe6 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -18,7 +18,7 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { LazyComponent } from "@utils/react"; +import { LazyComponent, LazyComponentType } from "@utils/react"; import { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -104,8 +104,8 @@ const ErrorBoundary = LazyComponent(() => { } }; }) as - React.ComponentType> & { - wrap(Component: React.ComponentType, errorBoundaryProps?: Omit, "wrappedProps">): React.FunctionComponent; + LazyComponentType> & { + wrap(Component: React.ComponentType, errorBoundaryProps?: Omit, "wrappedProps">): React.FunctionComponent; }; ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => ( diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 8b14283b8..a340dbc16 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -24,13 +24,12 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { gitRemote } from "@shared/vencordUserAgent"; -import { proxyLazy } from "@utils/lazy"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { OptionType, Plugin } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; +import { find, findByProps, findComponentByCode } from "@webpack"; +import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserUtils } from "@webpack/common"; import { User } from "discord-types/general"; import { Constructor } from "type-fest"; @@ -50,9 +49,9 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton"; const cl = classNameFactory("vc-plugin-modal-"); -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); -const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); -const UserRecord: Constructor> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); +const UserRecord = find>>(m => m?.prototype?.getAvatarURL && m?.prototype?.hasHadPremium); interface PluginModalProps extends ModalProps { plugin: Plugin; diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index c3b6e9082..9bbed1906 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -33,19 +33,19 @@ import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import { Plugin } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common"; import Plugins, { ExcludedPlugins } from "~plugins"; // Avoid circular dependency -const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")); +const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins"); const cl = classNameFactory("vc-plugins-"); const logger = new Logger("PluginSettings", "#a6d189"); -const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error"); -const ButtonClasses = findByPropsLazy("button", "disabled", "enabled"); +const InputStyles = findByProps("inputWrapper", "inputDefault", "error"); +const ButtonClasses = findByProps("button", "disabled", "enabled"); function showErrorToast(message: string) { @@ -100,7 +100,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on // If we're enabling a plugin, make sure all deps are enabled recursively. if (!wasEnabled) { - const { restartNeeded, failures } = startDependenciesRecursive(plugin); + const { restartNeeded, failures } = PluginManager.startDependenciesRecursive(plugin); if (failures.length) { logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`); showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null); @@ -126,7 +126,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on return; } - const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin); + const result = wasEnabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin); if (!result) { settings.enabled = false; diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 10904e141..4e72054dd 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -19,7 +19,7 @@ import "./Switch.css"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; interface SwitchProps { checked: boolean; @@ -29,7 +29,7 @@ interface SwitchProps { const SWITCH_ON = "var(--green-360)"; const SWITCH_OFF = "var(--primary-400)"; -const SwitchClasses = findByPropsLazy("slider", "input", "container"); +const SwitchClasses = findByProps("slider", "input", "container"); export function Switch({ checked, onChange, disabled }: SwitchProps) { return ( diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index fd33c09df..eac5db4d9 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -22,7 +22,7 @@ import { Margins } from "@utils/margins"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { makeCodeblock } from "@utils/text"; import { Patch, ReplaceFn } from "@utils/types"; -import { search } from "@webpack"; +import { searchFactories } from "@webpack"; import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; import { SettingsTab, wrapTab } from "./shared"; @@ -33,7 +33,7 @@ if (IS_DEV) { } const findCandidates = debounce(function ({ find, setModule, setError }) { - const candidates = search(find); + const candidates = searchFactories(find); const keys = Object.keys(candidates); const len = keys.length; if (len === 0) @@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError const [compileResult, setCompileResult] = React.useState<[boolean, string]>(); const [patchedCode, matchResult, diff] = React.useMemo(() => { - const src: string = fact.toString().replaceAll("\n", ""); + const src = String(fact).replaceAll("\n", ""); try { new RegExp(match); diff --git a/src/components/VencordSettings/ThemesTab.tsx b/src/components/VencordSettings/ThemesTab.tsx index f718ab11f..538c86c7b 100644 --- a/src/components/VencordSettings/ThemesTab.tsx +++ b/src/components/VencordSettings/ThemesTab.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Settings, useSettings } from "@api/Settings"; +import { useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import { Flex } from "@components/Flex"; import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons"; @@ -27,9 +27,9 @@ import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { showItemInFolder } from "@utils/native"; import { useAwaiter } from "@utils/react"; -import { findLazy } from "@webpack"; +import { findComponentByFields } from "@webpack"; import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; -import type { ComponentType, Ref, SyntheticEvent } from "react"; +import type { Ref, SyntheticEvent } from "react"; import Plugins from "~plugins"; @@ -37,14 +37,14 @@ import { AddonCard } from "./AddonCard"; import { QuickAction, QuickActionCard } from "./quickActions"; import { SettingsTab, wrapTab } from "./shared"; -type FileInput = ComponentType<{ +type FileInputProps = { ref: Ref; onChange: (e: SyntheticEvent) => void; multiple?: boolean; filters?: { name?: string; extensions: string[]; }[]; -}>; +}; -const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef); +const FileInput = findComponentByFields("activateUploadDialogue", "setRef"); const cl = classNameFactory("vc-settings-theme-"); @@ -257,7 +257,7 @@ function ThemesTab() { Icon={PaintbrushIcon} /> - {Settings.plugins.ClientTheme.enabled && ( + {Vencord.Plugins.isPluginEnabled("ClientTheme") && ( openPluginModal(Plugins.ClientTheme)} diff --git a/src/components/VencordSettings/quickActions.tsx b/src/components/VencordSettings/quickActions.tsx index 6cc57180a..183c83254 100644 --- a/src/components/VencordSettings/quickActions.tsx +++ b/src/components/VencordSettings/quickActions.tsx @@ -8,13 +8,12 @@ import "./quickActions.css"; import { classNameFactory } from "@api/Styles"; import { Card } from "@webpack/common"; -import type { ComponentType, PropsWithChildren, ReactNode } from "react"; const cl = classNameFactory("vc-settings-quickActions-"); export interface QuickActionProps { - Icon: ComponentType<{ className?: string; }>; - text: ReactNode; + Icon: React.ComponentType<{ className?: string; }>; + text: React.ReactNode; action?: () => void; disabled?: boolean; } @@ -30,7 +29,7 @@ export function QuickAction(props: QuickActionProps) { ); } -export function QuickActionCard(props: PropsWithChildren) { +export function QuickActionCard(props: React.PropsWithChildren) { return ( {props.children} diff --git a/src/components/VencordSettings/shared.tsx b/src/components/VencordSettings/shared.tsx index 1c5f37d82..ddf9d90a3 100644 --- a/src/components/VencordSettings/shared.tsx +++ b/src/components/VencordSettings/shared.tsx @@ -24,9 +24,8 @@ import { handleComponentFailed } from "@components/handleComponentFailed"; import { Margins } from "@utils/margins"; import { onlyOnce } from "@utils/onlyOnce"; import { Forms, Text } from "@webpack/common"; -import type { ComponentType, PropsWithChildren } from "react"; -export function SettingsTab({ title, children }: PropsWithChildren<{ title: string; }>) { +export function SettingsTab({ title, children }: React.PropsWithChildren<{ title: string; }>) { return ( , tab: string) { +export function wrapTab

(component: React.ComponentType

, tab: string) { return ErrorBoundary.wrap(component, { message: `Failed to render the ${tab} tab. If this issue persists, try using the installer to reinstall!`, onError: handleSettingsTabError, diff --git a/src/debug/Tracer.ts b/src/debug/Tracer.ts index 7d80f425c..37ea4cc05 100644 --- a/src/debug/Tracer.ts +++ b/src/debug/Tracer.ts @@ -23,35 +23,61 @@ if (IS_DEV || IS_REPORTER) { var logger = new Logger("Tracer", "#FFD166"); } -const noop = function () { }; - -export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop : +export const beginTrace = !(IS_DEV || IS_REPORTER) ? () => { } : function beginTrace(name: string, ...args: any[]) { - if (name in traces) + if (name in traces) { throw new Error(`Trace ${name} already exists!`); + } traces[name] = [performance.now(), args]; }; -export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) { - const end = performance.now(); +export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 : + function finishTrace(name: string) { + const end = performance.now(); - const [start, args] = traces[name]; - delete traces[name]; + const [start, args] = traces[name]; + delete traces[name]; - logger.debug(`${name} took ${end - start}ms`, args); -}; + const totalTime = end - start; + logger.debug(`${name} took ${totalTime}ms`, args); + + return totalTime; + }; type Func = (...args: any[]) => any; type TraceNameMapper = (...args: Parameters) => string; -const noopTracer = - (name: string, f: F, mapper?: TraceNameMapper) => f; +function noopTracerWithResults(name: string, f: F, mapper?: TraceNameMapper) { + return function (this: unknown, ...args: Parameters): [ReturnType, number] { + return [f.apply(this, args), 0]; + }; +} + +function noopTracer(name: string, f: F, mapper?: TraceNameMapper) { + return f; +} + +export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER) + ? noopTracerWithResults + : function traceFunctionWithResults(name: string, f: F, mapper?: TraceNameMapper): (this: unknown, ...args: Parameters) => [ReturnType, number] { + return function (this: unknown, ...args: Parameters) { + const traceName = mapper?.(...args) ?? name; + + beginTrace(traceName, ...arguments); + try { + return [f.apply(this, args), finishTrace(traceName)]; + } catch (e) { + finishTrace(traceName); + throw e; + } + }; + }; export const traceFunction = !(IS_DEV || IS_REPORTER) ? noopTracer : function traceFunction(name: string, f: F, mapper?: TraceNameMapper): F { - return function (this: any, ...args: Parameters) { + return function (this: unknown, ...args: Parameters) { const traceName = mapper?.(...args) ?? name; beginTrace(traceName, ...arguments); diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index 73a89504f..7fa0e3ada 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -8,10 +8,11 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch } from "@utils/patches"; import * as Webpack from "@webpack"; import { wreq } from "@webpack"; - -const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); +import { AnyModuleFactory, ModuleFactory } from "webpack"; export async function loadLazyChunks() { + const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); + try { LazyChunkLoaderLogger.log("Loading all chunks..."); @@ -25,16 +26,12 @@ export async function loadLazyChunks() { // True if resolved, false otherwise const chunksSearchPromises = [] as Array<() => boolean>; - const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g); async function searchAndLoadLazyChunks(factoryCode: string) { const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>(); - // Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before - // the chunk containing the component - const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT"); - await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : []; @@ -69,19 +66,14 @@ export async function loadLazyChunks() { await Promise.all( Array.from(validChunkGroups) .map(([chunkIds]) => - Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) + Promise.all(chunkIds.map(id => wreq.e(id))) ) ); // Requires the entry points for all valid chunk groups for (const [, entryPoint] of validChunkGroups) { try { - if (shouldForceDefer) { - deferredRequires.add(entryPoint); - continue; - } - - if (wreq.m[entryPoint]) wreq(entryPoint as any); + if (wreq.m[entryPoint]) wreq(entryPoint); } catch (err) { console.error(err); } @@ -109,32 +101,33 @@ export async function loadLazyChunks() { }, 0); } - Webpack.factoryListeners.add(factory => { + function factoryListener(factory: AnyModuleFactory | ModuleFactory) { let isResolved = false; - searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - }); - - for (const factoryId in wreq.m) { - let isResolved = false; - searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); + searchAndLoadLazyChunks(String(factory)) + .then(() => isResolved = true) + .catch(() => isResolved = true); chunksSearchPromises.push(() => isResolved); } + Webpack.factoryListeners.add(factoryListener); + for (const factoryId in wreq.m) { + factoryListener(wreq.m[factoryId]); + } + await chunksSearchingDone; + Webpack.factoryListeners.delete(factoryListener); // Require deferred entry points for (const deferredRequire of deferredRequires) { - wreq!(deferredRequire as any); + wreq(deferredRequire); } // All chunks Discord has mapped to asset files, even if they are not used anymore const allChunks = [] as number[]; // Matches "id" or id: - for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) { + for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) { const id = currentMatch[1] ?? currentMatch[2]; if (id == null) continue; @@ -143,7 +136,8 @@ export async function loadLazyChunks() { if (allChunks.length === 0) throw new Error("Failed to get all chunks"); - // Chunks that are not loaded (not used) by Discord code anymore + // Chunks which our regex could not catch to load + // It will always contain WebWorker assets, and also currently contains some language packs which are loaded differently const chunksLeft = allChunks.filter(id => { return !(validChunks.has(id) || invalidChunks.has(id)); }); @@ -153,12 +147,9 @@ export async function loadLazyChunks() { .then(r => r.text()) .then(t => t.includes("importScripts(")); - // Loads and requires a chunk + // Loads the chunk. Currently this only happens with the language packs which are loaded differently if (!isWorkerAsset) { - await wreq.e(id as any); - // Technically, the id of the chunk does not match the entry point - // But, still try it because we have no way to get the actual entry point - if (wreq.m[id]) wreq(id as any); + await wreq.e(id); } })); diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index ddd5e5f18..2b8f3f9a5 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -4,22 +4,39 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; +import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner"; import * as Webpack from "@webpack"; -import { patches } from "plugins"; +import { addPatch, patches } from "plugins"; import { loadLazyChunks } from "./loadLazyChunks"; -const ReporterLogger = new Logger("Reporter"); - async function runReporter() { + const ReporterLogger = new Logger("Reporter"); + try { ReporterLogger.log("Starting test..."); - let loadLazyChunksResolve: (value: void | PromiseLike) => void; + let loadLazyChunksResolve: (value: void) => void; const loadLazyChunksDone = new Promise(r => loadLazyChunksResolve = r); - Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve))); + // The main patch for starting the reporter chunk loading + addPatch({ + find: '"Could not find app-mount"', + replacement: { + match: /(?<="use strict";)/, + replace: "Vencord.Webpack._initReporter();" + } + }, "Vencord Reporter"); + + // @ts-ignore + Vencord.Webpack._initReporter = function () { + // initReporter is called in the patched entry point of Discord + // setImmediate to only start searching for lazy chunks after Discord initialized the app + setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0); + }; + await loadLazyChunksDone; for (const patch of patches) { @@ -28,52 +45,158 @@ async function runReporter() { } } - for (const [searchType, args] of Webpack.lazyWebpackSearchHistory) { - let method = searchType; - - if (searchType === "findComponent") method = "find"; - if (searchType === "findExportedComponent") method = "findByProps"; - if (searchType === "waitFor" || searchType === "waitForComponent") { - if (typeof args[0] === "string") method = "findByProps"; - else method = "find"; + for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) { + if (totalTime > 3) { + new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`); } - if (searchType === "waitForStore") method = "findStore"; + } - let result: any; + await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => { + args = [...args]; + + let result = null as any; try { - if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { - const [factory] = args; - result = factory(); - } else if (method === "extractAndLoadChunks") { - const [code, matcher] = args; + switch (searchType) { + case "webpackDependantLazy": + case "webpackDependantLazyComponent": { + const [factory] = args; + result = factory(); + break; + } + case "extractAndLoadChunks": { + const extractAndLoadChunks = args.shift(); - result = await Webpack.extractAndLoadChunks(code, matcher); - if (result === false) result = null; - } else if (method === "mapMangledModule") { - const [code, mapper] = args; + result = await extractAndLoadChunks(); + if (result === false) { + result = null; + } - result = Webpack.mapMangledModule(code, mapper); - if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail"); - } else { - // @ts-ignore - result = Webpack[method](...args); + break; + } + default: { + const findResult = args.shift(); + + if (findResult != null) { + if (findResult.$$vencordCallbackCalled != null && findResult.$$vencordCallbackCalled()) { + result = findResult; + break; + } + + if (findResult[SYM_PROXY_INNER_GET] != null) { + result = findResult[SYM_PROXY_INNER_VALUE]; + break; + } + + if (findResult[SYM_LAZY_COMPONENT_INNER] != null) { + result = findResult[SYM_LAZY_COMPONENT_INNER](); + break; + } + + if (searchType === "mapMangledModule") { + result = findResult; + + for (const innerMap in result) { + if ( + (result[innerMap][SYM_PROXY_INNER_GET] != null && result[innerMap][SYM_PROXY_INNER_VALUE] == null) || + (result[innerMap][SYM_LAZY_COMPONENT_INNER] != null && result[innerMap][SYM_LAZY_COMPONENT_INNER]() == null) + ) { + throw new Error("Webpack Find Fail"); + } + } + + break; + } + + // This can happen if a `find` was immediately found + result = findResult; + } + + break; + } } - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); + if (result == null) { + throw new Error("Webpack Find Fail"); + } } catch (e) { let logMessage = searchType; - if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; - else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`; - else if (method === "mapMangledModule") { - const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null); - logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; + let filterName = ""; + let parsedArgs = args; + + if (args[0].$$vencordProps != null) { + if (["find", "findComponent", "waitFor"].includes(searchType)) { + filterName = args[0].$$vencordProps[0]; + parsedArgs = args[0].$$vencordProps.slice(1); + } else { + parsedArgs = args[0].$$vencordProps; + } + } + + function stringifyFilter(code: Webpack.CodeFilterWithSingle) { + if (Array.isArray(code)) { + return `[${code.map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")}]`; + } + + return code instanceof RegExp ? String(code) : JSON.stringify(code); + } + + // if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions), + // so log the filter function instead + if ( + parsedArgs === args && + ["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType) + ) { + let filter = String(parsedArgs[0]); + if (filter.length > 150) { + filter = filter.slice(0, 147) + "..."; + } + + logMessage += `(${filter})`; + } else if (searchType === "extractAndLoadChunks") { + const [code, matcher] = parsedArgs; + + let regexStr: string; + if (matcher === Webpack.DefaultExtractAndLoadChunksRegex) { + regexStr = "DefaultExtractAndLoadChunksRegex"; + } else { + regexStr = String(matcher); + } + + logMessage += `(${stringifyFilter(code)}, ${regexStr})`; + } else if (searchType === "mapMangledModule") { + const [code, mappers] = parsedArgs; + + const parsedFailedMappers = Object.entries(mappers) + .filter(([key]) => + result == null || + (result[key]?.[SYM_PROXY_INNER_GET] != null && result[key][SYM_PROXY_INNER_VALUE] == null) || + (result[key]?.[SYM_LAZY_COMPONENT_INNER] != null && result[key][SYM_LAZY_COMPONENT_INNER]() == null) + ) + .map(([key, filter]) => { + let parsedFilter: string; + + if (filter.$$vencordProps != null) { + const filterName = filter.$$vencordProps[0]; + parsedFilter = `${filterName}(${filter.$$vencordProps.slice(1).map((arg: any) => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`; + } else { + parsedFilter = String(filter); + if (parsedFilter.length > 150) { + parsedFilter = parsedFilter.slice(0, 147) + "..."; + } + } + + return [key, parsedFilter]; + }); + + logMessage += `(${stringifyFilter(code)}, {\n${parsedFailedMappers.map(([key, parsedFilter]) => `\t${key}: ${parsedFilter}`).join(",\n")}\n})`; + } else { + logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(stringifyFilter).join(", ")})${filterName.length ? ")" : ""}`; } - else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; ReporterLogger.log("Webpack Find Fail:", logMessage); } - } + })); ReporterLogger.log("Finished test"); } catch (e) { @@ -81,4 +204,5 @@ async function runReporter() { } } -runReporter(); +// Run after the Vencord object has been created +setTimeout(runReporter, 0); diff --git a/src/globals.d.ts b/src/globals.d.ts index e20ca4b71..e02ad0416 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -19,6 +19,10 @@ import { LoDashStatic } from "lodash"; declare global { + type AnyRecord = Record; + type AnyComponentType

= React.ComponentType

& AnyRecord; + type AnyComponentTypeWithChildren

= AnyComponentType>; + /** * This exists only at build time, so references to it in patches should insert it * via String interpolation OR use different replacement code based on this @@ -64,13 +68,8 @@ declare global { export var Vesktop: any; export var VesktopNative: any; - interface Window { - webpackChunkdiscord_app: { - push(chunk: any): any; - pop(): any; - }; + interface Window extends AnyRecord { _: LoDashStatic; - [k: string]: any; } } diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 8d6a1e76d..326834dcd 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, StartAt } from "@utils/types"; +import { WebpackRequire } from "webpack"; const settings = definePluginSettings({ disableAnalytics: { @@ -81,9 +82,9 @@ export default definePlugin({ Object.defineProperty(Function.prototype, "g", { configurable: true, - set(v: any) { + set(this: WebpackRequire, globalObj: WebpackRequire["g"]) { Object.defineProperty(this, "g", { - value: v, + value: globalObj, configurable: true, enumerable: true, writable: true @@ -92,11 +93,11 @@ export default definePlugin({ // Ensure this is most likely the Sentry WebpackInstance. // Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: ) to include it const { stack } = new Error(); - if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || !String(this).includes("exports:{}") || this.c != null) { + if (this.c != null || !stack?.includes("http") || !String(this).includes("exports:{}")) { return; } - const assetPath = stack?.match(/\/assets\/.+?\.js/)?.[0]; + const assetPath = stack.match(/http.+?(?=:\d+?:\d+?$)/m)?.[0]; if (!assetPath) { return; } @@ -106,7 +107,8 @@ export default definePlugin({ srcRequest.send(); // Final condition to see if this is the Sentry WebpackInstance - if (!srcRequest.responseText.includes("window.DiscordSentry=")) { + // This is matching window.DiscordSentry=, but without `window` to avoid issues on some proxies + if (!srcRequest.responseText.includes(".DiscordSentry=")) { return; } diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index be220db1a..813c2ace8 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab"; import CloudTab from "@components/VencordSettings/CloudTab"; import PatchHelperTab from "@components/VencordSettings/PatchHelperTab"; @@ -33,11 +33,27 @@ import gitHash from "~git-hash"; type SectionType = "HEADER" | "DIVIDER" | "CUSTOM"; type SectionTypes = Record; +const settings = definePluginSettings({ + settingsLocation: { + type: OptionType.SELECT, + description: "Where to put the Vencord settings section", + options: [ + { label: "At the very top", value: "top" }, + { label: "Above the Nitro section", value: "aboveNitro", default: true }, + { label: "Below the Nitro section", value: "belowNitro" }, + { label: "Above Activity Settings", value: "aboveActivity" }, + { label: "Below Activity Settings", value: "belowActivity" }, + { label: "At the very bottom", value: "bottom" }, + ] + } +}); + export default definePlugin({ name: "Settings", description: "Adds Settings UI and debug info", authors: [Devs.Ven, Devs.Megu], required: true, + settings, patches: [ { @@ -136,12 +152,12 @@ export default definePlugin({ ].filter(Boolean); }, - isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) { - const firstChild = settings?.[0]; + isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) { + const firstChild = settingsChilds?.[0]; // lowest two elements... sanity backup if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true; - const { settingsLocation } = Settings.plugins.Settings; + const { settingsLocation } = settings.store; if (settingsLocation === "bottom") return firstChild === "LOGOUT"; if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG"; @@ -181,21 +197,6 @@ export default definePlugin({ }; }, - options: { - settingsLocation: { - type: OptionType.SELECT, - description: "Where to put the Vencord settings section", - options: [ - { label: "At the very top", value: "top" }, - { label: "Above the Nitro section", value: "aboveNitro", default: true }, - { label: "Below the Nitro section", value: "belowNitro" }, - { label: "Above Activity Settings", value: "aboveActivity" }, - { label: "Below Activity Settings", value: "belowActivity" }, - { label: "At the very bottom", value: "bottom" }, - ] - }, - }, - get electronVersion() { return VencordNative.native.getVersions().electron || window.armcord?.electron || null; }, diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 432896fc7..05b151bd9 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -59,7 +59,7 @@ const TrustedRolesIds = [ const AsyncFunction = async function () { }.constructor; -const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame"); async function forceUpdate() { const outdated = await checkForUpdates(); diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index fe5df48ad..cda6c6bc5 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common"; import { User } from "discord-types/general"; @@ -19,11 +19,11 @@ interface UserProfileProps { originalPopout: () => React.ReactNode; } -const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined"); -const styles = findByPropsLazy("accountProfilePopoutWrapper"); +const UserProfile = findComponentByCode("UserProfilePopoutWrapper: user cannot be undefined"); +const styles = findByProps("accountProfilePopoutWrapper"); let openAlternatePopout = false; -let accountPanelRef: React.MutableRefObject | null> = { current: null }; +let accountPanelRef: React.MutableRefObject = { current: null }; const AccountPanelContextMenu = ErrorBoundary.wrap(() => { const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]); diff --git a/src/plugins/anonymiseFileNames/index.tsx b/src/plugins/anonymiseFileNames/index.tsx index 526ccd12e..c7f5afe9e 100644 --- a/src/plugins/anonymiseFileNames/index.tsx +++ b/src/plugins/anonymiseFileNames/index.tsx @@ -21,12 +21,12 @@ 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"; +import { findByProps, findComponentByCode } from "@webpack"; type AnonUpload = Upload & { anonymise?: boolean; }; -const ActionBarIcon = findByCodeLazy(".actionBarIcon)"); -const UploadDraft = findByPropsLazy("popFirstFile", "update"); +const ActionBarIcon = findComponentByCode(".actionBarIcon)"); +const UploadDraft = findByProps("popFirstFile", "update"); const enum Methods { Random, diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index df307e756..7d03643fc 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import definePlugin, { ReporterTestable } from "@utils/types"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID"); +const fetchApplicationsRPC = findByCode("APPLICATION_RPC(", "Client ID"); async function lookupAsset(applicationId: string, key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 53d24ed93..05f7833b0 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -17,15 +17,15 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { findByProps, findComponentByCode, findStore } from "@webpack"; import { useStateFromStores } from "@webpack/common"; import type { CSSProperties } from "react"; import { ExpandedGuildFolderStore, settings } from "."; -const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); -const Animations = findByPropsLazy("a", "animated", "useTransition"); -const GuildsBar = findComponentByCodeLazy('("guildsnav")'); +const ChannelRTCStore = findStore("ChannelRTCStore"); +const Animations = findByProps("a", "animated", "useTransition"); +const GuildsBar = findComponentByCode('("guildsnav")'); export default ErrorBoundary.wrap(guildsBarProps => { const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index c2969988b..0e930bfe0 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -19,7 +19,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; +import { find, findByProps, findStore } from "@webpack"; import { FluxDispatcher, i18n, useMemo } from "@webpack/common"; import FolderSideBar from "./FolderSideBar"; @@ -30,10 +30,10 @@ enum FolderIconDisplay { MoreThanOneFolderExpanded } -export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); -const SortedGuildStore = findStoreLazy("SortedGuildStore"); -const GuildsTree = findLazy(m => m.prototype?.moveNextTo); -const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); +export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore"); +const SortedGuildStore = findStore("SortedGuildStore"); +const GuildsTree = find(m => m.prototype?.moveNextTo); +const FolderUtils = findByProps("move", "toggleGuildFolderExpand"); let lastGuildId = null as string | null; let dispatchingFoldersClose = false; diff --git a/src/plugins/betterNotes/index.tsx b/src/plugins/betterNotes/index.tsx index 63fcf6477..2a56e2961 100644 --- a/src/plugins/betterNotes/index.tsx +++ b/src/plugins/betterNotes/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { definePluginSettings, Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { canonicalizeMatch } from "@utils/patches"; import definePlugin, { OptionType } from "@utils/types"; @@ -31,7 +31,7 @@ const settings = definePluginSettings({ noSpellCheck: { type: OptionType.BOOLEAN, description: "Disable spellcheck in notes", - disabled: () => Settings.plugins.BetterNotesBox.hide, + disabled: () => settings.store.hide, default: false } }); diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index bf4cf0f37..ae7419af2 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -10,12 +10,12 @@ import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; -const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); +const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild"); -const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!; +const DeveloperMode = getUserSettingLazy("appearance", "developerMode"); function PencilIcon() { return ( diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index a8cadd8b0..eefeb76fd 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -16,16 +16,32 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { Clipboard, Toasts } from "@webpack/common"; +const settings = definePluginSettings({ + bothStyles: { + type: OptionType.BOOLEAN, + description: "Show both role dot and coloured names", + restartNeeded: true, + default: false, + }, + copyRoleColorInProfilePopout: { + type: OptionType.BOOLEAN, + description: "Allow click on role dot in profile popout to copy role color", + restartNeeded: true, + default: false + } +}); + export default definePlugin({ name: "BetterRoleDot", authors: [Devs.Ven, Devs.AutumnVN], description: "Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously", + settings, patches: [ { @@ -39,7 +55,7 @@ export default definePlugin({ find: '"dot"===', all: true, noWarn: true, - predicate: () => Settings.plugins.BetterRoleDot.bothStyles, + predicate: () => settings.store.bothStyles, replacement: { match: /"(?:username|dot)"===\i(?!\.\i)/g, replace: "true", @@ -49,7 +65,7 @@ export default definePlugin({ { find: ".ADD_ROLE_A11Y_LABEL", all: true, - predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, + predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles, noWarn: true, replacement: { match: /"dot"===\i/, @@ -59,7 +75,7 @@ export default definePlugin({ { find: ".roleVerifiedIcon", all: true, - predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, + predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles, noWarn: true, replacement: { match: /"dot"===\i/, @@ -68,21 +84,6 @@ export default definePlugin({ } ], - options: { - bothStyles: { - type: OptionType.BOOLEAN, - description: "Show both role dot and coloured names", - restartNeeded: true, - default: false, - }, - copyRoleColorInProfilePopout: { - type: OptionType.BOOLEAN, - description: "Allow click on role dot in profile popout to copy role color", - restartNeeded: true, - default: false - } - }, - copyToClipBoard(color: string) { Clipboard.copy(color); Toasts.show({ diff --git a/src/plugins/betterSessions/index.tsx b/src/plugins/betterSessions/index.tsx index 598e01042..3b8fa99df 100644 --- a/src/plugins/betterSessions/index.tsx +++ b/src/plugins/betterSessions/index.tsx @@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; +import { findByProps, findExportedComponent, findStore } from "@webpack"; import { Constants, React, RestAPI, Tooltip } from "@webpack/common"; import { RenameButton } from "./components/RenameButton"; import { Session, SessionInfo } from "./types"; import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils"; -const AuthSessionsStore = findStoreLazy("AuthSessionsStore"); -const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open"); +const AuthSessionsStore = findStore("AuthSessionsStore"); +const UserSettingsModal = findByProps("saveAccountChanges", "open"); -const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer"); -const SessionIconClasses = findByPropsLazy("sessionIcon"); +const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer"); +const SessionIconClasses = findByProps("sessionIcon"); -const BlobMask = findExportedComponentLazy("BlobMask"); +const BlobMask = findExportedComponent("BlobMask"); const settings = definePluginSettings({ backgroundCheck: { diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index f0dd89a7a..c28bae522 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -8,8 +8,10 @@ import { definePluginSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; +import { SYM_PROXY_INNER_VALUE } from "@utils/proxyInner"; +import { NoopComponent } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { waitFor } from "@webpack"; +import { findByProps } from "@webpack"; import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common"; import type { HTMLAttributes, ReactElement } from "react"; @@ -18,8 +20,7 @@ import PluginsSubmenu from "./PluginsSubmenu"; type SettingsEntry = { section: string, label: string; }; const cl = classNameFactory(""); -let Classes: Record; -waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m); +const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers"); const settings = definePluginSettings({ disableFade: { @@ -142,7 +143,7 @@ export default definePlugin({ // try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but // not in children Layer(props: LayerProps) { - if (!FocusLock || !ComponentDispatch || !Classes) { + if (FocusLock === NoopComponent || ComponentDispatch[SYM_PROXY_INNER_VALUE] == null || Classes[SYM_PROXY_INNER_VALUE] == null) { new Logger("BetterSettings").error("Failed to find some components"); return props.children; } diff --git a/src/plugins/biggerStreamPreview/webpack/stores.ts b/src/plugins/biggerStreamPreview/webpack/stores.ts index ba5227baa..805c8622e 100644 --- a/src/plugins/biggerStreamPreview/webpack/stores.ts +++ b/src/plugins/biggerStreamPreview/webpack/stores.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import * as t from "./types/stores"; -export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore"); -export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore"); +export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore"); +export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore"); diff --git a/src/plugins/blurNsfw/index.ts b/src/plugins/blurNsfw/index.ts index a80f9f260..967205d73 100644 --- a/src/plugins/blurNsfw/index.ts +++ b/src/plugins/blurNsfw/index.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -26,7 +26,7 @@ function setCss() { style.textContent = ` .vc-nsfw-img [class^=imageWrapper] img, .vc-nsfw-img [class^=wrapperPaused] video { - filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px); + filter: blur(${settings.store.blurAmount}px); transition: filter 0.2s; } .vc-nsfw-img [class^=imageWrapper]:hover img, @@ -36,10 +36,20 @@ function setCss() { `; } +const settings = definePluginSettings({ + blurAmount: { + type: OptionType.NUMBER, + description: "Blur Amount", + default: 10, + onChange: setCss + } +}); + export default definePlugin({ name: "BlurNSFW", description: "Blur attachments in NSFW channels until hovered", authors: [Devs.Ven], + settings, patches: [ { @@ -51,15 +61,6 @@ export default definePlugin({ } ], - options: { - blurAmount: { - type: OptionType.NUMBER, - description: "Blur Amount", - default: 10, - onChange: setCss - } - }, - start() { style = document.createElement("style"); style.id = "VcBlurNsfw"; diff --git a/src/plugins/callTimer/index.tsx b/src/plugins/callTimer/index.tsx index c018cc715..b0b002d39 100644 --- a/src/plugins/callTimer/index.tsx +++ b/src/plugins/callTimer/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { useTimer } from "@utils/react"; @@ -25,7 +25,7 @@ import { React } from "@webpack/common"; function formatDuration(ms: number) { // here be dragons (moment fucking sucks) - const human = Settings.plugins.CallTimer.format === "human"; + const human = settings.store.format === "human"; const format = (n: number) => human ? n : n.toString().padStart(2, "0"); const unit = (s: string) => human ? s : ""; @@ -46,32 +46,33 @@ function formatDuration(ms: number) { return res; } +const settings = definePluginSettings({ + format: { + type: OptionType.SELECT, + description: "The timer format. This can be any valid moment.js format", + options: [ + { + label: "30d 23:00:42", + value: "stopwatch", + default: true + }, + { + label: "30d 23h 00m 42s", + value: "human" + } + ] + } +}); + export default definePlugin({ name: "CallTimer", description: "Adds a timer to vcs", authors: [Devs.Ven], + settings, startTime: 0, interval: void 0 as NodeJS.Timeout | undefined, - options: { - format: { - type: OptionType.SELECT, - description: "The timer format. This can be any valid moment.js format", - options: [ - { - label: "30d 23:00:42", - value: "stopwatch", - default: true - }, - { - label: "30d 23h 00m 42s", - value: "human" - } - ] - } - }, - patches: [{ find: "renderConnectionStatus(){", replacement: { diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 59f3d5fe2..c1feb19d3 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -11,10 +11,10 @@ import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { findByCode, findComponentByCode, findStore } from "@webpack"; import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const colorPresets = [ "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", @@ -30,20 +30,20 @@ function onPickColor(color: number) { updateColorVars(hexColor); } -const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"==='); +const saveClientTheme = findByCode('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"==='); function setTheme(theme: string) { saveClientTheme({ theme }); } -const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore"); +const ClientThemesBackgroundStore = findStore("ClientThemesBackgroundStore"); function ThemeSettings() { const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme); const isLightTheme = theme === "light"; const oppositeTheme = isLightTheme ? "dark" : "light"; - const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset); + const nitroTheme = useStateFromStores([ClientThemesBackgroundStore], () => ClientThemesBackgroundStore.gradientPreset); const nitroThemeEnabled = nitroTheme !== undefined; const selectedLuminance = relativeLuminance(settings.store.color); diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 2fdf87356..2265dd171 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -19,40 +19,43 @@ import { Devs } from "@utils/constants"; import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy"; +import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact"; import { relaunch } from "@utils/native"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; +import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner"; import definePlugin, { PluginNative, StartAt } from "@utils/types"; import * as Webpack from "@webpack"; -import { extract, filters, findAll, findModuleId, search } from "@webpack"; +import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack"; import * as Common from "@webpack/common"; import { loadLazyChunks } from "debug/loadLazyChunks"; -import type { ComponentType } from "react"; const DESKTOP_ONLY = (f: string) => () => { throw new Error(`'${f}' is Discord Desktop only.`); }; -const define: typeof Object.defineProperty = - (obj, prop, desc) => { - if (Object.hasOwn(desc, "value")) - desc.writable = true; - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: true, - ...desc - }); - }; +type Define = typeof Object.defineProperty; +const define: Define = (target, p, attributes) => { + if (Object.hasOwn(attributes, "value")) { + attributes.writable = true; + } + + return Object.defineProperty(target, p, { + configurable: true, + enumerable: true, + ...attributes + }); +}; function makeShortcuts() { - function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { + function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) { const cache = new Map(); return function (...filterProps: unknown[]) { const cacheKey = String(filterProps); if (cache.has(cacheKey)) return cache.get(cacheKey); - const matches = findAll(filterFactory(...filterProps)); + const matches = cacheFindAll(filterFactory(...filterProps), shouldReturnFactory); const result = (() => { switch (matches.length) { @@ -60,51 +63,72 @@ function makeShortcuts() { case 1: return matches[0]; default: const uniqueMatches = [...new Set(matches)]; - if (uniqueMatches.length > 1) + if (uniqueMatches.length > 1) { console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches); + } - return matches[0]; + return uniqueMatches[0]; } })(); + if (result && cacheKey) cache.set(cacheKey, result); return result; }; } let fakeRenderWin: WeakRef | undefined; + const find = newFindWrapper(f => f); const findByProps = newFindWrapper(filters.byProps); return { - ...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])), wp: Webpack, wpc: { getter: () => Webpack.cache }, wreq: { getter: () => Webpack.wreq }, - wpsearch: search, - wpex: extract, - wpexs: (code: string) => extract(findModuleId(code)!), + + WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances }, loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); }, + + wpsearch: searchFactories, + wpex: extract, + wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!), + + filters, find, - findAll: findAll, - findByProps, - findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), - findByCode: newFindWrapper(filters.byCode), - findAllByCode: (code: string) => findAll(filters.byCode(code)), + findAll: cacheFindAll, + findComponent: find, + findAllComponents: cacheFindAll, + findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]], findComponentByCode: newFindWrapper(filters.componentByCode), - findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), - findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], + findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)), + findComponentByFields: newFindWrapper(filters.componentByFields), + findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)), + findByProps, + findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)), + findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]], + findByCode: newFindWrapper(filters.byCode), + findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)), findStore: newFindWrapper(filters.byStoreName), - PluginsApi: { getter: () => Vencord.Plugins }, + findByFactoryCode: newFindWrapper(filters.byFactoryCode), + findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)), + findModuleFactory: newFindWrapper(filters.byFactoryCode, true), + findAllModuleFactories: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code), true), + plugins: { getter: () => Vencord.Plugins.plugins }, + PluginsApi: { getter: () => Vencord.Plugins }, Settings: { getter: () => Vencord.Settings }, Api: { getter: () => Vencord.Api }, Util: { getter: () => Vencord.Util }, + reload: () => location.reload(), restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch, + canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement, - fakeRender: (component: ComponentType, props: any) => { + + preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true, + fakeRender: (component: React.ComponentType, props: any) => { const prevWin = fakeRenderWin?.deref(); const win = prevWin?.closed === false ? prevWin @@ -133,8 +157,6 @@ function makeShortcuts() { Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); }, - preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true, - channel: { getter: () => getCurrentChannel(), preload: false }, channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false }, guild: { getter: () => getCurrentGuild(), preload: false }, @@ -143,6 +165,7 @@ function makeShortcuts() { meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false }, messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }, + ...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])), Stores: { getter: () => Object.fromEntries( Common.Flux.Store.getAll() @@ -157,11 +180,39 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) { const currentVal = val.getter(); if (!currentVal || val.preload === false) return currentVal; - const value = currentVal[SYM_LAZY_GET] - ? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED] - : currentVal; + function unwrapProxy(value: any) { + if (value[SYM_LAZY_GET]) { + return forceLoad ? value[SYM_LAZY_GET]() : value[SYM_LAZY_CACHED]; + } else if (value[SYM_PROXY_INNER_GET]) { + return forceLoad ? value[SYM_PROXY_INNER_GET]() : value[SYM_PROXY_INNER_VALUE]; + } else if (value[SYM_LAZY_COMPONENT_INNER]) { + return value[SYM_LAZY_COMPONENT_INNER]() != null ? value[SYM_LAZY_COMPONENT_INNER]() : value; + } - if (value) define(window.shortcutList, key, { value }); + return value; + } + + const value = unwrapProxy(currentVal); + if (value != null && typeof value === "object") { + const descriptors = Object.getOwnPropertyDescriptors(value); + + for (const propKey in descriptors) { + if (value[propKey] == null) continue; + + const descriptor = descriptors[propKey]; + if (descriptor.writable === true || descriptor.set != null) { + const currentValue = value[propKey]; + const newValue = unwrapProxy(currentValue); + if (newValue != null && currentValue !== newValue) { + value[propKey] = newValue; + } + } + } + } + + if (value != null) { + define(window.shortcutList, key, { value }); + } return value; } @@ -176,8 +227,10 @@ export default definePlugin({ const shortcuts = makeShortcuts(); window.shortcutList = {}; - for (const [key, val] of Object.entries(shortcuts)) { - if ("getter" in val) { + for (const key in shortcuts) { + const val = shortcuts[key]; + + if (Object.hasOwn(val, "getter")) { define(window.shortcutList, key, { get: () => loadAndCacheShortcut(key, val, true) }); @@ -191,8 +244,8 @@ export default definePlugin({ } } - // unproxy loaded modules - Webpack.onceReady.then(() => { + // Unproxy loaded modules + Webpack.onceDiscordLoaded.then(() => { setTimeout(() => this.eagerLoad(false), 1000); if (!IS_WEB) { @@ -203,13 +256,13 @@ export default definePlugin({ }, async eagerLoad(forceLoad: boolean) { - await Webpack.onceReady; + await Webpack.onceDiscordLoaded; const shortcuts = makeShortcuts(); + for (const key in shortcuts) { + const val = shortcuts[key]; - for (const [key, val] of Object.entries(shortcuts)) { - if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue; - + if (!Object.hasOwn(val, "getter") || val.preload === false) continue; try { loadAndCacheShortcut(key, val, forceLoad); } catch { } // swallow not found errors in DEV diff --git a/src/plugins/copyEmojiMarkdown/index.tsx b/src/plugins/copyEmojiMarkdown/index.tsx index a9c018a91..63f75aec5 100644 --- a/src/plugins/copyEmojiMarkdown/index.tsx +++ b/src/plugins/copyEmojiMarkdown/index.tsx @@ -8,10 +8,10 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findProp } from "@webpack"; import { Menu } from "@webpack/common"; -const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate"); +const convertNameToSurrogate = findProp("convertNameToSurrogate"); interface Emoji { type: string; @@ -55,7 +55,7 @@ export default definePlugin({ settings, contextMenus: { - "expression-picker"(children, { target }: { target: Target }) { + "expression-picker"(children, { target }: { target: Target; }) { if (target.dataset.type !== "emoji") return; children.push( diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts index ab881e60c..37e028f3c 100644 --- a/src/plugins/crashHandler/index.ts +++ b/src/plugins/crashHandler/index.ts @@ -23,22 +23,12 @@ import { Logger } from "@utils/Logger"; import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { maybePromptToUpdate } from "@utils/updater"; -import { filters, findBulk, proxyLazyWebpack } from "@webpack"; +import { findByProps } from "@webpack"; import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; const CrashHandlerLogger = new Logger("CrashHandler"); - -const { ModalStack, DraftManager } = proxyLazyWebpack(() => { - const [ModalStack, DraftManager] = findBulk( - filters.byProps("pushLazy", "popAll"), - filters.byProps("clearDraft", "saveDraft"), - ); - - return { - ModalStack, - DraftManager - }; -}); +const ModalStack = findByProps("pushLazy", "popAll"); +const DraftManager = findByProps("clearDraft", "saveDraft"); const settings = definePluginSettings({ attemptToPreventCrashes: { diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index f4b9ab060..5fd5993d3 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { definePluginSettings, Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { getUserSettingLazy } from "@api/UserSettings"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; @@ -26,13 +26,13 @@ import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findComponentByCodeLazy } from "@webpack"; +import { findByCode, findComponentByCode } from "@webpack"; import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; -const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color"); -const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); +const useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color"); +const ActivityComponent = findComponentByCode("onOpenGameProfile"); -const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame"); async function getApplicationAsset(key: string): Promise { if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); @@ -260,7 +260,7 @@ const settings = definePluginSettings({ function onChange() { setRpc(true); - if (Settings.plugins.CustomRPC.enabled) setRpc(); + if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc(); } function isStreamLinkDisabled() { diff --git a/src/plugins/decor/ui/components/DecorSection.tsx b/src/plugins/decor/ui/components/DecorSection.tsx index ff044f8c7..c4e5a94a6 100644 --- a/src/plugins/decor/ui/components/DecorSection.tsx +++ b/src/plugins/decor/ui/components/DecorSection.tsx @@ -5,7 +5,7 @@ */ import { Flex } from "@components/Flex"; -import { findByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import { Button, useEffect } from "@webpack/common"; import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore"; @@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco import { cl } from "../"; import { openChangeDecorationModal } from "../modals/ChangeDecorationModal"; -const CustomizationSection = findByCodeLazy(".customizationSectionBackground"); +const CustomizationSection = findComponentByCode(".customizationSectionBackground"); export interface DecorSectionProps { hideTitle?: boolean; diff --git a/src/plugins/decor/ui/components/SectionedGridList.tsx b/src/plugins/decor/ui/components/SectionedGridList.tsx index 9a6ec1b8d..315448c64 100644 --- a/src/plugins/decor/ui/components/SectionedGridList.tsx +++ b/src/plugins/decor/ui/components/SectionedGridList.tsx @@ -5,13 +5,13 @@ */ import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { React } from "@webpack/common"; import { cl } from "../"; import Grid, { GridProps } from "./Grid"; -const ScrollerClasses = findByPropsLazy("managedReactiveScroller"); +const ScrollerClasses = findByProps("managedReactiveScroller"); type Section = SectionT & { items: Array; diff --git a/src/plugins/decor/ui/components/index.ts b/src/plugins/decor/ui/components/index.ts index 8fe41fc92..9673343e7 100644 --- a/src/plugins/decor/ui/components/index.ts +++ b/src/plugins/decor/ui/components/index.ts @@ -4,30 +4,27 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findComponentByCode, LazyComponentWebpack } from "@webpack"; +import { NoopComponent } from "@utils/react"; +import { findComponentByCode } from "@webpack"; import { React } from "@webpack/common"; -import type { ComponentType, HTMLProps, PropsWithChildren } from "react"; import { AvatarDecoration } from "../.."; -type DecorationGridItemComponent = ComponentType> & { +type DecorationGridItemComponent = AnyComponentTypeWithChildren & { onSelect: () => void, isSelected: boolean, }>; -export let DecorationGridItem: DecorationGridItemComponent; +export let DecorationGridItem: DecorationGridItemComponent = NoopComponent; export const setDecorationGridItem = v => DecorationGridItem = v; -export const AvatarDecorationModalPreview = LazyComponentWebpack(() => { - const component = findComponentByCode(".shopPreviewBanner"); - return React.memo(component); -}); +export const AvatarDecorationModalPreview = findComponentByCode(".shopPreviewBanner", component => React.memo(component)); -type DecorationGridDecorationComponent = React.ComponentType & { +type DecorationGridDecorationComponent = AnyComponentType & { avatarDecoration: AvatarDecoration; onSelect: () => void, isSelected: boolean, }>; -export let DecorationGridDecoration: DecorationGridDecorationComponent; +export let DecorationGridDecoration: DecorationGridDecorationComponent = NoopComponent; export const setDecorationGridDecoration = v => DecorationGridDecoration = v; diff --git a/src/plugins/decor/ui/index.ts b/src/plugins/decor/ui/index.ts index c7846364c..6890b7d37 100644 --- a/src/plugins/decor/ui/index.ts +++ b/src/plugins/decor/ui/index.ts @@ -5,10 +5,10 @@ */ import { classNameFactory } from "@api/Styles"; -import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findByProps } from "@webpack"; export const cl = classNameFactory("vc-decor-"); -export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton"); +export const DecorationModalStyles = findByProps("modalFooterShopButton"); -export const requireAvatarDecorationModal = extractAndLoadChunksLazy([".COLLECTIBLES_SHOP_FULLSCREEN&&"]); -export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]); +export const requireAvatarDecorationModal = extractAndLoadChunksLazy(".COLLECTIBLES_SHOP_FULLSCREEN&&"); +export const requireCreateStickerModal = extractAndLoadChunksLazy("stickerInspected]:"); diff --git a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx index 6501e0feb..c5ea18d01 100644 --- a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx +++ b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx @@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes, copyWithToast } from "@utils/misc"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common"; import { User } from "discord-types/general"; @@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList"; import { openCreateDecorationModal } from "./CreateDecorationModal"; import { openGuidelinesModal } from "./GuidelinesModal"; -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); function usePresets() { const [presets, setPresets] = useState([]); diff --git a/src/plugins/decor/ui/modals/CreateDecorationModal.tsx b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx index f5596f391..24339be3a 100644 --- a/src/plugins/decor/ui/modals/CreateDecorationModal.tsx +++ b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx @@ -9,7 +9,7 @@ import { Link } from "@components/Link"; import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack"; +import { filters, findComponentByCode, mapMangledModule } from "@webpack"; import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common"; import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants"; @@ -17,11 +17,11 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../"; import { AvatarDecorationModalPreview } from "../components"; -const FileUpload = findComponentByCodeLazy("fileUploadInput,"); +const FileUpload = findComponentByCode("fileUploadInput,"); -const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', { +const { HelpMessage, HelpMessageTypes } = mapMangledModule('POSITIVE=3]="POSITIVE', { + HelpMessage: filters.componentByCode(".iconDiv,", "messageType"), HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"), - HelpMessage: filters.byCode(".iconDiv") }); function useObjectURL(object: Blob | MediaSource | null) { diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index a495907b2..c6d485874 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -22,7 +22,7 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { filters, findAll, search } from "@webpack"; +import { cacheFindAll, filters, searchFactories } from "@webpack"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -154,13 +154,13 @@ function initWs(isManual = false) { case "testPatch": { const { find, replacement } = data as PatchData; - const candidates = search(find); + const candidates = searchFactories(find); const keys = Object.keys(candidates); if (keys.length !== 1) return reply("Expected exactly one 'find' matches, found " + keys.length); const mod = candidates[keys[0]]; - let src = String(mod.original ?? mod).replaceAll("\n", ""); + let src = String(mod).replaceAll("\n", ""); if (src.startsWith("function(")) { src = "0," + src; @@ -201,22 +201,22 @@ function initWs(isManual = false) { let results: any[]; switch (type.replace("find", "").replace("Lazy", "")) { case "": - results = findAll(parsedArgs[0]); + results = cacheFindAll(parsedArgs[0]); break; case "ByProps": - results = findAll(filters.byProps(...parsedArgs)); + results = cacheFindAll(filters.byProps(...parsedArgs)); break; case "Store": - results = findAll(filters.byStoreName(parsedArgs[0])); + results = cacheFindAll(filters.byStoreName(parsedArgs[0])); break; case "ByCode": - results = findAll(filters.byCode(...parsedArgs)); + results = cacheFindAll(filters.byCode(...parsedArgs)); break; case "ModuleId": - results = Object.keys(search(parsedArgs[0])); + results = Object.keys(searchFactories(parsedArgs[0])); break; case "ComponentByCode": - results = findAll(filters.componentByCode(...parsedArgs)); + results = cacheFindAll(filters.componentByCode(...parsedArgs)); break; default: return reply("Unknown Find Type " + type); diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index 6dd3eb300..a27767933 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import definePlugin from "@utils/types"; -import { findByCodeLazy, findStoreLazy } from "@webpack"; +import { findByCode, findStore } from "@webpack"; import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Promisable } from "type-fest"; -const StickersStore = findStoreLazy("StickersStore"); -const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START"); +const StickersStore = findStore("StickersStore"); +const uploadEmoji = findByCode(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START"); interface Sticker { t: "Sticker"; diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 82e20f734..0fc6b1a57 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -23,13 +23,13 @@ import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy } from "@webpack"; +import { find, findByProps } from "@webpack"; import { Forms, React } from "@webpack/common"; import hideBugReport from "./hideBugReport.css?managed"; -const KbdStyles = findByPropsLazy("key", "combo"); -const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter"); +const KbdStyles = findByProps("key", "combo"); +const BugReporterExperiment = find(m => m?.definition?.id === "2024-09_bug_reporter"); const settings = definePluginSettings({ toolbarDevMenu: { diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index efc194954..a06102302 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -23,36 +23,36 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { getCurrentGuild } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; +import { findByCode, findByProps, findStore, webpackDependantLazy } from "@webpack"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; import type { ReactElement, ReactNode } from "react"; -const StickerStore = findStoreLazy("StickersStore") as { +const StickersStore = findStore("StickersStore") as { getPremiumPacks(): StickerPack[]; getAllGuildStickers(): Map; getStickerById(id: string): Sticker | undefined; }; -const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); +const UserSettingsProtoStore = findStore("UserSettingsProtoStore"); -const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory"); +const BINARY_READ_OPTIONS = findByProps("readerFactory"); function searchProtoClassField(localName: string, protoClass: any) { const field = protoClass?.fields?.find((field: any) => field.localName === localName); if (!field) return; - const fieldGetter = Object.values(field).find(value => typeof value === "function") as any; + const fieldGetter = Object.values(field).find(value => typeof value === "function"); return fieldGetter?.(); } -const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); -const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); -const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); +const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); +const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); +const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); -const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin("); +const isUnusableRoleSubscriptionEmoji = findByCode(".getUserIsAdmin("); const enum EmojiIntentions { REACTION, @@ -566,8 +566,8 @@ export default definePlugin({ const gifMatch = child.props.href.match(fakeNitroGifStickerRegex); if (gifMatch) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(gifMatch[1])) return null; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(gifMatch[1])) return null; } } @@ -655,7 +655,7 @@ export default definePlugin({ url = new URL(item); } catch { } - const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker"; + const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker"; stickers.push({ format_type: 1, id: imgMatch[1], @@ -668,9 +668,9 @@ export default definePlugin({ const gifMatch = item.match(fakeNitroGifStickerRegex); if (gifMatch) { - if (!StickerStore.getStickerById(gifMatch[1])) continue; + if (!StickersStore.getStickerById(gifMatch[1])) continue; - const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker"; + const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker"; stickers.push({ format_type: 2, id: gifMatch[1], @@ -703,8 +703,8 @@ export default definePlugin({ const gifMatch = embed.url!.match(fakeNitroGifStickerRegex); if (gifMatch) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(gifMatch[1])) return true; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(gifMatch[1])) return true; } } @@ -721,8 +721,8 @@ export default definePlugin({ const match = attachment.url.match(fakeNitroGifStickerRegex); if (match) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(match[1])) return false; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(match[1])) return false; } return true; @@ -878,7 +878,7 @@ export default definePlugin({ if (!s.enableStickerBypass) break stickerBypass; - const sticker = StickerStore.getStickerById(extra.stickers?.[0]!); + const sticker = StickersStore.getStickerById(extra.stickers?.[0]!); if (!sticker) break stickerBypass; diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx index 708c961aa..f11dc6e92 100644 --- a/src/plugins/fakeProfileThemes/index.tsx +++ b/src/plugins/fakeProfileThemes/index.tsx @@ -26,7 +26,7 @@ import { Margins } from "@utils/margins"; import { classes, copyWithToast } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCode } from "@webpack"; import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common"; import { User } from "discord-types/general"; import virtualMerge from "virtual-merge"; @@ -108,10 +108,10 @@ interface ProfileModalProps { isTryItOutFlow: boolean; } -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ProfileModal = findComponentByCodeLazy("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER"); +const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ProfileModal = findComponentByCode("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER"); -const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/); +const requireColorPicker = extractAndLoadChunksLazy("USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"); export default definePlugin({ name: "FakeProfileThemes", diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index d71f56795..f1af5b043 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { useCallback, useEffect, useRef, useState } from "@webpack/common"; interface SearchBarComponentProps { @@ -35,7 +35,7 @@ interface SearchBarComponentProps { } type TSearchBarComponent = - React.FC & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; }; + React.ComponentType & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; }; interface Gif { format: number; @@ -60,7 +60,7 @@ interface Instance { } -const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow"); +const containerClasses: { searchBar: string; } = findByProps("searchBar", "searchBarFullRow"); export const settings = definePluginSettings({ searchOption: { diff --git a/src/plugins/friendInvites/index.ts b/src/plugins/friendInvites/index.ts index 3c5a324fd..5a83d9dcf 100644 --- a/src/plugins/friendInvites/index.ts +++ b/src/plugins/friendInvites/index.ts @@ -19,9 +19,9 @@ import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const FriendInvites = findByPropsLazy("createFriendInvite"); +const FriendInvites = findByProps("createFriendInvite"); export default definePlugin({ name: "FriendInvites", diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index cb5634c8e..435b913da 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -8,14 +8,14 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByCode, findByProps, findComponentByCode } from "@webpack"; import { RelationshipStore, Text } from "@webpack/common"; -const containerWrapper = findByPropsLazy("memberSinceWrapper"); -const container = findByPropsLazy("memberSince"); -const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"'); -const locale = findByPropsLazy("getLocale"); -const Section = findComponentByCodeLazy('"auto":"smooth"', ".section"); +const containerWrapper = findByProps("memberSinceWrapper"); +const container = findByProps("memberSince"); +const getCreatedAtDate = findByCode('month:"short",day:"numeric"'); +const locale = findByProps("getLocale"); +const Section = findComponentByCode('"auto":"smooth"', ".section"); export default definePlugin({ name: "FriendsSince", diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 7aeb470d6..37acac730 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -22,13 +22,13 @@ import { getUserSettingLazy } from "@api/UserSettings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import style from "./style.css?managed"; -const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); +const Button = findComponentByCode("Button.Sizes.NONE,disabled:"); -const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame"); function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 438fa40c6..2dabb9fae 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -19,7 +19,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findLazy } from "@webpack"; +import { find } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; @@ -49,7 +49,7 @@ const settings = definePluginSettings({ unholyMultiGreetEnabled?: boolean; }>(); -const WELCOME_STICKERS = findLazy(m => Array.isArray(m) && m[0]?.name === "Wave"); +const WELCOME_STICKERS = find(m => Array.isArray(m) && m[0]?.name === "Wave"); function greet(channel: Channel, message: Message, stickers: string[]) { const options = MessageActions.getSendMessageOptionsForReply({ diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 8c36e1d75..44814af3e 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -12,7 +12,7 @@ import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common"; const enum ActivitiesTypes { @@ -31,9 +31,9 @@ const enum FilterMode { Blacklist } -const RunningGameStore = findStoreLazy("RunningGameStore"); +const RunningGameStore = findStore("RunningGameStore"); -const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame"); function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { return ( diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index da01f1a79..400524b8d 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -26,7 +26,7 @@ import definePlugin, { OptionType } from "@utils/types"; import { Menu, ReactDOM } from "@webpack/common"; import type { Root } from "react-dom/client"; -import { Magnifier, MagnifierProps } from "./components/Magnifier"; +import { Magnifier } from "./components/Magnifier"; import { ELEMENT_ID } from "./constants"; import styles from "./styles.css?managed"; @@ -201,7 +201,7 @@ export default definePlugin({ }, // to stop from rendering twice /shrug - currentMagnifierElement: null as React.FunctionComponentElement | null, + currentMagnifierElement: null as React.ReactNode, element: null as HTMLDivElement | null, Magnifier, diff --git a/src/plugins/implicitRelationships/index.ts b/src/plugins/implicitRelationships/index.ts index 7fefb39b8..8f013c741 100644 --- a/src/plugins/implicitRelationships/index.ts +++ b/src/plugins/implicitRelationships/index.ts @@ -19,16 +19,26 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { ChannelStore, Constants, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common"; -import { Settings } from "Vencord"; -const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore"); +const UserAffinitiesStore = findStore("UserAffinitiesStore"); + +const settings = definePluginSettings({ + sortByAffinity: { + type: OptionType.BOOLEAN, + default: true, + description: "Whether to sort implicit relationships by their affinity to you.", + restartNeeded: true + } +}); export default definePlugin({ name: "ImplicitRelationships", description: "Shows your implicit relationships in the Friends tab.", authors: [Devs.Dolfies], + settings, + patches: [ // Counts header { @@ -75,7 +85,7 @@ export default definePlugin({ { find: "getRelationshipCounts(){", replacement: { - predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity, + predicate: () => settings.store.sortByAffinity, match: /\}\)\.sortBy\((.+?)\)\.value\(\)/, replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()" } @@ -104,16 +114,6 @@ export default definePlugin({ }, } ], - settings: definePluginSettings( - { - sortByAffinity: { - type: OptionType.BOOLEAN, - default: true, - description: "Whether to sort implicit relationships by their affinity to you.", - restartNeeded: true - }, - } - ), wrapSort(comparator: Function, row: any) { return row.type === 5 diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 129e42a0d..2a0886a98 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -20,7 +20,7 @@ import { registerCommand, unregisterCommand } from "@api/Commands"; import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; import { Logger } from "@utils/Logger"; -import { canonicalizeFind } from "@utils/patches"; +import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches"; import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; import { FluxEvents } from "@webpack/types"; @@ -66,10 +66,12 @@ export function addPatch(newPatch: Omit, pluginName: string) { patch.replacement = [patch.replacement]; } - if (IS_REPORTER) { - patch.replacement.forEach(r => { - delete r.predicate; - }); + for (const replacement of patch.replacement) { + canonicalizeReplacement(replacement, pluginName); + + if (IS_REPORTER) { + delete replacement.predicate; + } } patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate()); diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfm/index.tsx index 02fd694f8..35667cb66 100644 --- a/src/plugins/lastfm/index.tsx +++ b/src/plugins/lastfm/index.tsx @@ -21,7 +21,7 @@ import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findStore } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; interface ActivityAssets { @@ -86,7 +86,7 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f"; const logger = new Logger("LastFMRichPresence"); -const presenceStore = findByPropsLazy("getLocalPresence"); +const SelfPresenceStore = findStore("SelfPresenceStore"); async function getApplicationAsset(key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; @@ -275,7 +275,7 @@ export default definePlugin({ async getActivity(): Promise { if (settings.store.hideWithSpotify) { - for (const activity of presenceStore.getActivities()) { + for (const activity of SelfPresenceStore.getActivities()) { if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) { // there is already music status because of Spotify or richerCider (probably more) return null; diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index 85dcc4b2d..0f38b9933 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -23,20 +23,19 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { FluxStore } from "@webpack/types"; import { MemberCount } from "./MemberCount"; -export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; }; -export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & { +export const GuildMemberCountStore = findStore("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; }; +export const ChannelMemberStore = findStore("ChannelMemberStore") as FluxStore & { getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; }; }; -export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as FluxStore & { +export const ThreadMemberListStore = findStore("ThreadMemberListStore") as FluxStore & { getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; }; }; - const settings = definePluginSettings({ toolTip: { type: OptionType.BOOLEAN, diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 14899c367..832c0f09d 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -20,11 +20,11 @@ import { addClickListener, removeClickListener } from "@api/MessageEvents"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common"; -const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); -const EditStore = findByPropsLazy("isEditing", "isEditingAny"); +const MessageActions = findByProps("deleteMessage", "startEditMessage"); +const EditMessageStore = findStore("EditMessageStore"); let isDeletePressed = false; const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); @@ -74,7 +74,7 @@ export default definePlugin({ if (msg.deleted === true) return; if (isMe) { - if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return; + if (!settings.store.enableDoubleClickToEdit || EditMessageStore.isEditing(channel.id, msg.id)) return; MessageActions.startEditMessage(channel.id, msg.id, msg.content); event.preventDefault(); diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index e4e5b8771..31b2c3432 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; import definePlugin, { OptionType } from "@utils/types"; -import { findExportedComponentLazy } from "@webpack"; +import { findExportedComponent } from "@webpack"; import { SnowflakeUtils, Tooltip } from "@webpack/common"; import { Message } from "discord-types/general"; @@ -26,7 +26,7 @@ interface Diff { } const DISCORD_KT_DELAY = 1471228928; -const HiddenVisually = findExportedComponentLazy("HiddenVisually"); +const HiddenVisually = findExportedComponent("HiddenVisually"); export default definePlugin({ name: "MessageLatency", diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 9fd677389..9efed0643 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -25,7 +25,7 @@ import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; import { Queue } from "@utils/Queue"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { Button, ChannelStore, @@ -47,14 +47,14 @@ const messageCache = new Map(); -const Embed = findComponentByCodeLazy(".inlineMediaEmbed"); -const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:"); -const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories"); +const Embed = findComponentByCode(".inlineMediaEmbed"); +const AutoModEmbed = findComponentByCode(".withFooter]:", "childrenMessageContent:"); +const ChannelMessage = findComponentByCode("childrenExecutedCommand:", ".hideAccessories"); -const SearchResultClasses = findByPropsLazy("message", "searchResult"); -const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); +const SearchResultClasses = findByProps("message", "searchResult"); +const EmbedClasses = findByProps("embedAuthorIcon", "embedAuthor", "embedAuthor"); -const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact")!; +const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact"); const messageLinkRegex = /(? addDeleteStyle() + }, + logDeletes: { + type: OptionType.BOOLEAN, + description: "Whether to log deleted messages", + default: true, + }, + collapseDeleted: { + type: OptionType.BOOLEAN, + description: "Whether to collapse deleted messages, similar to blocked messages", + default: false + }, + logEdits: { + type: OptionType.BOOLEAN, + description: "Whether to log edited messages", + default: true, + }, + inlineEdits: { + type: OptionType.BOOLEAN, + description: "Whether to display edit history as part of message content", + default: true + }, + ignoreBots: { + type: OptionType.BOOLEAN, + description: "Whether to ignore messages by bots", + default: false + }, + ignoreSelf: { + type: OptionType.BOOLEAN, + description: "Whether to ignore messages by yourself", + default: false + }, + ignoreUsers: { + type: OptionType.STRING, + description: "Comma-separated list of user IDs to ignore", + default: "" + }, + ignoreChannels: { + type: OptionType.STRING, + description: "Comma-separated list of channel IDs to ignore", + default: "" + }, + ignoreGuilds: { + type: OptionType.STRING, + description: "Comma-separated list of guild IDs to ignore", + default: "" + }, +}); + export default definePlugin({ name: "MessageLogger", description: "Temporarily logs deleted and edited messages.", authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi], dependencies: ["MessageUpdaterAPI"], + settings, contextMenus: { "message": patchMessageContextMenu, @@ -168,7 +227,7 @@ export default definePlugin({ (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory ); - return Settings.plugins.MessageLogger.inlineEdits && ( + return settings.store.inlineEdits && ( <> {message.editHistory?.map(edit => (

@@ -193,64 +252,6 @@ export default definePlugin({ }; }, - options: { - deleteStyle: { - type: OptionType.SELECT, - description: "The style of deleted messages", - default: "text", - options: [ - { label: "Red text", value: "text", default: true }, - { label: "Red overlay", value: "overlay" } - ], - onChange: () => addDeleteStyle() - }, - logDeletes: { - type: OptionType.BOOLEAN, - description: "Whether to log deleted messages", - default: true, - }, - collapseDeleted: { - type: OptionType.BOOLEAN, - description: "Whether to collapse deleted messages, similar to blocked messages", - default: false - }, - logEdits: { - type: OptionType.BOOLEAN, - description: "Whether to log edited messages", - default: true, - }, - inlineEdits: { - type: OptionType.BOOLEAN, - description: "Whether to display edit history as part of message content", - default: true - }, - ignoreBots: { - type: OptionType.BOOLEAN, - description: "Whether to ignore messages by bots", - default: false - }, - ignoreSelf: { - type: OptionType.BOOLEAN, - description: "Whether to ignore messages by yourself", - default: false - }, - ignoreUsers: { - type: OptionType.STRING, - description: "Comma-separated list of user IDs to ignore", - default: "" - }, - ignoreChannels: { - type: OptionType.STRING, - description: "Comma-separated list of channel IDs to ignore", - default: "" - }, - ignoreGuilds: { - type: OptionType.STRING, - description: "Comma-separated list of guild IDs to ignore", - default: "" - }, - }, - handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) { try { if (cache == null || (!isBulk && !cache.has(data.id))) return cache; @@ -285,7 +286,7 @@ export default definePlugin({ }, shouldIgnore(message: any, isEdit = false) { - const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = Settings.plugins.MessageLogger; + const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store; const myId = UserStore.getCurrentUser().id; return ignoreBots && message.author?.bot || @@ -493,7 +494,7 @@ export default definePlugin({ match: /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/, replace: '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";', }, - predicate: () => Settings.plugins.MessageLogger.collapseDeleted + predicate: () => settings.store.collapseDeleted }, { // Message group rendering @@ -508,7 +509,7 @@ export default definePlugin({ replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:', }, ], - predicate: () => Settings.plugins.MessageLogger.collapseDeleted + predicate: () => settings.store.collapseDeleted } ] }); diff --git a/src/plugins/messageTags/index.ts b/src/plugins/messageTags/index.ts index 5ba4ab94a..77f506166 100644 --- a/src/plugins/messageTags/index.ts +++ b/src/plugins/messageTags/index.ts @@ -18,7 +18,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands"; import * as DataStore from "@api/DataStore"; -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -60,7 +60,7 @@ function createTagCommand(tag: Tag) { return { content: `/${tag.name}` }; } - if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, { + if (settings.store.clyde) sendBotMessage(ctx.channel.id, { content: `${EMOTE} The tag **${tag.name}** has been sent!` }); return { content: tag.message.replaceAll("\\n", "\n") }; @@ -69,19 +69,20 @@ function createTagCommand(tag: Tag) { }, "CustomTags"); } +const settings = definePluginSettings({ + clyde: { + name: "Clyde message on send", + description: "If enabled, clyde will send you an ephemeral message when a tag was used.", + type: OptionType.BOOLEAN, + default: true + } +}); export default definePlugin({ name: "MessageTags", description: "Allows you to save messages and to use them with a simple command.", authors: [Devs.Luna], - options: { - clyde: { - name: "Clyde message on send", - description: "If enabled, clyde will send you an ephemeral message when a tag was used.", - type: OptionType.BOOLEAN, - default: true - } - }, + settings, async start() { for (const tag of await getTags()) createTagCommand(tag); diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 0a87c57a9..b7e52d7b7 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -19,11 +19,12 @@ import { definePluginSettings } from "@api/Settings"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; +import { LazyComponentType } from "@utils/lazyReact"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findLazy } from "@webpack"; +import { findByCode, findComponentByCode } from "@webpack"; import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common"; -import type { Permissions, RC } from "@webpack/types"; +import type { Permissions } from "@webpack/types"; import type { Channel, Guild, Message, User } from "discord-types/general"; interface Tag { @@ -59,9 +60,9 @@ const computePermissions: (options: { overwrites?: Channel["permissionOverwrites"] | null; checkElevated?: boolean /* = true */; excludeGuildPermissions?: boolean /* = false */; -}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); +}) => bigint = findByCode(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); -const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; +const Tag = findComponentByCode(".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,") as LazyComponentType<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index ec52b4061..4d49d471e 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -20,16 +20,16 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; import definePlugin from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common"; import { Channel, User } from "discord-types/general"; -const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); -const UserUtils = findByPropsLazy("getGlobalName"); +const SelectedChannelActionCreators = findByProps("selectPrivateChannel"); +const UserUtils = findByProps("getGlobalName"); -const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); -const ExpandableList = findComponentByCodeLazy(".mutualFriendItem]"); -const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); +const ProfileListClasses = findByProps("emptyIconFriends", "emptyIconGuilds"); +const ExpandableList = findComponentByCode(".mutualFriendItem]"); +const GuildLabelClasses = findByProps("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { return channel.name || diff --git a/src/plugins/newGuildSettings/index.tsx b/src/plugins/newGuildSettings/index.tsx index 7cfb073fa..905344899 100644 --- a/src/plugins/newGuildSettings/index.tsx +++ b/src/plugins/newGuildSettings/index.tsx @@ -24,18 +24,18 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { CogWheel } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; +import { findByCode, findProp, mapMangledModule } from "@webpack"; import { Menu } from "@webpack/common"; import { Guild } from "discord-types/general"; -const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings"); -const { toggleShowAllChannels } = mapMangledModuleLazy(".onboardExistingMember(", { +const updateGuildNotificationSettings = findProp("updateGuildNotificationSettings"); +const { toggleShowAllChannels } = mapMangledModule(".onboardExistingMember(", { toggleShowAllChannels: m => { const s = String(m); return s.length < 100 && !s.includes("onboardExistingMember") && !s.includes("getOptedInChannels"); } }); -const isOptInEnabledForGuild = findByCodeLazy(".COMMUNITY)||", ".isOptInEnabled("); +const isOptInEnabledForGuild = findByCode(".COMMUNITY)||", ".isOptInEnabled("); const settings = definePluginSettings({ guild: { diff --git a/src/plugins/noBlockedMessages/index.ts b/src/plugins/noBlockedMessages/index.ts index 1ea5b5296..efd800938 100644 --- a/src/plugins/noBlockedMessages/index.ts +++ b/src/plugins/noBlockedMessages/index.ts @@ -16,19 +16,28 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { RelationshipStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); +const settings = definePluginSettings({ + ignoreBlockedMessages: { + description: "Completely ignores (recent) incoming messages from blocked users (locally).", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true, + }, +}); export default definePlugin({ name: "NoBlockedMessages", description: "Hides all blocked messages from chat completely.", authors: [Devs.rushii, Devs.Samu], + settings, + patches: [ { find: "Messages.BLOCKED_MESSAGES_HIDE", @@ -44,7 +53,7 @@ export default definePlugin({ '"displayName","ReadStateStore")' ].map(find => ({ find, - predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true, + predicate: () => settings.store.ignoreBlockedMessages === true, replacement: [ { match: /(?<=MESSAGE_CREATE:function\((\i)\){)/, diff --git a/src/plugins/noPendingCount/index.ts b/src/plugins/noPendingCount/index.ts index d3e27563b..72cf64cac 100644 --- a/src/plugins/noPendingCount/index.ts +++ b/src/plugins/noPendingCount/index.ts @@ -19,9 +19,9 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findStore } from "@webpack"; -const MessageRequestStore = findByPropsLazy("getMessageRequestsCount"); +const MessageRequestStore = findStore("MessageRequestStore"); const settings = definePluginSettings({ hideFriendRequestsCount: { diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index 82925d610..f3cac3255 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -21,7 +21,7 @@ import { Flex } from "@components/Flex"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; import { getUniqueUsername } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; import { UnicodeEmoji } from "@webpack/types"; import type { Guild, Role, User } from "discord-types/general"; @@ -45,7 +45,7 @@ export interface RoleOrUserPermission { } type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; }; -const getRoleIconData: GetRoleIconData = findByCodeLazy("convertSurrogateToName", "customIconSrc", "unicodeEmoji"); +const getRoleIconData: GetRoleIconData = findByCode("convertSurrogateToName", "customIconSrc", "unicodeEmoji"); function getRoleIconSrc(role: Role) { const icon = getRoleIconData(role, 20); diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index 7c0858f10..53862ff53 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -19,7 +19,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { ExpandableHeader } from "@components/ExpandableHeader"; import { classes } from "@utils/misc"; -import { filters, findBulk, proxyLazyWebpack } from "@webpack"; +import { findByProps } from "@webpack"; import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; @@ -36,15 +36,9 @@ interface UserPermission { type UserPermissions = Array; -const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() => { - const [RoleRootClasses, RoleClasses, RoleBorderClasses] = findBulk( - filters.byProps("root", "expandButton", "collapseButton"), - filters.byProps("role", "roleCircle", "roleName"), - filters.byProps("roleCircle", "dot", "dotBorderColor") - ) as Record[]; - - return { RoleRootClasses, RoleClasses, RoleBorderClasses }; -}); +const RoleRootClasses = findByProps("root", "expandButton", "collapseButton"); +const RoleClasses = findByProps("role", "roleCircle", "roleName"); +const RoleBorderClasses = findByProps("roleCircle", "dot", "dotBorderColor"); interface FakeRoleProps extends React.HTMLAttributes { text: string; diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index ca28f845f..830cfcff3 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -25,7 +25,7 @@ import { SafetyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; @@ -33,8 +33,8 @@ import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission import UserPermissions from "./components/UserPermissions"; import { getSortedRoles, sortPermissionOverwrites } from "./utils"; -const PopoutClasses = findByPropsLazy("container", "scroller", "list"); -const RoleButtonClasses = findByPropsLazy("button", "buttonInner", "icon", "banner"); +const PopoutClasses = findByProps("container", "scroller", "list"); +const RoleButtonClasses = findByProps("button", "buttonInner", "icon", "banner"); export const enum PermissionsSortOrder { HighestRole, diff --git a/src/plugins/petpet/index.ts b/src/plugins/petpet/index.ts index 708c6f0c0..1d3267094 100644 --- a/src/plugins/petpet/index.ts +++ b/src/plugins/petpet/index.ts @@ -20,7 +20,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, Co import { Devs } from "@utils/constants"; import { makeLazy } from "@utils/lazy"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findStore } from "@webpack"; import { DraftType, UploadHandler, UploadManager, UserUtils } from "@webpack/common"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; @@ -35,7 +35,7 @@ const getFrames = makeLazy(() => Promise.all( )) ); -const UploadStore = findByPropsLazy("getUploads"); +const UploadAttachmentStore = findStore("UploadAttachmentStore"); function loadImage(source: File | string) { const isFile = source instanceof File; @@ -58,7 +58,7 @@ async function resolveImage(options: Argument[], ctx: CommandContext, noServerPf for (const opt of options) { switch (opt.name) { case "image": - const upload = UploadStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand); + const upload = UploadAttachmentStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand); if (upload) { if (!upload.isImage) { UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand); diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index 2f1d4d1d5..f9b8d0395 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; -import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCode, findExportedComponent } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -30,10 +30,10 @@ interface ColorPickerWithSwatchesProps { renderCustomButton?: () => React.ReactNode; } -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); +const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ColorPickerWithSwatches = findExportedComponent("ColorPicker", "CustomColorPicker"); -export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); +export const requireSettingsMenu = extractAndLoadChunksLazy('name:"UserSettings"', /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); const cl = classNameFactory("vc-pindms-modal-"); diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 620a615a1..5b5db6611 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -11,7 +11,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu, React } from "@webpack/common"; import { Channel } from "discord-types/general"; @@ -27,9 +27,9 @@ interface ChannelComponentProps { } -const headerClasses = findByPropsLazy("privateChannelsHeaderContainer"); +const headerClasses = findByProps("privateChannelsHeaderContainer"); -export const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; }; +export const PrivateChannelSortStore = findStore("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; }; export let instance: any; export const forceUpdate = () => instance?.props?._forceUpdate?.(); diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 1dc76e9d3..a4342345b 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -21,11 +21,11 @@ import "./style.css"; import { addBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeBadge } from "@api/Badges"; import { addDecorator, removeDecorator } from "@api/MemberListDecorators"; import { addDecoration, removeDecoration } from "@api/MessageDecorations"; -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; @@ -40,7 +40,7 @@ export interface Session { }; } -const SessionsStore = findStoreLazy("SessionsStore") as { +const SessionsStore = findStore("SessionsStore") as { getSessions(): Record; }; @@ -70,7 +70,7 @@ const Icons = { }; type Platform = keyof typeof Icons; -const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); +const StatusUtils = findByProps("useStatusFillColor", "StatusTypes"); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const tooltip = platform === "embedded" @@ -195,29 +195,36 @@ const indicatorLocations = { } }; +const settings = definePluginSettings({ + ...Object.fromEntries( + Object.entries(indicatorLocations).map(([key, value]) => { + return [key, { + type: OptionType.BOOLEAN, + description: `Show indicators ${value.description.toLowerCase()}`, + // onChange doesn't give any way to know which setting was changed, so restart required + restartNeeded: true, + default: true + }]; + }) + ) as Record<"list" | "badges" | "messages", { type: OptionType.BOOLEAN; description: string; restartNeeded: boolean; default: boolean; }>, + colorMobileIndicator: { + type: OptionType.BOOLEAN, + description: "Whether to make the mobile indicator match the color of the user status.", + default: true, + restartNeeded: true + } +}); + export default definePlugin({ name: "PlatformIndicators", description: "Adds platform indicators (Desktop, Mobile, Web...) to users", authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven], dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"], + settings, start() { - const settings = Settings.plugins.PlatformIndicators; - const { displayMode } = settings; - - // transfer settings from the old ones, which had a select menu instead of booleans - if (displayMode) { - if (displayMode !== "both") settings[displayMode] = true; - else { - settings.list = true; - settings.badges = true; - } - settings.messages = true; - delete settings.displayMode; - } - Object.entries(indicatorLocations).forEach(([key, value]) => { - if (settings[key]) value.onEnable(); + if (settings.store[key]) value.onEnable(); }); }, @@ -230,7 +237,7 @@ export default definePlugin({ patches: [ { find: ".Masks.STATUS_ONLINE_MOBILE", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: [ { // Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status @@ -246,7 +253,7 @@ export default definePlugin({ }, { find: ".AVATAR_STATUS_MOBILE_16;", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: [ { // Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status @@ -267,32 +274,12 @@ export default definePlugin({ }, { find: "}isMobileOnline(", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: { // Make isMobileOnline return true no matter what is the user status match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/, replace: "!= null" } } - ], - - options: { - ...Object.fromEntries( - Object.entries(indicatorLocations).map(([key, value]) => { - return [key, { - type: OptionType.BOOLEAN, - description: `Show indicators ${value.description.toLowerCase()}`, - // onChange doesn't give any way to know which setting was changed, so restart required - restartNeeded: true, - default: true - }]; - }) - ), - colorMobileIndicator: { - type: OptionType.BOOLEAN, - description: "Whether to make the mobile indicator match the color of the user status.", - default: true, - restartNeeded: true - } - } + ] }); diff --git a/src/plugins/previewMessage/index.tsx b/src/plugins/previewMessage/index.tsx index fe6b227a5..6c438caf1 100644 --- a/src/plugins/previewMessage/index.tsx +++ b/src/plugins/previewMessage/index.tsx @@ -20,15 +20,14 @@ import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatB import { generateId, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; import definePlugin, { StartAt } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findStore } from "@webpack"; import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common"; import { MessageAttachment } from "discord-types/general"; -const UploadStore = findByPropsLazy("getUploads"); +const UploadAttachmentStore = findStore("UploadAttachmentStore"); const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage); - const getImageBox = (url: string): Promise<{ width: number, height: number; } | null> => new Promise(res => { const img = new Image(); @@ -44,7 +43,7 @@ const getImageBox = (url: string): Promise<{ width: number, height: number; } | const getAttachments = async (channelId: string) => await Promise.all( - UploadStore.getUploads(channelId, DraftType.ChannelMessage) + UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage) .map(async (upload: any) => { const { isImage, filename, spoiler, item: { file } } = upload; const url = URL.createObjectURL(file); @@ -79,7 +78,7 @@ const PreviewButton: ChatBarButton = ({ isMainChat, isEmpty, type: { attachments if (!isMainChat) return null; - const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0; + const hasAttachments = attachments && UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage).length > 0; const hasContent = !isEmpty && draft?.length > 0; if (!hasContent && !hasAttachments) return null; diff --git a/src/plugins/pronoundb/components/PronounsChatComponent.tsx b/src/plugins/pronoundb/components/PronounsChatComponent.tsx index 64fac18ba..5164f0d0e 100644 --- a/src/plugins/pronoundb/components/PronounsChatComponent.tsx +++ b/src/plugins/pronoundb/components/PronounsChatComponent.tsx @@ -18,14 +18,14 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; import { useFormattedPronouns } from "../pronoundbUtils"; import { settings } from "../settings"; -const styles: Record = findByPropsLazy("timestampInline"); +const styles: Record = findByProps("timestampInline"); const AUTO_MODERATION_ACTION = 24; diff --git a/src/plugins/pronoundb/pronoundbUtils.ts b/src/plugins/pronoundb/pronoundbUtils.ts index 991e9031a..a3c90c07c 100644 --- a/src/plugins/pronoundb/pronoundbUtils.ts +++ b/src/plugins/pronoundb/pronoundbUtils.ts @@ -16,18 +16,17 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; import { debounce } from "@shared/debounce"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { getCurrentChannel } from "@utils/discord"; import { useAwaiter } from "@utils/react"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { UserProfileStore, UserStore } from "@webpack/common"; import { settings } from "./settings"; import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types"; -const UserSettingsAccountStore = findStoreLazy("UserSettingsAccountStore"); +const UserSettingsAccountStore = findStore("UserSettingsAccountStore"); type PronounsWithSource = [pronouns: string | null, source: string, hasPendingPronouns: boolean]; const EmptyPronouns: PronounsWithSource = [null, "", false]; @@ -156,7 +155,7 @@ export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[]; if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified; // PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}. const pronouns = pronounSet.en; - const { pronounsFormat } = Settings.plugins.PronounDB as { pronounsFormat: PronounsFormat, enabled: boolean; }; + const { pronounsFormat } = settings.store; if (pronouns.length === 1) { // For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string diff --git a/src/plugins/quickReply/index.ts b/src/plugins/quickReply/index.ts index ac2a38705..e93349a70 100644 --- a/src/plugins/quickReply/index.ts +++ b/src/plugins/quickReply/index.ts @@ -19,12 +19,11 @@ import { definePluginSettings, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; -import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; +import { findByProps } from "@webpack"; +import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const Kangaroo = findByPropsLazy("jumpToMessage"); -const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); +const Kangaroo = findByProps("jumpToMessage"); const isMac = navigator.platform.includes("Mac"); // bruh let replyIdx = -1; diff --git a/src/plugins/readAllNotificationsButton/index.tsx b/src/plugins/readAllNotificationsButton/index.tsx index 7a6737a8a..75de1d730 100644 --- a/src/plugins/readAllNotificationsButton/index.tsx +++ b/src/plugins/readAllNotificationsButton/index.tsx @@ -22,8 +22,8 @@ import { addServerListElement, removeServerListElement, ServerListRenderPosition import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findStoreLazy } from "@webpack"; -import { Button, FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common"; +import { findStore } from "@webpack"; +import { Button, FluxDispatcher, GenericStore, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common"; import { Channel } from "discord-types/general"; interface ThreadJoined { @@ -34,11 +34,11 @@ interface ThreadJoined { type ThreadsJoined = Record; type ThreadsJoinedByParent = Record; -interface ActiveJoinedThreadsStore { +interface ActiveJoinedThreadsStore extends GenericStore { getActiveJoinedThreadsForGuild(guildId: string): ThreadsJoinedByParent; } -const ActiveJoinedThreadsStore: ActiveJoinedThreadsStore = findStoreLazy("ActiveJoinedThreadsStore"); +const ActiveJoinedThreadsStore = findStore("ActiveJoinedThreadsStore"); function onClick() { const channels: Array = []; diff --git a/src/plugins/relationshipNotifier/utils.ts b/src/plugins/relationshipNotifier/utils.ts index 053cff835..ddf4e49a7 100644 --- a/src/plugins/relationshipNotifier/utils.ts +++ b/src/plugins/relationshipNotifier/utils.ts @@ -19,14 +19,14 @@ import { DataStore, Notices } from "@api/index"; import { showNotification } from "@api/Notifications"; import { getUniqueUsername, openUserProfile } from "@utils/discord"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common"; import { FluxStore } from "@webpack/types"; import settings from "./settings"; import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types"; -export const GuildAvailabilityStore = findStoreLazy("GuildAvailabilityStore") as FluxStore & { +export const GuildAvailabilityStore = findStore("GuildAvailabilityStore") as FluxStore & { totalGuilds: number; totalUnavailableGuilds: number; unavailableGuilds: string[]; diff --git a/src/plugins/replyTimestamp/index.tsx b/src/plugins/replyTimestamp/index.tsx index 851a62b9d..9d60be973 100644 --- a/src/plugins/replyTimestamp/index.tsx +++ b/src/plugins/replyTimestamp/index.tsx @@ -9,17 +9,17 @@ import "./style.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; +import { filters, findByProps, mapMangledModule } from "@webpack"; import { Timestamp } from "@webpack/common"; import type { Message } from "discord-types/general"; import type { HTMLAttributes } from "react"; -const { calendarFormat, dateFormat, isSameDay } = mapMangledModuleLazy("millisecondsInUnit:", { +const { calendarFormat, dateFormat, isSameDay } = mapMangledModule("millisecondsInUnit:", { calendarFormat: filters.byCode("sameElse"), - dateFormat: filters.byCode('":'), + dateFormat: filters.byCode(':").concat'), isSameDay: filters.byCode("Math.abs(+"), }); -const MessageClasses = findByPropsLazy("separator", "latin24CompactTimeStamp"); +const MessageClasses = findByProps("separator", "latin24CompactTimeStamp"); function Sep(props: HTMLAttributes) { return ; diff --git a/src/plugins/revealAllSpoilers/index.ts b/src/plugins/revealAllSpoilers/index.ts index 98e8423cf..e903b3efe 100644 --- a/src/plugins/revealAllSpoilers/index.ts +++ b/src/plugins/revealAllSpoilers/index.ts @@ -18,10 +18,10 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const SpoilerClasses = findByPropsLazy("spoilerContent"); -const MessagesClasses = findByPropsLazy("messagesWrapper"); +const SpoilerClasses = findByProps("spoilerContent"); +const MessagesClasses = findByProps("messagesWrapper"); export default definePlugin({ name: "RevealAllSpoilers", diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx index 4cd81f2ea..8d9789dd7 100644 --- a/src/plugins/reviewDB/auth.tsx +++ b/src/plugins/reviewDB/auth.tsx @@ -7,15 +7,12 @@ import { DataStore } from "@api/index"; import { Logger } from "@utils/Logger"; import { openModal } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { showToast, Toasts, UserStore } from "@webpack/common"; +import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common"; import { ReviewDBAuth } from "./entities"; const DATA_STORE_KEY = "rdb-auth"; -const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); - export let Auth: ReviewDBAuth = {}; export async function initAuth() { diff --git a/src/plugins/reviewDB/components/MessageButton.tsx b/src/plugins/reviewDB/components/MessageButton.tsx index 9b0b4be1a..5c84303c7 100644 --- a/src/plugins/reviewDB/components/MessageButton.tsx +++ b/src/plugins/reviewDB/components/MessageButton.tsx @@ -18,10 +18,10 @@ import { DeleteIcon } from "@components/Icons"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Tooltip } from "@webpack/common"; -const iconClasses = findByPropsLazy("button", "wrapper", "disabled", "separator"); +const iconClasses = findByProps("button", "wrapper", "disabled", "separator"); export function DeleteButton({ onClick }: { onClick(): void; }) { return ( diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 31eab29f5..6392dc2ef 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -18,8 +18,7 @@ import { openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; -import { LazyComponent } from "@utils/react"; -import { filters, findBulk } from "@webpack"; +import { findByProps } from "@webpack"; import { Alerts, Parser, Timestamp, useState } from "@webpack/common"; import { Auth, getToken } from "../auth"; @@ -31,161 +30,150 @@ import { openBlockModal } from "./BlockedUserModal"; import { BlockButton, DeleteButton, ReportButton } from "./MessageButton"; import ReviewBadge from "./ReviewBadge"; -export default LazyComponent(() => { - // this is terrible, blame mantika - const p = filters.byProps; - const [ - { cozyMessage, buttons, message, buttonsInner, groupStart }, - { container, isHeader }, - { avatar, clickable, username, wrapper, cozy }, - buttonClasses, - botTag - ] = findBulk( - p("cozyMessage"), - p("container", "isHeader"), - p("avatar", "zalgo"), - p("button", "wrapper", "selected"), - p("botTagRegular") - ); +const messageClasses = findByProps("cozyMessage"); +const containerClasses = findByProps("container", "isHeader"); +const avatarClasses = findByProps("avatar", "zalgo"); +const buttonClasses = findByProps("button", "wrapper", "selected"); +const botTagClasses = findByProps("botTagRegular"); - const dateFormat = new Intl.DateTimeFormat(); +const dateFormat = new Intl.DateTimeFormat(); - return function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { - const [showAll, setShowAll] = useState(false); +export default function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { + const [showAll, setShowAll] = useState(false); - function openModal() { - openUserProfile(review.sender.discordID); - } + function openModal() { + openUserProfile(review.sender.discordID); + } - function delReview() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really want to delete this review?", - confirmText: "Delete", - cancelText: "Nevermind", - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to delete reviews."); - } else { - deleteReview(review.id).then(res => { - if (res) { - refetch(); - } - }); - } + function delReview() { + Alerts.show({ + title: "Are you sure?", + body: "Do you really want to delete this review?", + confirmText: "Delete", + cancelText: "Nevermind", + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to delete reviews."); + } else { + deleteReview(review.id).then(res => { + if (res) { + refetch(); + } + }); } - }); - } + } + }); + } - function reportRev() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really you want to report this review?", - confirmText: "Report", - cancelText: "Nevermind", - // confirmColor: "red", this just adds a class name and breaks the submit button guh - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to report reviews."); - } else { - reportReview(review.id); - } + function reportRev() { + Alerts.show({ + title: "Are you sure?", + body: "Do you really you want to report this review?", + confirmText: "Report", + cancelText: "Nevermind", + // confirmColor: "red", this just adds a class name and breaks the submit button guh + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to report reviews."); + } else { + reportReview(review.id); } - }); - } + } + }); + } - const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false; + const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false; - function blockReviewSender() { - if (isAuthorBlocked) - return unblockUser(review.sender.discordID); + function blockReviewSender() { + if (isAuthorBlocked) + return unblockUser(review.sender.discordID); - Alerts.show({ - title: "Are you sure?", - body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.", - confirmText: "Block", - cancelText: "Nevermind", - // confirmColor: "red", this just adds a class name and breaks the submit button guh - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to block users."); - } else { - blockUser(review.sender.discordID); - } + Alerts.show({ + title: "Are you sure?", + body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.", + confirmText: "Block", + cancelText: "Nevermind", + // confirmColor: "red", this just adds a class name and breaks the submit button guh + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to block users."); + } else { + blockUser(review.sender.discordID); } - }); - } + } + }); + } - return ( -
+ return ( +
- -
+ +
+ openModal()} + > + {review.sender.username} + + + {review.type === ReviewType.System && ( openModal()} - > - {review.sender.username} - - - {review.type === ReviewType.System && ( - - - System - + className={classes(botTagClasses.botTagVerified, botTagClasses.botTagRegular, botTagClasses.px, botTagClasses.rem)} + style={{ marginLeft: "4px" }}> + + System - )} -
- {isAuthorBlocked && ( - openBlockModal()} - /> - )} - {review.sender.badges.map(badge => )} - - { - !settings.store.hideTimestamps && review.type !== ReviewType.System && ( - - {dateFormat.format(review.timestamp * 1000)} - ) - } - -
- {(review.comment.length > 200 && !showAll) - ? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...",
, ( setShowAll(true)}>Read more)] - : Parser.parseGuildEventDescription(review.comment)} -
- - {review.id !== 0 && ( -
-
- {canReportReview(review) && } - {canBlockReviewAuthor(profileId, review) && } - {canDeleteReview(profileId, review) && } -
-
+ )}
- ); - }; -}); + {isAuthorBlocked && ( + openBlockModal()} + /> + )} + {review.sender.badges.map(badge => )} + + { + !settings.store.hideTimestamps && review.type !== ReviewType.System && ( + + {dateFormat.format(review.timestamp * 1000)} + ) + } + +
+ {(review.comment.length > 200 && !showAll) + ? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...",
, ( setShowAll(true)}>Read more)] + : Parser.parseGuildEventDescription(review.comment)} +
+ + {review.id !== 0 && ( +
+
+ {canReportReview(review) && } + {canBlockReviewAuthor(profileId, review) && } + {canDeleteReview(profileId, review) && } +
+
+ )} +
+ ); +} + diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index 7a7d8d020..41b4e1712 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -17,7 +17,7 @@ */ import { useAwaiter, useForceUpdater } from "@utils/react"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByCode, findByProps, findComponentByCode } from "@webpack"; import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Auth, authorize } from "../auth"; @@ -27,11 +27,11 @@ import { settings } from "../settings"; import { cl, showToast } from "../utils"; import ReviewComponent from "./ReviewComponent"; -const Transforms = findByPropsLazy("insertNodes", "textToText"); -const Editor = findByPropsLazy("start", "end", "toSlateRange"); -const ChatInputTypes = findByPropsLazy("FORM"); -const InputComponent = findComponentByCodeLazy("disableThemedBackground", "CHANNEL_TEXT_AREA"); -const createChannelRecordFromServer = findByCodeLazy(".GUILD_TEXT])", "fromServer)"); +const Transforms = findByProps("insertNodes", "textToText"); +const Editor = findByProps("start", "end", "toSlateRange"); +const ChatInputTypes = findByProps("FORM"); +const InputComponent = findComponentByCode("disableThemedBackground", "CHANNEL_TEXT_AREA"); +const createChannelRecordFromServer = findByCode(".GUILD_TEXT])", "fromServer)"); interface UserProps { discordId: string; diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 9d93e53a3..ac1d5fb5a 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -24,7 +24,7 @@ import { NotesIcon, OpenExternalIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Alerts, Button, Menu, Parser, TooltipContainer } from "@webpack/common"; import { Guild, User } from "discord-types/general"; @@ -35,7 +35,7 @@ import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; import { settings } from "./settings"; import { showToast } from "./utils"; -const RoleButtonClasses = findByPropsLazy("button", "buttonInner", "icon", "banner"); +const RoleButtonClasses = findByProps("button", "buttonInner", "icon", "banner"); const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild, onClose(): void; }) => { if (!guild) return; diff --git a/src/plugins/searchReply/index.tsx b/src/plugins/searchReply/index.tsx index 298e74218..169bc4d8d 100644 --- a/src/plugins/searchReply/index.tsx +++ b/src/plugins/searchReply/index.tsx @@ -20,12 +20,11 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { ReplyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common"; import { Message } from "discord-types/general"; - -const replyToMessage = findByCodeLazy(".TEXTAREA_FOCUS)", "showMentionToggle:"); +const replyToMessage = findByCode(".TEXTAREA_FOCUS)", "showMentionToggle:"); const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { // make sure the message is in the selected channel diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx index de50e0a9d..bba66bd1f 100644 --- a/src/plugins/seeSummaries/index.tsx +++ b/src/plugins/seeSummaries/index.tsx @@ -8,11 +8,11 @@ import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByCode, findStore } from "@webpack"; import { ChannelStore, GuildStore } from "@webpack/common"; -const SummaryStore = findByPropsLazy("allSummaries", "findSummary"); -const createSummaryFromServer = findByCodeLazy(".people)),startId:", ".type}"); +const SummaryStore = findStore("SummaryStore"); +const createSummaryFromServer = findByCode(".people)),startId:", ".type}"); const settings = definePluginSettings({ summaryExpiryThresholdDays: { diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index fb8df2ce1..f3ea3e4a5 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -11,12 +11,12 @@ import { openImageModal, openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, GuildStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { Guild, User } from "discord-types/general"; -const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); -const FriendRow = findComponentByCodeLazy(".listName,discriminatorClass"); +const IconClasses = findByProps("icon", "acronym", "childWrapper"); +const FriendRow = findComponentByCode(".listName,discriminatorClass"); const cl = classNameFactory("vc-gp-"); diff --git a/src/plugins/serverListIndicators/index.tsx b/src/plugins/serverListIndicators/index.tsx index 96833d8f3..63ce3f9ac 100644 --- a/src/plugins/serverListIndicators/index.tsx +++ b/src/plugins/serverListIndicators/index.tsx @@ -17,7 +17,7 @@ */ import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; -import { Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { useForceUpdater } from "@utils/react"; @@ -89,26 +89,27 @@ function handleGuildUpdate() { forceUpdateGuildCount?.(); } +const settings = definePluginSettings({ + mode: { + description: "mode", + type: OptionType.SELECT, + options: [ + { label: "Only online friend count", value: IndicatorType.FRIEND, default: true }, + { label: "Only server count", value: IndicatorType.SERVER }, + { label: "Both server and online friend counts", value: IndicatorType.BOTH }, + ] + } +}); + export default definePlugin({ name: "ServerListIndicators", description: "Add online friend count or server count in the server list", authors: [Devs.dzshn], dependencies: ["ServerListAPI"], - - options: { - mode: { - description: "mode", - type: OptionType.SELECT, - options: [ - { label: "Only online friend count", value: IndicatorType.FRIEND, default: true }, - { label: "Only server count", value: IndicatorType.SERVER }, - { label: "Both server and online friend counts", value: IndicatorType.BOTH }, - ] - } - }, + settings, renderIndicator: () => { - const { mode } = Settings.plugins.ServerListIndicators; + const { mode } = settings.store; return
{!!(mode & IndicatorType.FRIEND) && } diff --git a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx index 508153b4b..48853ec8c 100644 --- a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx +++ b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx @@ -4,7 +4,7 @@ import React from "react"; const handleClick = async () => console.log((await import("@webpack/common")).Clipboard.copy("\u200b")); -export const Example: React.FC<{ +export const Example: React.ComponentType<{ real: boolean, shigged?: number, }> = ({ real, shigged }) => <> diff --git a/src/plugins/showConnections/VerifiedIcon.tsx b/src/plugins/showConnections/VerifiedIcon.tsx index ffdf21e63..ec435b438 100644 --- a/src/plugins/showConnections/VerifiedIcon.tsx +++ b/src/plugins/showConnections/VerifiedIcon.tsx @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import { findComponentByCodeLazy, findLazy } from "@webpack"; +import { find, findComponentByCode } from "@webpack"; import { i18n, useToken } from "@webpack/common"; -const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); -const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"); +const ColorMap = find(m => m.colors?.INTERACTIVE_MUTED?.css); +const VerifiedIconComponent = findComponentByCode(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"); export function VerifiedIcon() { const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex(); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index a946e0433..627d4821f 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -25,15 +25,15 @@ import { CopyIcon, LinkIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByCode, findByProps } from "@webpack"; import { Tooltip, UserProfileStore } from "@webpack/common"; import { User } from "discord-types/general"; import { VerifiedIcon } from "./VerifiedIcon"; -const useLegacyPlatformType: (platform: string) => string = findByCodeLazy(".TWITTER_LEGACY:"); -const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); -const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:"); +const useLegacyPlatformType: (platform: string) => string = findByCode(".TWITTER_LEGACY:"); +const platforms: { get(type: string): ConnectionPlatform; } = findByProps("isSupported", "getByUrl"); +const getProfileThemeProps = findByCode(".getPreviewThemeColors", "primaryColor:"); const enum Spacing { COMPACT, diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index bdb9fee8c..25609403c 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -16,10 +16,9 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { formatDuration } from "@utils/text"; -import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; import type { Channel } from "discord-types/general"; @@ -78,19 +77,13 @@ const enum ChannelFlags { } -const ChatScrollClasses = findByPropsLazy("auto", "managedReactiveScroller"); -const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent"); -const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); -const TagComponent = findComponentLazy(m => { - if (typeof m !== "function") return false; +const ChatScrollClasses = findByProps("auto", "managedReactiveScroller"); +const ChatClasses = findByProps("chat", "content", "noChat", "chatContent"); +const ChannelBeginHeader = findComponentByCode(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); +const TagComponent = findComponentByCode(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG"); - const code = Function.prototype.toString.call(m); - // Get the component which doesn't include increasedActivity - return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill"); -}); - -const EmojiParser = findByPropsLazy("convertSurrogateToName"); -const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors"); +const EmojiParser = findByProps("convertSurrogateToName"); +const EmojiUtils = findByProps("getURL", "getEmojiColors"); const ChannelTypesToChannelNames = { [ChannelTypes.GUILD_TEXT]: "text", @@ -163,7 +156,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { }); } - if (Settings.plugins.PermissionsViewer.enabled) { + if (Vencord.Plugins.isPluginEnabled("PermissionsViewer")) { setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({ type: overwrite.type as PermissionType, id: overwrite.id, @@ -280,7 +273,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { }
- {Settings.plugins.PermissionsViewer.enabled && ( + {Vencord.Plugins.isPluginEnabled("PermissionsViewer") && ( {({ onMouseLeave, onMouseEnter }) => ( ))} diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index 00805fbd3..b894845e5 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -23,11 +23,11 @@ import { Settings, useSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findExportedComponentLazy } from "@webpack"; +import { findExportedComponent } from "@webpack"; import { Menu, Popout, useState } from "@webpack/common"; import type { ReactNode } from "react"; -const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider"); +const HeaderBarIcon = findExportedComponent("Icon", "Divider"); function VencordPopout(onClose: () => void) { const { useQuickCss } = useSettings(["useQuickCss"]); diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index 9c77d8329..24cf6359a 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -17,7 +17,7 @@ */ import { useTimer } from "@utils/react"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import { cl } from "./utils"; @@ -25,7 +25,7 @@ interface VoiceMessageProps { src: string; waveform: string; } -const VoiceMessage = findComponentByCodeLazy("waveform:", "onVolumeChange"); +const VoiceMessage = findComponentByCode("waveform:", "onVolumeChange"); export type VoicePreviewOptions = { src?: string; diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 8365bb51a..d7f9be3df 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -27,9 +27,8 @@ import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModa import { useAwaiter } from "@utils/react"; import definePlugin from "@utils/types"; import { chooseFile } from "@utils/web"; -import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; +import { find, findByProps, findStore } from "@webpack"; import { Button, Card, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common"; -import { ComponentType } from "react"; import { VoiceRecorderDesktop } from "./DesktopRecorder"; import { settings } from "./settings"; @@ -37,11 +36,11 @@ import { cl } from "./utils"; import { VoicePreview } from "./VoicePreview"; import { VoiceRecorderWeb } from "./WebRecorder"; -const CloudUpload = findLazy(m => m.prototype?.trackUploadFinished); -const PendingReplyStore = findStoreLazy("PendingReplyStore"); -const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel"); +const CloudUpload = find(m => m.prototype?.trackUploadFinished); +const PendingReplyStore = findStore("PendingReplyStore"); +const OptionClasses = findByProps("optionName", "optionIcon", "optionLabel"); -export type VoiceRecorder = ComponentType<{ +export type VoiceRecorder = AnyComponentType<{ setAudioBlob(blob: Blob): void; onRecordingChange?(recording: boolean): void; }>; diff --git a/src/plugins/voiceMessages/utils.ts b/src/plugins/voiceMessages/utils.ts index ef571bbf3..81cea2f03 100644 --- a/src/plugins/voiceMessages/utils.ts +++ b/src/plugins/voiceMessages/utils.ts @@ -17,7 +17,7 @@ */ import { classNameFactory } from "@api/Styles"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; -export const MediaEngineStore = findStoreLazy("MediaEngineStore"); +export const MediaEngineStore = findStore("MediaEngineStore"); export const cl = classNameFactory("vc-vmsg-"); diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index bae780109..c2bc3c345 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -20,10 +20,10 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { saveFile } from "@utils/web"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Clipboard, ComponentDispatch } from "@webpack/common"; -const ctxMenuCallbacks = findByPropsLazy("contextMenuCallbackNative"); +const ctxMenuCallbacks = findByProps("contextMenuCallbackNative"); async function fetchImage(url: string) { const res = await fetch(url); diff --git a/src/plugins/webKeybinds.web/index.ts b/src/plugins/webKeybinds.web/index.ts index 1c43dc0cf..41962a08a 100644 --- a/src/plugins/webKeybinds.web/index.ts +++ b/src/plugins/webKeybinds.web/index.ts @@ -18,10 +18,10 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ComponentDispatch, FluxDispatcher, NavigationRouter, SelectedGuildStore, SettingsRouter } from "@webpack/common"; -const KeyBinds = findByPropsLazy("JUMP_TO_GUILD", "SERVER_NEXT"); +const KeyBinds = findByProps("JUMP_TO_GUILD", "SERVER_NEXT"); export default definePlugin({ name: "WebKeybinds", diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index 679fe714e..dc8466b67 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -22,13 +22,14 @@ import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; import { CustomEmoji } from "@webpack/types"; import { Message, ReactionEmoji, User } from "discord-types/general"; -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); -const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); + let Scroll: any = null; const queue = new Queue(); let reactions: Record; diff --git a/src/plugins/xsOverlay/index.tsx b/src/plugins/xsOverlay/index.tsx index 4c5b7a80e..7c2c23634 100644 --- a/src/plugins/xsOverlay/index.tsx +++ b/src/plugins/xsOverlay/index.tsx @@ -7,13 +7,12 @@ import { definePluginSettings } from "@api/Settings"; import { makeRange } from "@components/PluginSettings/components"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; -import { findByCodeLazy, findLazy } from "@webpack"; +import { find, findByCode } from "@webpack"; import { Button, ChannelStore, GuildStore, UserStore } from "@webpack/common"; import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general"; -const ChannelTypes = findLazy(m => m.ANNOUNCEMENT_THREAD === 10); +const ChannelTypes = find(m => m.ANNOUNCEMENT_THREAD === 10); interface Message { guild_id: string, @@ -90,8 +89,7 @@ interface NotificationObject { sourceApp: string; } -const notificationsShouldNotify = findByCodeLazy(".SUPPRESS_NOTIFICATIONS))return!1"); -const logger = new Logger("XSOverlay"); +const notificationsShouldNotify = findByCode(".SUPPRESS_NOTIFICATIONS))return!1"); const settings = definePluginSettings({ webSocketPort: { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6653e6307..ffbfd2c14 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -export const WEBPACK_CHUNK = "webpackChunkdiscord_app"; export const REACT_GLOBAL = "Vencord.Webpack.Common.React"; export const SUPPORT_CHANNEL_ID = "1026515880080842772"; diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 4c7cc38a0..da92adc20 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -20,7 +20,7 @@ import { MessageObject } from "@api/MessageEvents"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; -import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; +import { ImageModal, ImageModalProps, ModalRoot, ModalSize, openModal } from "./modal"; /** * Open the invite modal @@ -108,7 +108,7 @@ export function sendMessage( return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra); } -export function openImageModal(url: string, props?: Partial>): string { +export function openImageModal(url: string, props?: Partial): string { return openModal(modalProps => ( . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ -export function makeLazy(factory: () => T, attempts = 5): () => T { - let tries = 0; - let cache: T; - return () => { - if (!cache && attempts > tries++) { - cache = factory(); - if (!cache && attempts === tries) - console.error("Lazy factory failed:", factory); - } - return cache; - }; -} - -// Proxies demand that these properties be unmodified, so proxyLazy -// will always return the function default for them. -const unconfigurable = ["arguments", "caller", "prototype"]; - -const handler: ProxyHandler = {}; +import { UNCONFIGURABLE_PROPERTIES } from "./misc"; export const SYM_LAZY_GET = Symbol.for("vencord.lazy.get"); export const SYM_LAZY_CACHED = Symbol.for("vencord.lazy.cached"); -for (const method of [ - "apply", - "construct", - "defineProperty", - "deleteProperty", - "getOwnPropertyDescriptor", - "getPrototypeOf", - "has", - "isExtensible", - "ownKeys", - "preventExtensions", - "set", - "setPrototypeOf" -]) { - handler[method] = - (target: any, ...args: any[]) => Reflect[method](target[SYM_LAZY_GET](), ...args); -} - -handler.ownKeys = target => { - const v = target[SYM_LAZY_GET](); - const keys = Reflect.ownKeys(v); - for (const key of unconfigurable) { - if (!keys.includes(key)) keys.push(key); - } - return keys; +export type LazyFunction = (() => T) & { + $$vencordLazyFailed: () => boolean; }; -handler.getOwnPropertyDescriptor = (target, p) => { - if (typeof p === "string" && unconfigurable.includes(p)) - return Reflect.getOwnPropertyDescriptor(target, p); +export function makeLazy(factory: () => T, attempts = 5, { isIndirect = false }: { isIndirect?: boolean; } = {}): LazyFunction { + let tries = 0; + let cache: T; - const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_LAZY_GET](), p); + const getter: LazyFunction = function () { + if (!cache && attempts > tries) { + tries++; + cache = factory(); + if (!cache && attempts === tries && !isIndirect) { + console.error(`makeLazy factory failed:\n${factory}`); + } + } - if (descriptor) Object.defineProperty(target, p, descriptor); - return descriptor; + return cache; + }; + + getter.$$vencordLazyFailed = () => tries === attempts; + return getter; +} + +const handler: ProxyHandler = { + ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName => + [propName, (target: any, ...args: any[]) => Reflect[propName](target[SYM_LAZY_GET](), ...args)] + )), + set: (target, p, newValue) => { + const lazyTarget = target[SYM_LAZY_GET](); + return Reflect.set(lazyTarget, p, newValue, lazyTarget); + }, + ownKeys: target => { + const keys = Reflect.ownKeys(target[SYM_LAZY_GET]()); + for (const key of UNCONFIGURABLE_PROPERTIES) { + if (!keys.includes(key)) keys.push(key); + } + return keys; + }, + getOwnPropertyDescriptor: (target, p) => { + if (typeof p === "string" && UNCONFIGURABLE_PROPERTIES.includes(p)) { + return Reflect.getOwnPropertyDescriptor(target, p); + } + + const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_LAZY_GET](), p); + if (descriptor) Object.defineProperty(target, p, descriptor); + return descriptor; + } }; /** - * Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy. - * On first property access, the lazy is evaluated - * @param factory lazy factory - * @param attempts how many times to try to evaluate the lazy before giving up - * @returns Proxy + * Wraps the result of factory in a Proxy you can consume as if it wasn't lazy. + * On first property access, the factory is evaluated. * - * Note that the example below exists already as an api, see {@link findByPropsLazy} - * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); + * IMPORTANT: + * Destructuring at top level is not supported for proxyLazy. + * + * @param factory Factory returning the result + * @param attempts How many times to try to evaluate the factory before giving up + * @param err The error message to throw when the factory fails + * @param primitiveErr The error message to throw when factory result is a primitive + * @returns Result of factory function */ -export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T { - let isSameTick = true; - if (!isChild) - setTimeout(() => isSameTick = false, 0); +export function proxyLazy(factory: () => T, attempts = 5, err: string | (() => string) = `proxyLazy factory failed:\n${factory}`, primitiveErr = "proxyLazy called on a primitive value.", isChild = false): T { + const get = makeLazy(factory, attempts, { isIndirect: true }); + + let isSameTick = true; + if (!isChild) setTimeout(() => isSameTick = false, 0); - let tries = 0; const proxyDummy = Object.assign(function () { }, { - [SYM_LAZY_CACHED]: void 0 as T | undefined, [SYM_LAZY_GET]() { - if (!proxyDummy[SYM_LAZY_CACHED] && attempts > tries++) { - proxyDummy[SYM_LAZY_CACHED] = factory(); - if (!proxyDummy[SYM_LAZY_CACHED] && attempts === tries) - console.error("Lazy factory failed:", factory); + if (!proxyDummy[SYM_LAZY_CACHED]) { + if (!get.$$vencordLazyFailed()) { + proxyDummy[SYM_LAZY_CACHED] = get(); + } + + if (!proxyDummy[SYM_LAZY_CACHED]) { + throw new Error(typeof err === "string" ? err : err()); + } else { + if (typeof proxyDummy[SYM_LAZY_CACHED] === "function") { + proxy.toString = proxyDummy[SYM_LAZY_CACHED].toString.bind(proxyDummy[SYM_LAZY_CACHED]); + } + } } + return proxyDummy[SYM_LAZY_CACHED]; + }, + [SYM_LAZY_CACHED]: void 0 as T | undefined + }); + + const proxy = new Proxy(proxyDummy, { + ...handler, + get(target, p, receiver) { + if (p === SYM_LAZY_GET || p === SYM_LAZY_CACHED) { + return Reflect.get(target, p, receiver); + } + + // If we're still in the same tick, it means the lazy was immediately used. + // thus, we lazy proxy the get access to make things like destructuring work as expected + // meow here will also be a lazy + // `const { meow } = proxyLazy(() => ({ meow: [] }));` + if (!isChild && isSameTick) { + console.warn( + "Destructuring webpack finds/proxyInner/proxyLazy at top level is deprecated. For more information read https://github.com/Vendicated/Vencord/pull/2409#issue-2277161516" + + "\nConsider not destructuring, using findProp or if you really need to destructure, using mapMangledModule instead." + ); + + return proxyLazy( + () => { + const lazyTarget = target[SYM_LAZY_GET](); + return Reflect.get(lazyTarget, p, lazyTarget); + }, + attempts, + err, + primitiveErr, + true + ); + } + + const lazyTarget = target[SYM_LAZY_GET](); + if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { + return Reflect.get(lazyTarget, p, lazyTarget); + } + + throw new Error(primitiveErr); } }); - return new Proxy(proxyDummy, { - ...handler, - get(target, p, receiver) { - if (p === SYM_LAZY_CACHED || p === SYM_LAZY_GET) - return Reflect.get(target, p, receiver); - - // if we're still in the same tick, it means the lazy was immediately used. - // thus, we lazy proxy the get access to make things like destructuring work as expected - // meow here will also be a lazy - // `const { meow } = findByPropsLazy("meow");` - if (!isChild && isSameTick) - return proxyLazy( - () => Reflect.get(target[SYM_LAZY_GET](), p, receiver), - attempts, - true - ); - const lazyTarget = target[SYM_LAZY_GET](); - if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { - return Reflect.get(lazyTarget, p, receiver); - } - throw new Error("proxyLazy called on a primitive value"); - } - }) as any; + return proxy; +} + +/** + * A string which returns the factory result every time its value is accessed. + * + * @param factory Factory returning the string to use as the value + */ +export function lazyString(factory: () => T) { + const descriptor: PropertyDescriptor = { + configurable: true, + enumerable: false, + writable: false, + value: factory + }; + + return Object.create(String.prototype, { + toString: descriptor, + valueOf: descriptor + }); } diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index 4896a0581..97343f370 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -4,26 +4,50 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ComponentType } from "react"; - import { makeLazy } from "./lazy"; -const NoopComponent = () => null; +export const SYM_LAZY_COMPONENT_INNER = Symbol.for("vencord.lazyComponent.inner"); + +export type LazyComponentType

= React.FunctionComponent

& AnyRecord & { + [SYM_LAZY_COMPONENT_INNER]: () => AnyComponentType

| null; +}; +export type AnyLazyComponentType

= LazyComponentType

; /** * A lazy component. The factory method is called on first render. - * @param factory Function returning a Component + * + * @param factory Function returning a component * @param attempts How many times to try to get the component before giving up * @returns Result of factory function */ -export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { - const get = makeLazy(factory, attempts); - const LazyComponent = (props: T) => { - const Component = get() ?? NoopComponent; - return ; +export function LazyComponent

(factory: () => React.ComponentType

, attempts = 5, err: string | (() => string) = `LazyComponent factory failed:\n${factory}`): LazyComponentType

{ + const get = makeLazy(factory, attempts, { isIndirect: true }); + + let InnerComponent = null as AnyComponentType

| null; + + let lazyFailedLogged = false; + const LazyComponent: LazyComponentType

= function (props) { + if (!get.$$vencordLazyFailed()) { + const ResultComponent = get(); + + if (ResultComponent != null) { + InnerComponent = ResultComponent; + Object.assign(LazyComponent, ResultComponent); + } + } + + if (InnerComponent === null && !lazyFailedLogged) { + if (get.$$vencordLazyFailed()) { + lazyFailedLogged = true; + } + + console.error(typeof err === "string" ? err : err()); + } + + return InnerComponent && ; }; - LazyComponent.$$vencordInternal = get; + LazyComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent; - return LazyComponent as ComponentType; + return LazyComponent; } diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 28c371c5b..e4490ba65 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -100,6 +100,14 @@ export function pluralise(amount: number, singular: string, plural = singular + return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`; } +/** Proxies which have an internal target but use a function as the main target require these properties to be unconfigurable */ +export const UNCONFIGURABLE_PROPERTIES = ["arguments", "caller", "prototype"]; + +export function interpolateIfDefined(strings: TemplateStringsArray, ...args: any[]) { + if (args.some(arg => arg == null)) return ""; + return strings.reduce((acc, str, i) => `${acc}${str}${args[i] ?? ""}`, ""); +} + export function tryOrElse(func: () => T, fallback: T): T { try { const res = func(); diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 79f777088..d542d9b72 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,10 +16,9 @@ * along with this program. If not, see . */ -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; +import { findByProps, findComponentByCode } from "@webpack"; -import { LazyComponent } from "./react"; +import { NoopComponent } from "./react"; export const enum ModalSize { SMALL = "small", @@ -47,10 +46,10 @@ export interface ModalOptions { onCloseCallback?: (() => void); } -type RenderFunction = (props: ModalProps) => ReactNode; +type RenderFunction = (props: ModalProps) => React.ReactNode; -export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { - ModalRoot: ComponentType>; - ModalHeader: ComponentType; + ModalHeader: AnyComponentTypeWithChildren<{ /** Flex.Justify.START */ justify?: string; /** Flex.Direction.HORIZONTAL */ @@ -72,14 +71,13 @@ export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { separator?: boolean; className?: string; - }>>; + }>; /** This also accepts Scroller props but good luck with that */ - ModalContent: ComponentType; - [prop: string]: any; - }>>; - ModalFooter: ComponentType; + }>; + ModalFooter: AnyComponentTypeWithChildren<{ /** Flex.Justify.START */ justify?: string; /** Flex.Direction.HORIZONTAL_REVERSE */ @@ -91,8 +89,8 @@ export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { separator?: boolean; className?: string; - }>>; - ModalCloseButton: ComponentType<{ + }>; + ModalCloseButton: AnyComponentType<{ focusProps?: any; onClick(): void; withCircleBackground?: boolean; @@ -101,7 +99,20 @@ export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { }>; }; -export type ImageModal = ComponentType<{ + +export let ModalRoot: Modals["ModalRoot"] = NoopComponent; +export let ModalHeader: Modals["ModalHeader"] = NoopComponent; +export let ModalContent: Modals["ModalContent"] = NoopComponent; +export let ModalFooter: Modals["ModalFooter"] = NoopComponent; +export let ModalCloseButton: Modals["ModalCloseButton"] = NoopComponent; + +export const Modals = findByProps("ModalRoot", "ModalCloseButton", (m: Modals) => { + ({ ModalRoot, ModalHeader, ModalContent, ModalFooter, ModalCloseButton } = m); + + return m; +}); + +export type ImageModalProps = { className?: string; src: string; placeholder: string; @@ -110,24 +121,18 @@ export type ImageModal = ComponentType<{ height?: number; animated?: boolean; responsive?: boolean; - renderLinkComponent(props: any): ReactNode; - renderForwardComponent(props: any): ReactNode; + renderLinkComponent(props: any): React.ReactNode; + renderForwardComponent(props: any): React.ReactNode; maxWidth?: number; maxHeight?: number; shouldAnimate?: boolean; onClose?(): void; shouldHideMediaOptions?: boolean; -}>; +}; -export const ImageModal = findComponentByCodeLazy(".MEDIA_MODAL_CLOSE", "responsive") as ImageModal; +export const ImageModal = findComponentByCode(".MEDIA_MODAL_CLOSE", "responsive"); -export const ModalRoot = LazyComponent(() => Modals.ModalRoot); -export const ModalHeader = LazyComponent(() => Modals.ModalHeader); -export const ModalContent = LazyComponent(() => Modals.ModalContent); -export const ModalFooter = LazyComponent(() => Modals.ModalFooter); -export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton); - -const ModalAPI = findByPropsLazy("openModalLazy"); +const ModalAPI = findByProps("openModalLazy"); /** * Wait for the render promise to resolve, then open a modal with it. diff --git a/src/utils/patches.ts b/src/utils/patches.ts index 87f3ce78c..c5bd81205 100644 --- a/src/utils/patches.ts +++ b/src/utils/patches.ts @@ -19,17 +19,22 @@ import { Patch, PatchReplacement, ReplaceFn } from "./types"; export function canonicalizeMatch(match: T): T { - if (typeof match === "string") return match; - const canonSource = match.source - .replaceAll("\\i", "[A-Za-z_$][\\w$]*"); - return new RegExp(canonSource, match.flags) as T; + if (typeof match === "string") { + return match; + } + + const canonRegex = new RegExp(match.source.replaceAll(String.raw`\i`, String.raw`(?:[A-Za-z_$][\w$]*)`), match.flags); + canonRegex.toString = match.toString.bind(match); + + return canonRegex as T; } export function canonicalizeReplace(replace: T, pluginName: string): T { const self = `Vencord.Plugins.plugins[${JSON.stringify(pluginName)}]`; - if (typeof replace !== "function") + if (typeof replace !== "function") { return replace.replaceAll("$self", self) as T; + } return ((...args) => replace(...args).replaceAll("$self", self)) as T; } diff --git a/src/utils/proxyInner.ts b/src/utils/proxyInner.ts new file mode 100644 index 000000000..fabec64c4 --- /dev/null +++ b/src/utils/proxyInner.ts @@ -0,0 +1,120 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated, Nuckyz and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { UNCONFIGURABLE_PROPERTIES } from "./misc"; + +export const SYM_PROXY_INNER_GET = Symbol.for("vencord.proxyInner.get"); +export const SYM_PROXY_INNER_VALUE = Symbol.for("vencord.proxyInner.innerValue"); + +const handler: ProxyHandler = { + ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName => + [propName, (target: any, ...args: any[]) => Reflect[propName](target[SYM_PROXY_INNER_GET](), ...args)] + )), + set: (target, p, value) => { + const innerTarget = target[SYM_PROXY_INNER_GET](); + return Reflect.set(innerTarget, p, value, innerTarget); + }, + ownKeys: target => { + const keys = Reflect.ownKeys(target[SYM_PROXY_INNER_GET]()); + for (const key of UNCONFIGURABLE_PROPERTIES) { + if (!keys.includes(key)) keys.push(key); + } + return keys; + }, + getOwnPropertyDescriptor: (target, p) => { + if (typeof p === "string" && UNCONFIGURABLE_PROPERTIES.includes(p)) { + return Reflect.getOwnPropertyDescriptor(target, p); + } + + const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_PROXY_INNER_GET](), p); + if (descriptor) Object.defineProperty(target, p, descriptor); + return descriptor; + } +}; + +/** + * A proxy which has an inner value that can be set later. + * When a property is accessed, the proxy looks for the property value in its inner value, and errors if it's not set. + * + * IMPORTANT: + * Destructuring at top level is not supported for proxyInner. + * + * @param err The error message to throw when the inner value is not set + * @param primitiveErr The error message to throw when the inner value is a primitive + * @returns A proxy which will act like the inner value when accessed + */ +export function proxyInner(err: string | (() => string) = "Proxy inner value is undefined, setInnerValue was never called.", primitiveErr = "proxyInner called on a primitive value. This can happen if you try to destructure a primitive at the same tick as the proxy was created.", isChild = false): [proxy: T, setInnerValue: (innerValue: T) => void] { + let isSameTick = true; + if (!isChild) setTimeout(() => isSameTick = false, 0); + + const proxyDummy = Object.assign(function () { }, { + [SYM_PROXY_INNER_GET]: function () { + if (proxyDummy[SYM_PROXY_INNER_VALUE] == null) { + throw new Error(typeof err === "string" ? err : err()); + } + + return proxyDummy[SYM_PROXY_INNER_VALUE]; + }, + [SYM_PROXY_INNER_VALUE]: void 0 as T | undefined + }); + + const proxy = new Proxy(proxyDummy, { + ...handler, + get(target, p, receiver) { + if (p === SYM_PROXY_INNER_GET || p === SYM_PROXY_INNER_VALUE) { + return Reflect.get(target, p, receiver); + } + + // If we're still in the same tick, it means the proxy was immediately used. + // And, if the inner value is still nullish, it means the proxy was used before setInnerValue was called. + // So, proxy the get access to make things like destructuring work as expected. + // We dont need to proxy if the inner value is available, and recursiveSetInnerValue won't ever be called anyways, + // because the top setInnerValue was called before we proxied the get access + // example here will also be a proxy: + // `const { example } = findByProps("example");` + if (isSameTick && !isChild && proxyDummy[SYM_PROXY_INNER_VALUE] == null) { + console.warn( + "Destructuring webpack finds/proxyInner/proxyLazy at top level is deprecated. For more information read https://github.com/Vendicated/Vencord/pull/2409#issue-2277161516" + + "\nConsider not destructuring, using findProp or if you really need to destructure, using mapMangledModule instead." + ); + + const [recursiveProxy, recursiveSetInnerValue] = proxyInner(err, primitiveErr, true); + + recursiveSetInnerValues.push((innerValue: T) => { + // Set the inner value of the destructured value as the prop value p of the parent + recursiveSetInnerValue(Reflect.get(innerValue as object, p, innerValue)); + }); + + return recursiveProxy; + } + + const innerTarget = target[SYM_PROXY_INNER_GET](); + if (typeof innerTarget === "object" || typeof innerTarget === "function") { + return Reflect.get(innerTarget, p, innerTarget); + } + + throw new Error(primitiveErr); + } + }); + + // Values destructured in the same tick the proxy was created will push their setInnerValue here + const recursiveSetInnerValues = [] as Array<(innerValue: T) => void>; + + // Once we set the parent inner value, we will call the setInnerValue functions of the destructured values, + // for them to get the proper value from the parent and use as their inner instead + function setInnerValue(innerValue: T) { + proxyDummy[SYM_PROXY_INNER_VALUE] = innerValue; + recursiveSetInnerValues.forEach(setInnerValue => setInnerValue(innerValue)); + + // Avoid binding toString if the inner value is null. + // This can happen if we are setting the inner value as another instance of proxyInner, which will cause that proxy to instantly evaluate and throw an error + if (typeof innerValue === "function" && (innerValue[SYM_PROXY_INNER_GET] == null || innerValue[SYM_PROXY_INNER_VALUE] != null)) { + proxy.toString = innerValue.toString.bind(innerValue); + } + } + + return [proxy, setInnerValue]; +} diff --git a/src/webpack/api.tsx b/src/webpack/api.tsx new file mode 100644 index 000000000..581a5c48f --- /dev/null +++ b/src/webpack/api.tsx @@ -0,0 +1,1086 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated, Nuckyz and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { makeLazy, proxyLazy } from "@utils/lazy"; +import { AnyLazyComponentType, LazyComponent, SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact"; +import { Logger } from "@utils/Logger"; +import { canonicalizeMatch } from "@utils/patches"; +import { proxyInner, SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner"; + +import { traceFunction } from "../debug/Tracer"; +import { GenericStore } from "./common"; +import { AnyModuleFactory, ModuleExports, ModuleFactory, WebpackRequire } from "./wreq"; + +const logger = new Logger("Webpack"); + +export let _resolveDiscordLoaded: () => void; +/** + * Fired once a gateway connection to Discord has been established. + * This indicates that the core Webpack modules have been initialized, and we are logged in. + */ +export const onceDiscordLoaded = new Promise(r => _resolveDiscordLoaded = r); + +export let wreq: WebpackRequire; +export let cache: WebpackRequire["c"]; + +export function _initWebpack(webpackRequire: WebpackRequire) { + wreq = webpackRequire; + + if (webpackRequire.c == null) return; + cache = webpackRequire.c; + + Reflect.defineProperty(webpackRequire.c, Symbol.toStringTag, { + value: "ModuleCache", + configurable: true, + writable: true, + enumerable: false + }); +} + +export type ModListenerInfo = { + id: PropertyKey; + factory: AnyModuleFactory; +}; + +export type ModCallbackInfo = { + id: PropertyKey; + exportKey: PropertyKey | null; + factory: AnyModuleFactory; +}; + +export type FactoryListernFn = (factory: AnyModuleFactory) => void; +export type ModListenerFn = (module: ModuleExports, info: ModListenerInfo) => void; +export type ModCallbackFn = ((module: ModuleExports, info: ModCallbackInfo) => void) & { + $$vencordCallbackCalled?: () => boolean; +}; + +export const factoryListeners = new Set(); +export const moduleListeners = new Set(); +export const waitForSubscriptions = new Map(); + +let devToolsOpen = false; +if (IS_DEV && IS_DISCORD_DESKTOP) { + // At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed + setTimeout(() => { + DiscordNative?.window.setDevtoolsCallbacks(() => devToolsOpen = true, () => devToolsOpen = false); + }, 0); +} + +export type PropsFilter = Array; +export type CodeFilter = Array; +export type CodeFilterWithSingle = string | RegExp | CodeFilter; +export type StoreNameFilter = string; + +export type FilterFn = ((module: ModuleExports) => boolean) & { + $$vencordProps?: Array; + $$vencordIsComponentFilter?: boolean; + $$vencordIsFactoryFilter?: boolean; +}; + +export const stringMatches = (s: string, filter: CodeFilter) => + filter.every(f => + typeof f === "string" + ? s.includes(f) + : (f.global && (f.lastIndex = 0), f.test(s)) + ); + +export const filters = { + byProps: (...props: PropsFilter): FilterFn => { + const filter: FilterFn = props.length === 1 + ? m => m?.[props[0]] !== undefined + : m => props.every(p => m?.[p] !== undefined); + + filter.$$vencordProps = ["byProps", ...props]; + return filter; + }, + + byCode: (...code: CodeFilter): FilterFn => { + const parsedCode = code.map(canonicalizeMatch); + const filter: FilterFn = m => { + if (typeof m !== "function") return false; + return stringMatches(String(m), parsedCode); + }; + + filter.$$vencordProps = ["byCode", ...code]; + return filter; + }, + + byStoreName: (name: StoreNameFilter): FilterFn => { + const filter: FilterFn = m => m?.constructor?.displayName === name || m?.constructor?.persistKey === name; + + filter.$$vencordProps = ["byStoreName", name]; + return filter; + }, + + // For use inside mapMangledModule + componentByFilter: (filter: FilterFn): FilterFn => { + filter.$$vencordIsComponentFilter = true; + return filter; + }, + + componentByCode: (...code: CodeFilter): FilterFn => { + const byCodeFilter = filters.byCode(...code); + const filter: FilterFn = m => { + let inner = m; + + while (inner != null) { + if (byCodeFilter(inner)) return true; + else if (!inner.$$typeof) return false; + else if (inner.type) inner = inner.type; // memos + else if (inner.render) inner = inner.render; // forwardRefs + else return false; + } + + return false; + }; + + filter.$$vencordProps = ["componentByCode", ...code]; + filter.$$vencordIsComponentFilter = true; + return filter; + }, + + componentByFields: (...fields: PropsFilter): FilterFn => { + const byPropsFilter = filters.byProps(...fields); + const filter: FilterFn = m => m?.prototype?.render && byPropsFilter(m.prototype); + + filter.$$vencordProps = ["componentByFields", ...fields]; + filter.$$vencordIsComponentFilter = true; + return filter; + }, + + byFactoryCode: (...code: CodeFilter): FilterFn => { + const byCodeFilter = filters.byCode(...code); + + byCodeFilter.$$vencordProps = ["byFactoryCode", ...code]; + byCodeFilter.$$vencordIsFactoryFilter = true; + return byCodeFilter; + } +}; + +export const webpackSearchHistory = [] as Array<["waitFor" | "find" | "findComponent" | "findExportedComponent" | "findComponentByCode" | "findComponentByFields" | "findByProps" | "findProp" | "findByCode" | "findStore" | "findByFactoryCode" | "mapMangledModule" | "findModuleFactory" | "extractAndLoadChunks" | "webpackDependantLazy" | "webpackDependantLazyComponent", any[]]>; + +function printFilter(filter: FilterFn) { + if (filter.$$vencordProps != null) { + const props = filter.$$vencordProps; + return `${props[0]}(${props.slice(1).map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`; + } + + return String(filter); +} + +function wrapWebpackComponent

(err: string | (() => string)): [WrapperComponent: AnyLazyComponentType

, setInnerComponent: (rawComponent: any, parsedComponent: React.ComponentType

) => void] { + let InnerComponent = null as AnyComponentType

| null; + + let findFailedLogged = false; + const WrapperComponent: AnyLazyComponentType

= function (props) { + if (InnerComponent === null && !findFailedLogged) { + findFailedLogged = true; + logger.error(typeof err === "string" ? err : err()); + } + + return InnerComponent && ; + }; + + WrapperComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent; + + function setInnerComponent(RawComponent: any, ParsedComponent: React.ComponentType

) { + InnerComponent = ParsedComponent; + Object.assign(WrapperComponent, RawComponent); + } + + return [WrapperComponent, setInnerComponent]; +} + +/** + * Wait for the first export or module exports that matches the provided filter to be required, + * then call the callback with the export or module exports as the first argument. + * + * If the module containing the export(s) is already required, the callback will be called immediately. + * + * @param filter A function that takes an export or module exports and returns a boolean + * @param callback A function that takes the find result as its first argument + */ +export function waitFor(filter: FilterFn, callback: ModCallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") { + throw new Error("Invalid filter. Expected a function got " + typeof filter); + } + if (typeof callback !== "function") { + throw new Error("Invalid callback. Expected a function got " + typeof callback); + } + + if (IS_REPORTER && !isIndirect) { + const originalCallback = callback; + + let callbackCalled = false; + callback = function (this: unknown) { + callbackCalled = true; + + Reflect.apply(originalCallback, this, arguments); + }; + + callback.$$vencordCallbackCalled = () => callbackCalled; + webpackSearchHistory.push(["waitFor", [callback, filter]]); + } + + if (cache != null) { + const cacheFindResult = _cacheFind(filter); + + if (cacheFindResult != null) { + const { result, id, exportKey, factory } = cacheFindResult; + return callback(result, { id, exportKey, factory }); + } + } + + waitForSubscriptions.set(filter, callback); +} + +/** + * Find the first export or module exports that matches the filter. + * + * The way this works internally is: + * Wait for the first export or module exports that matches the provided filter to be required, + * then call the parse function with the export or module exports as the first argument. + * + * If the module containing the export(s) is already required, the parse function will be called immediately. + * + * The parse function must return a value that will be used as the proxy inner value. + * + * If no parse function is specified, the default parse will assign the proxy inner value to the plain find result. + * + * @param filter A function that takes an export or module exports and returns a boolean + * @param parse A function that takes the find result as its first argument and returns something to use as the proxy inner value. Useful if you want to use a value from the find result, instead of all of it. Defaults to the find result itself + * @returns A proxy that has the parse function return value as its true value, or the plain parse function return value, if it was called immediately. + */ +export function find(filter: FilterFn, parse: (module: ModuleExports) => ModuleExports = m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") { + throw new Error("Invalid filter. Expected a function got " + typeof filter); + } + if (typeof parse !== "function") { + throw new Error("Invalid find parse. Expected a function got " + typeof parse); + } + + const [proxy, setInnerValue] = proxyInner(`Webpack find matched no module. Filter: ${printFilter(filter)}`, "Webpack find with proxy called on a primitive value. This can happen if you try to destructure a primitive in the top level definition of the find."); + waitFor(filter, m => setInnerValue(parse(m)), { isIndirect: true }); + + if (IS_REPORTER && !isIndirect) { + webpackSearchHistory.push(["find", [proxy, filter]]); + } + + if (proxy[SYM_PROXY_INNER_VALUE] != null) return proxy[SYM_PROXY_INNER_VALUE] as T; + + return proxy; +} + +/** + * Find the first exported component that matches the filter. + * + * @param filter A function that takes an export or module exports and returns a boolean + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findComponent

(filter: FilterFn, parse: (component: ModuleExports) => React.ComponentType

= m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") { + throw new Error("Invalid filter. Expected a function got " + typeof filter); + } + if (typeof parse !== "function") { + throw new Error("Invalid component parse. Expected a function got " + typeof parse); + } + + const [WrapperComponent, setInnerComponent] = wrapWebpackComponent

(`Webpack find matched no module. Filter: ${printFilter(filter)}`); + waitFor(filter, m => setInnerComponent(m, parse(m)), { isIndirect: true }); + + if (IS_REPORTER && !isIndirect) { + webpackSearchHistory.push(["findComponent", [WrapperComponent, filter]]); + } + + if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as AnyLazyComponentType

; + + return WrapperComponent; +} + +/** + * Find the first component that is exported by the first prop name. + * + * @example findExportedComponent("FriendRow") + * @example findExportedComponent("FriendRow", "Friend", FriendRow => React.memo(FriendRow)) + * + * @param props A list of prop names to search the exports for + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findExportedComponent

(...props: PropsFilter | [...PropsFilter, (component: ModuleExports) => React.ComponentType

]) { + const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (component: ModuleExports) => React.ComponentType

; + const newProps = props as PropsFilter; + + const filter = filters.byProps(...newProps); + + const [WrapperComponent, setInnerComponent] = wrapWebpackComponent

(`Webpack find matched no module. Filter: ${printFilter(filter)}`); + waitFor(filter, m => setInnerComponent(m[newProps[0]], parse(m[newProps[0]])), { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findExportedComponent", [WrapperComponent, ...newProps]]); + } + + if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as AnyLazyComponentType

; + + return WrapperComponent; +} + +/** + * Find the first exported component which when its code is stringified includes all the given code. + * + * @example findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR") + * @example findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)", ColorPicker => React.memo(ColorPicker)) + * + * @param code A list of code to search each export for + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findComponentByCode

(...code: CodeFilter | [...CodeFilter, (component: ModuleExports) => React.ComponentType

]) { + const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (component: ModuleExports) => React.ComponentType

; + const newCode = code as CodeFilter; + + const ComponentResult = findComponent

(filters.componentByCode(...newCode), parse, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newCode]]); + } + + return ComponentResult; +} + +/** + * Find the first exported class component which includes all the given fields in its prototype. + * + * @example findComponentByFields("renderTooltip", "shouldShowTooltip") + * @example findComponentByFields("renderTooltip", "shouldShowTooltip", Tooltip => Tooltip) + * + * @param code A list of fields to search each exported class component for + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findComponentByFields

(...fields: PropsFilter | [...PropsFilter, (component: ModuleExports) => React.ComponentType

]) { + const parse = (typeof fields.at(-1) === "function" ? fields.pop() : m => m) as (component: ModuleExports) => React.ComponentType

; + const newFields = fields as PropsFilter; + + const ComponentResult = findComponent

(filters.componentByFields(...newFields), parse, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newFields]]); + } + + return ComponentResult; +} + +/** + * Find the first module exports or export that includes all the given props. + * + * @param props A list of props to search the module or exports for + * @param parse A function that takes the find result as its first argument and returns something. Useful if you want to use a value from the find result, instead of all of it. Defaults to the find result itself + */ +export function findByProps(...props: PropsFilter | [...PropsFilter, (module: ModuleExports) => T]) { + const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (module: ModuleExports) => T; + const newProps = props as PropsFilter; + + const result = find(filters.byProps(...newProps), parse, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findByProps", [result, ...newProps]]); + } + + return result; +} + +/** + * Find the first prop value defined by the first prop name, which is in a module exports or export including all the given props. + * + * @example const getUser = findProp("getUser", "fetchUser") + * // An object which contains the getUser and fetchUser props is found and the value of getUser is returned + * + * @param props A list of props to search the module or exports for + * @param parse A function that takes the find result as its first argument and returns something. Useful if you want to use a value from the find result, instead of all of it. Defaults to the find result itself + */ +export function findProp(...props: PropsFilter | [...PropsFilter, (module: ModuleExports) => T]) { + const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (module: ModuleExports) => T; + const newProps = props as PropsFilter; + + const result = find(filters.byProps(...newProps), m => parse(m[newProps[0]]), { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findProp", [result, ...newProps]]); + } + + return result; +} + +/** + * Find the first exported function which when stringified includes all the given code. + * + * @param code A list of code to search each export for + * @param parse A function that takes the find result as its first argument and returns something. Useful if you want to use a value from the find result, instead of all of it. Defaults to the find result itself + */ +export function findByCode(...code: CodeFilter | [...CodeFilter, (module: ModuleExports) => T]) { + const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (module: ModuleExports) => T; + const newCode = code as CodeFilter; + + const result = find(filters.byCode(...newCode), parse, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findByCode", [result, ...newCode]]); + } + + return result; +} + +/** + * Find a store by its name. + * + * @param name The store name + */ +export function findStore(name: StoreNameFilter) { + const result = find(filters.byStoreName(name), m => m, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findStore", [result, name]]); + } + + return result; +} + +/** + * Find the module exports of the first module which the factory when stringified includes all the given code. + * + * @param code A list of code to search each factory for + * @param parse A function that takes the find result as its first argument and returns something. Useful if you want to use a value from the find result, instead of all of it. Defaults to the find result itself + */ +export function findByFactoryCode(...code: CodeFilter | [...CodeFilter, (module: ModuleExports) => T]) { + const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (module: ModuleExports) => T; + const newCode = code as CodeFilter; + + const result = find(filters.byFactoryCode(...newCode), parse, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["findByFactoryCode", [result, ...newCode]]); + } + + return result; +} + +/** + * Find the module exports of the first module which the factory when stringified includes all the given code, + * then map them into an easily usable object via the specified mappers. + * + * IMPORTANT: mapMangledModule is the only way to webpack find primitive values, as long as you dont destructure them at top level. + * + * IMPORTANT: You can destructure the properties of the returned object at top level as long as the property filter does not return a primitive value export. + * + * @example + * const Modals = mapMangledModule("headerIdIsManaged:", { + * openModal: filters.byCode("headerIdIsManaged:"), + * closeModal: filters.byCode("key==") + * }); + * + * @param code The code or list of code to search each factory for + * @param mappers Mappers to create the non mangled exports object + * @returns Unmangled exports as specified in mappers + */ +export function mapMangledModule(code: CodeFilterWithSingle, mappers: Record) { + const mapping = {} as Record; + const proxyInnerSetters = {} as Record[1]>; + const wrapperComponentSetters = {} as Record[1]>; + + for (const newName in mappers) { + const mapperFilter = mappers[newName]; + + // Wrapper to select whether the parent factory filter or child mapper filter failed when the error is thrown + const errorWrapper = () => `Webpack mapMangledModule ${callbackCalled ? "mapper" : "factory"} filter matched no module. Filter: ${printFilter(callbackCalled ? mapperFilter : factoryFilter)}`; + + if (mapperFilter.$$vencordIsComponentFilter) { + const [WrapperComponent, setInnerComponent] = wrapWebpackComponent(errorWrapper); + mapping[newName] = WrapperComponent; + wrapperComponentSetters[newName] = setInnerComponent; + } else { + const [proxy, setInnerValue] = proxyInner(errorWrapper, "Webpack find with proxy called on a primitive value. This may happen if you are trying to destructure a mapMangledModule primitive value on top level."); + mapping[newName] = proxy; + proxyInnerSetters[newName] = setInnerValue; + } + } + + const factoryFilter = filters.byFactoryCode(...Array.isArray(code) ? code : [code]); + + let callbackCalled = false; + waitFor(factoryFilter, exports => { + callbackCalled = true; + + if (typeof exports !== "object") return; + + for (const exportKey in exports) { + const exportValue = exports[exportKey]; + if (exportValue == null) continue; + + for (const newName in mappers) { + const filter = mappers[newName]; + + if (filter(exportValue)) { + if (typeof exportValue !== "object" && typeof exportValue !== "function") { + mapping[newName] = exportValue; + } + + if (filter.$$vencordIsComponentFilter) { + wrapperComponentSetters[newName](exportValue, exportValue); + } else { + proxyInnerSetters[newName](exportValue); + } + } + } + } + }, { isIndirect: true }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["mapMangledModule", [mapping, code, mappers]]); + } + + if (callbackCalled) { + for (const innerMap in mapping) { + const innerValue = mapping[innerMap]; + + if (innerValue[SYM_PROXY_INNER_VALUE] != null) { + mapping[innerMap] = innerValue[SYM_PROXY_INNER_VALUE]; + } else if (innerValue[SYM_LAZY_COMPONENT_INNER] != null && innerValue[SYM_LAZY_COMPONENT_INNER]() != null) { + mapping[innerMap] = innerValue[SYM_LAZY_COMPONENT_INNER](); + } + } + } + + return mapping; +} + +/** + * Find the first module factory which when stringified includes all the given code. + */ +export function findModuleFactory(code: CodeFilterWithSingle, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + const filter = filters.byFactoryCode(...Array.isArray(code) ? code : [code]); + + const [proxy, setInnerValue] = proxyInner(`Webpack module factory find matched no module. Filter: ${printFilter(filter)}`, "Webpack find with proxy called on a primitive value. This can happen if you try to destructure a primitive in the top level definition of the find."); + waitFor(filter, (_, { factory }) => setInnerValue(factory), { isIndirect: true }); + + if (IS_REPORTER && !isIndirect) { + webpackSearchHistory.push(["findModuleFactory", [proxy, code]]); + } + + if (proxy[SYM_PROXY_INNER_VALUE] != null) return proxy[SYM_PROXY_INNER_VALUE] as AnyModuleFactory; + + return proxy; +} + +/** + * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. + * + * Wraps the result of factory in a Proxy you can consume as if it wasn't lazy. + * On first property access, the factory is evaluated. + * + * @param factory Factory returning the result + * @param attempts How many times to try to evaluate the factory before giving up + * @returns Result of factory function + */ +export function webpackDependantLazy(factory: () => T, attempts?: number) { + if (IS_REPORTER) { + webpackSearchHistory.push(["webpackDependantLazy", [factory]]); + } + + return proxyLazy(factory, attempts, `Webpack dependant lazy factory failed:\n${factory}`, "Webpack dependant lazy called on a primitive value. This can happen if you try to destructure a primitive in the top level definition of the lazy."); +} + +/** + * This is just a wrapper around {@link LazyComponent} to make our reporter test for your webpack finds. + * + * A lazy component. The factory method is called on first render. + * + * @param factory Function returning a Component + * @param attempts How many times to try to get the component before giving up + * @returns Result of factory function + */ +export function webpackDependantLazyComponent

(factory: () => any, attempts?: number) { + if (IS_REPORTER) { + webpackSearchHistory.push(["webpackDependantLazyComponent", [factory]]); + } + + return LazyComponent

(factory, attempts, `Webpack dependant LazyComponent factory failed:\n${factory}`); +} + +export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/; +export const ChunkIdsRegex = /\("([^"]+?)"\)/g; + +function handleWebpackError(err: string, returnValue: any, ...args: any[]) { + if (!IS_DEV || devToolsOpen) { + logger.warn(err, ...args); + return returnValue; + } + + throw new Error(err); // Throw the error in development if devtools are closed +} + +/** + * Extract and load chunks using their entry point. + * + * @param code The code or list of code the module factory containing the lazy chunk loading must include + * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory + * @returns A function that returns a promise that resolves with a boolean whether the chunks were loaded, on first call + */ +export function extractAndLoadChunksLazy(code: CodeFilterWithSingle, matcher: RegExp = DefaultExtractAndLoadChunksRegex) { + const module = findModuleFactory(code, { isIndirect: true }); + + const extractAndLoadChunks = makeLazy(async () => { + if (module[SYM_PROXY_INNER_GET] != null && module[SYM_PROXY_INNER_VALUE] == null) { + return handleWebpackError("extractAndLoadChunks: Couldn't find module factory", false, "Code:", code, "Matcher:", matcher); + } + + const match = String(module).match(canonicalizeMatch(matcher)); + if (!match) { + return handleWebpackError("extractAndLoadChunks: Couldn't find chunk loading in module factory code", false, "Code:", code, "Matcher:", matcher); + } + + const [, rawChunkIds, entryPointId] = match; + if (Number.isNaN(Number(entryPointId))) { + return handleWebpackError("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number", false, "Code:", code, "Matcher:", matcher); + } + + if (rawChunkIds) { + const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map(m => Number(m[1])); + await Promise.all(chunkIds.map(id => wreq.e(id))); + } + + if (wreq.m[entryPointId] == null) { + return handleWebpackError("extractAndLoadChunks: Entry point is not loaded in the module factories, perhaps one of the chunks failed to load", false, "Code:", code, "Matcher:", matcher); + } + + wreq(Number(entryPointId)); + return true; + }); + + if (IS_REPORTER) { + webpackSearchHistory.push(["extractAndLoadChunks", [extractAndLoadChunks, code, matcher]]); + } + + return extractAndLoadChunks; +} + +export type CacheFindResult = { + /** The find result. */ + result: ModuleExports; + /** The id of the module exporting where the result was found. */ + id: PropertyKey; + /** The key exporting the result. `null` if the find result was all the module exports. */ + exportKey: PropertyKey | null; + /** The factory of the module exporting the result. */ + factory: AnyModuleFactory; +} | undefined; + +/** + * Find the first export or module exports from an already required module that matches the filter. + * + * @param filter A function that takes an export or module exports and returns a boolean + */ +export const _cacheFind = traceFunction("cacheFind", function _cacheFind(filter: FilterFn): CacheFindResult { + if (typeof filter !== "function") { + throw new Error("Invalid filter. Expected a function got " + typeof filter); + } + + for (const key in cache) { + const mod = cache[key]; + if (!mod?.loaded || mod?.exports == null) continue; + + const factory = wreq.m[key] as AnyModuleFactory; + + if (filter.$$vencordIsFactoryFilter) { + if (filter(wreq.m[key])) { + return { result: mod.exports, id: key, exportKey: null, factory }; + } + + continue; + } + + if (filter(mod.exports)) { + return { result: mod.exports, id: key, exportKey: null, factory }; + } + + if (typeof mod.exports !== "object") { + continue; + } + + if (mod.exports.default != null && filter(mod.exports.default)) { + return { result: mod.exports.default, id: key, exportKey: "default ", factory }; + } + + for (const exportKey in mod.exports) if (exportKey.length <= 3) { + const exportValue = mod.exports[exportKey]; + + if (exportValue != null && filter(exportValue)) { + return { result: exportValue, id: key, exportKey, factory }; + } + } + } +}); + +/** + * Find the first export or module exports from an already required module that matches the filter. + * + * @param filter A function that takes an export or module exports and returns a boolean + * @returns The found export, module exports, module factory, or undefined + */ +export function cacheFind(filter: FilterFn, shouldReturnFactory: boolean = false) { + const cacheFindResult = _cacheFind(filter); + return shouldReturnFactory ? cacheFindResult?.factory : cacheFindResult?.result; +} + +/** + * Find the the export or module exports from an all the required modules that match the filter. + * + * @param filter A function that takes an export or module exports and returns a boolean + */ +export function _cacheFindAll(filter: FilterFn): NonNullable[] { + if (typeof filter !== "function") { + throw new Error("Invalid filter. Expected a function got " + typeof filter); + } + + const results: NonNullable[] = []; + + for (const key in cache) { + const mod = cache[key]; + if (!mod?.loaded || mod?.exports == null) continue; + + const factory = wreq.m[key] as AnyModuleFactory; + + if (filter.$$vencordIsFactoryFilter) { + if (filter(wreq.m[key])) { + results.push({ result: mod.exports, id: key, exportKey: null, factory }); + } + + continue; + } + + if (filter(mod.exports)) { + results.push({ result: mod.exports, id: key, exportKey: null, factory }); + } + + if (typeof mod.exports !== "object") { + continue; + } + + if (mod.exports.default != null && filter(mod.exports.default)) { + results.push({ result: mod.exports.default, id: key, exportKey: "default ", factory }); + } + + for (const exportKey in mod.exports) if (exportKey.length <= 3) { + const exportValue = mod.exports[exportKey]; + + if (exportValue != null && filter(exportValue)) { + results.push({ result: exportValue, id: key, exportKey, factory }); + break; + } + } + } + + return results; +} + +/** + * Find the the export or module exports from an all the required modules that match the filter. + * + * @param filter A function that takes an export or module exports and returns a boolean + * @returns An array of found exports, module exports, or module factories + */ +export function cacheFindAll(filter: FilterFn, shouldReturnFactories: boolean = false) { + const cacheFindAllResults = _cacheFindAll(filter); + return cacheFindAllResults.map(({ result, factory }) => shouldReturnFactories ? factory : result); +} + +/** + * Find the id of the first already loaded module factory that includes all the given code. + */ +export function cacheFindModuleId(...code: CodeFilter) { + return _cacheFind(filters.byFactoryCode(...code))?.id; +} + +/** + * Search factories by keyword. This searches the source code of the module factories, + * meaning you can search all sorts of things, methodName, strings somewhere in the code, etc. + * + * @param code One or more strings or regexes + * @returns Mapping of found factories by their module id + */ +export function searchFactories(...code: CodeFilter) { + const filter = filters.byFactoryCode(...code); + + return _cacheFindAll(filter).reduce((results, { factory, id }) => { + results[id] = factory; + + return results; + }, {} as Record); +} + +/** + * Extract a specific module by id into its own Source File. This has no effect on + * the code, it is only useful to be able to look at a specific module without having + * to view a massive file. extract then returns the extracted module so you can jump to it. + * As mentioned above, note that this extracted module is not actually used, + * so putting breakpoints or similar will have no effect. + * + * @param id The id of the module to extract + */ +export function extract(id: PropertyKey) { + const factory = wreq.m[id]; + if (factory == null) return null; + + const code = ` +// [EXTRACTED] WebpackModule${String(id)} +// WARNING: This module was extracted to be more easily readable. +// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! + +0,${String(factory)} +//# sourceURL=ExtractedWebpackModule${String(id)} +`; + const extracted: ModuleFactory = (0, eval)(code); + return extracted; +} + +/* ------------------------------- Deprecations ------------------------------- */ + +/** + * @deprecated Use separate finds instead + * Same as {@link cacheFind} but in bulk. + * + * @param filterFns Array of filters + * @returns Array of results in the same order as the passed filters + */ +export const cacheFindBulk = traceFunction("cacheFindBulk", function cacheFindBulk(...filterFns: FilterFn[]) { + if (!Array.isArray(filterFns)) { + throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); + } + + const { length } = filterFns; + + if (length === 0) { + throw new Error("Expected at least two filters."); + } + + if (length === 1) { + if (IS_DEV) { + throw new Error("bulk called with only one filter. Use find"); + } + + return [cacheFind(filterFns[0])]; + } + + let found = 0; + const results: ModuleExports[] = Array(length); + + const filters = [...filterFns] as Array; + + outer: + for (const key in cache) { + const mod = cache[key]; + if (!mod?.loaded || mod?.exports == null) continue; + + for (let i = 0; i < length; i++) { + const filter = filters[i]; + if (filter == null) continue; + + if (filter.$$vencordIsFactoryFilter) { + if (filter(wreq.m[key])) { + results[i] = mod.exports; + filters[i] = undefined; + + if (++found === length) break outer; + } + + break; + } + + if (filter(mod.exports)) { + results[i] = mod.exports; + filters[i] = undefined; + + if (++found === length) break outer; + break; + } + + if (typeof mod.exports !== "object") { + break; + } + + if (mod.exports.default != null && filter(mod.exports.default)) { + results[i] = mod.exports.default; + filters[i] = undefined; + + if (++found === length) break outer; + continue; + } + + for (const exportKey in mod.exports) if (exportKey.length <= 3) { + const exportValue = mod.exports[exportKey]; + + if (exportValue != null && filter(mod.exports[key])) { + results[i] = exportValue; + filters[i] = undefined; + + if (++found === length) break outer; + break; + } + } + } + } + + return results; +}); + +/** + * @deprecated use {@link findModuleFactory} instead + * Find the first already loaded module factory that includes all the given code. + */ +export const cacheFindModuleFactory = traceFunction("cacheFindModuleFactory", function cacheFindModuleFactory(...code: CodeFilter) { + const id = cacheFindModuleId(...code); + if (id == null) return; + + return wreq.m[id]; +}); + +function deprecatedRedirect any>(oldMethod: string, newMethod: string, redirect: T): T { + return ((...args: Parameters) => { + logger.warn(`Method ${oldMethod} is deprecated. Use ${newMethod} instead. For more information read https://github.com/Vendicated/Vencord/pull/2409#issue-2277161516`); + return redirect(...args); + }) as T; +} + +/** + * @deprecated Use {@link webpackDependantLazy} instead + * + * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. + * + * Wraps the result of factory in a Proxy you can consume as if it wasn't lazy. + * On first property access, the factory is evaluated. + * + * @param factory Factory returning the result + * @param attempts How many times to try to evaluate the factory before giving up + * @returns Result of factory function + */ +export const proxyLazyWebpack = deprecatedRedirect("proxyLazyWebpack", "webpackDependantLazy", webpackDependantLazy); + +/** + * @deprecated Use {@link webpackDependantLazyComponent} instead + * + * This is just a wrapper around {@link LazyComponent} to make our reporter test for your webpack finds. + * + * A lazy component. The factory method is called on first render. + * + * @param factory Function returning a Component + * @param attempts How many times to try to get the component before giving up + * @returns Result of factory function + */ +export const LazyComponentWebpack = deprecatedRedirect("LazyComponentWebpack", "webpackDependantLazyComponent", webpackDependantLazyComponent); + +/** + * @deprecated Use {@link find} instead + * + * Find the first module that matches the filter, lazily + */ +export const findLazy = deprecatedRedirect("findLazy", "find", find); + +/** + * @deprecated Use {@link findByProps} instead + * + * Find the first module that has the specified properties, lazily + */ +export const findByPropsLazy = deprecatedRedirect("findByPropsLazy", "findByProps", findByProps); + +/** + * @deprecated Use {@link findByCode} instead + * + * Find the first function that includes all the given code, lazily + */ +export const findByCodeLazy = deprecatedRedirect("findByCodeLazy", "findByCode", findByCode); + +/** + * @deprecated Use {@link findStore} instead + * + * Find a store by its displayName, lazily + */ +export const findStoreLazy = deprecatedRedirect("findStoreLazy", "findStore", findStore); + +/** + * @deprecated Use {@link findComponent} instead + * + * Finds the first component that matches the filter, lazily. + */ +export const findComponentLazy = deprecatedRedirect("findComponentLazy", "findComponent", findComponent); + +/** + * @deprecated Use {@link findComponentByCode} instead + * + * Finds the first component that includes all the given code, lazily + */ +export const findComponentByCodeLazy = deprecatedRedirect("findComponentByCodeLazy", "findComponentByCode", findComponentByCode); + +/** + * @deprecated Use {@link findExportedComponent} instead + * + * Finds the first component that is exported by the first prop name, lazily + */ +export const findExportedComponentLazy = deprecatedRedirect("findExportedComponentLazy", "findExportedComponent", findExportedComponent); + +/** + * @deprecated Use {@link mapMangledModule} instead + * + * {@link mapMangledModule}, lazy. + * + * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) + * then maps it into an easily usable module via the specified mappers. + * + * @param code The code to look for + * @param mappers Mappers to create the non mangled exports + * @returns Unmangled exports as specified in mappers + * + * @example mapMangledModule("headerIdIsManaged:", { + * openModal: filters.byCode("headerIdIsManaged:"), + * closeModal: filters.byCode("key==") + * }) + */ +export const mapMangledModuleLazy = deprecatedRedirect("mapMangledModuleLazy", "mapMangledModule", mapMangledModule); + +/** + * @deprecated Use {@link cacheFindAll} instead + */ +export const findAll = deprecatedRedirect("findAll", "cacheFindAll", cacheFindAll); + +/** + * @deprecated Use separate finds instead + * + * Same as {@link cacheFind} but in bulk + * + * @param filterFns Array of filters. Please note that this array will be modified in place, so if you still + * need it afterwards, pass a copy. + * @returns Array of results in the same order as the passed filters + */ +export const findBulk = deprecatedRedirect("findBulk", "cacheFindBulk", cacheFindBulk); + +/** + * @deprecated Use {@link cacheFindModuleId} instead + * + * Find the id of the first module factory that includes all the given code + * @returns string or null + */ +export const findModuleId = deprecatedRedirect("findModuleId", "cacheFindModuleId", cacheFindModuleId); + +/** + * @deprecated Use {@link searchFactories} instead + * + * Search modules by keyword. This searches the factory methods, + * meaning you can search all sorts of things, methodName, strings somewhere in the code, etc. + * + * @param code One or more strings or regexes + * @returns Mapping of found modules + */ +export const search = deprecatedRedirect("search", "searchFactories", searchFactories); diff --git a/src/webpack/common/classes.ts b/src/webpack/common/classes.ts index ca3d75f57..bdaff12c4 100644 --- a/src/webpack/common/classes.ts +++ b/src/webpack/common/classes.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { findByPropsLazy, findLazy } from "@webpack"; +import { find, findByProps } from "@webpack"; import * as t from "./types/classes"; -export const ModalImageClasses: t.ImageModalClasses = findLazy(m => m.image && m.modal && !m.applicationIcon); -export const ButtonWrapperClasses: t.ButtonWrapperClasses = findByPropsLazy("buttonWrapper", "buttonContent"); +export const ModalImageClasses = find(m => m.image && m.modal && !m.applicationIcon); +export const ButtonWrapperClasses = findByProps("buttonWrapper", "buttonContent"); diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 0bcb82d1b..d94373d07 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -16,52 +16,43 @@ * along with this program. If not, see . */ -import { filters, findByPropsLazy, waitFor } from "@webpack"; +import { NoopComponent } from "@utils/react"; +import { filters, findByProps, findComponent, findComponentByCode, findExportedComponent } from "@webpack"; -import { waitForComponent } from "./internal"; import * as t from "./types/components"; -export let Forms = {} as { - FormTitle: t.FormTitle, - FormSection: t.FormSection, - FormDivider: t.FormDivider, - FormText: t.FormText, -}; +export let Card: t.Card = NoopComponent as any; +export let Button: t.Button = NoopComponent as any; +export let Switch: t.Switch = NoopComponent; +export let Tooltip: t.Tooltip = NoopComponent as any; +export let TooltipContainer: t.TooltipContainer = NoopComponent as any; +export let TextInput: t.TextInput = NoopComponent as any; +export let TextArea: t.TextArea = NoopComponent; +export let Text: t.Text = NoopComponent; +export let Heading: t.Heading = NoopComponent; +export let Select: t.Select = NoopComponent; +export let SearchableSelect: t.SearchableSelect = NoopComponent; +export let Slider: t.Slider = NoopComponent; +export let ButtonLooks: t.ButtonLooks; +export let Popout: t.Popout = NoopComponent as any; +export let Dialog: t.Dialog = NoopComponent; +export let TabBar: t.TabBar = NoopComponent as any; +export let Paginator: t.Paginator = NoopComponent; +export let ScrollerThin: t.ScrollerThin = NoopComponent; +export let Clickable: t.Clickable = NoopComponent; +export let Avatar: t.Avatar = NoopComponent; +export let FocusLock: t.FocusLock = NoopComponent; +export let useToken: t.useToken; export let Icons = {} as t.Icons; -export let Card: t.Card; -export let Button: t.Button; -export let Switch: t.Switch; -export let Tooltip: t.Tooltip; -export let TooltipContainer: t.TooltipContainer; -export let TextInput: t.TextInput; -export let TextArea: t.TextArea; -export let Text: t.Text; -export let Heading: t.Heading; -export let Select: t.Select; -export let SearchableSelect: t.SearchableSelect; -export let Slider: t.Slider; -export let ButtonLooks: t.ButtonLooks; -export let Popout: t.Popout; -export let Dialog: t.Dialog; -export let TabBar: any; -export let Paginator: t.Paginator; -export let ScrollerThin: t.ScrollerThin; -export let Clickable: t.Clickable; -export let Avatar: t.Avatar; -export let FocusLock: t.FocusLock; -// token lagger real -/** css colour resolver stuff, no clue what exactly this does, just copied usage from Discord */ -export let useToken: t.useToken; +export const MaskedLink = findComponentByCode("MASKED_LINK)"); +export const Timestamp = findComponentByCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format"); +export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")) as t.Flex; -export const MaskedLink = waitForComponent("MaskedLink", filters.componentByCode("MASKED_LINK)")); -export const Timestamp = waitForComponent("Timestamp", filters.byCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); -export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap"]); +export const OAuth2AuthorizeModal = findExportedComponent("OAuth2AuthorizeModal"); -export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); - -waitFor(["FormItem", "Button"], m => { +export const Forms = findByProps("FormItem", "Button", m => { ({ useToken, Card, @@ -86,6 +77,7 @@ waitFor(["FormItem", "Button"], m => { FocusLock, Heading } = m); - Forms = m; + Icons = m; + return m; }); diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx deleted file mode 100644 index 8957c254b..000000000 --- a/src/webpack/common/internal.tsx +++ /dev/null @@ -1,44 +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 . -*/ - -import { LazyComponent } from "@utils/react"; - -// eslint-disable-next-line path-alias/no-relative -import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; - -export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]): T { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); - - let myValue: T = function () { - throw new Error(`Vencord could not find the ${name} Component`); - } as any; - - const lazyComponent = LazyComponent(() => myValue) as T; - waitFor(filter, (v: any) => { - myValue = v; - Object.assign(lazyComponent, v); - }, { isIndirect: true }); - - return lazyComponent; -} - -export function waitForStore(name: string, cb: (v: any) => void) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForStore", [name]]); - - waitFor(filters.byStoreName(name), cb, { isIndirect: true }); -} diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index d528390ef..14231f4fe 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -16,17 +16,14 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { filters, mapMangledModuleLazy, waitFor } from "../webpack"; +import { filters, findByProps, mapMangledModule } from "@webpack"; + import type * as t from "./types/menu"; -export let Menu = {} as t.Menu; +export const Menu = findByProps("MenuItem", "MenuSliderControl"); -waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m); - -export const ContextMenuApi: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN', { +export const ContextMenuApi: t.ContextMenuApi = mapMangledModule('type:"CONTEXT_MENU_OPEN', { closeContextMenu: filters.byCode("CONTEXT_MENU_CLOSE"), openContextMenu: filters.byCode("renderLazy:"), - openContextMenuLazy: e => typeof e === "function" && e.toString().length < 100 + openContextMenuLazy: e => typeof e === "function" && String(e).length < 100 }); - diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 17196132d..5e19c4f2f 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -16,10 +16,8 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy, waitFor } from "../webpack"; +import { findByProps } from "@webpack"; -export let React: typeof import("react"); export let useState: typeof React.useState; export let useEffect: typeof React.useEffect; export let useMemo: typeof React.useMemo; @@ -27,9 +25,10 @@ export let useRef: typeof React.useRef; export let useReducer: typeof React.useReducer; export let useCallback: typeof React.useCallback; -export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal", "render"); +export const ReactDOM = findByProps("createPortal", "render"); -waitFor("useState", m => { - React = m; - ({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = React); +export const React = findByProps("useState", (m: typeof import("react")) => { + ({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = m); + + return m; }); diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 8579f8b92..6834d57c8 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,46 +16,37 @@ * along with this program. If not, see . */ -import type * as Stores from "discord-types/stores"; +import { findByCode, findByProps, findStore } from "@webpack"; -// eslint-disable-next-line path-alias/no-relative -import { findByCodeLazy, findByPropsLazy } from "../webpack"; -import { waitForStore } from "./internal"; import * as t from "./types/stores"; -export const Flux: t.Flux = findByPropsLazy("connectStores"); +export const Flux = findByProps("connectStores"); export type GenericStore = t.FluxStore & Record; -export const DraftType = findByPropsLazy("ChannelMessage", "SlashCommand"); +export const DraftType = findByProps("ChannelMessage", "SlashCommand"); -export let MessageStore: Omit & { - getMessages(chanId: string): any; -}; +// This is not actually a FluxStore +export const PrivateChannelsStore = findByProps("openPrivateChannel"); +export const PermissionStore = findStore("PermissionStore"); +export const GuildChannelStore = findStore("GuildChannelStore"); +export const ReadStateStore = findStore("ReadStateStore"); +export const PresenceStore = findStore("PresenceStore"); +export const MessageStore = findStore("MessageStore"); -// this is not actually a FluxStore -export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel"); -export let PermissionStore: GenericStore; -export let GuildChannelStore: GenericStore; -export let ReadStateStore: GenericStore; -export let PresenceStore: GenericStore; +export const GuildStore = findStore("GuildStore"); +export const UserStore = findStore("UserStore"); +export const UserProfileStore = findStore("UserProfileStore"); +export const SelectedChannelStore = findStore("SelectedChannelStore"); +export const SelectedGuildStore = findStore("SelectedGuildStore"); +export const ChannelStore = findStore("ChannelStore"); +export const GuildMemberStore = findStore("GuildMemberStore"); +export const RelationshipStore = findStore("RelationshipStore"); -export let GuildStore: t.GuildStore; -export let UserStore: Stores.UserStore & t.FluxStore; -export let UserProfileStore: GenericStore; -export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore; -export let SelectedGuildStore: t.FluxStore & Record; -export let ChannelStore: Stores.ChannelStore & t.FluxStore; -export let GuildMemberStore: Stores.GuildMemberStore & t.FluxStore; -export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & { - /** Get the date (as a string) that the relationship was created */ - getSince(userId: string): string; -}; - -export let EmojiStore: t.EmojiStore; -export let ThemeStore: t.ThemeStore; -export let WindowStore: t.WindowStore; -export let DraftStore: t.DraftStore; +export const EmojiStore = findStore("EmojiStore"); +export const ThemeStore = findStore("ThemeStore"); +export const WindowStore = findStore("WindowStore"); +export const DraftStore = findStore("DraftStore"); /** * React hook that returns stateful data for one or more stores @@ -67,22 +58,4 @@ export let DraftStore: t.DraftStore; * * @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id); */ -export const useStateFromStores: t.useStateFromStores = findByCodeLazy("useStateFromStores"); - -waitForStore("DraftStore", s => DraftStore = s); -waitForStore("UserStore", s => UserStore = s); -waitForStore("UserProfileStore", m => UserProfileStore = m); -waitForStore("ChannelStore", m => ChannelStore = m); -waitForStore("SelectedChannelStore", m => SelectedChannelStore = m); -waitForStore("SelectedGuildStore", m => SelectedGuildStore = m); -waitForStore("GuildStore", m => GuildStore = m); -waitForStore("GuildMemberStore", m => GuildMemberStore = m); -waitForStore("RelationshipStore", m => RelationshipStore = m); -waitForStore("PermissionStore", m => PermissionStore = m); -waitForStore("PresenceStore", m => PresenceStore = m); -waitForStore("ReadStateStore", m => ReadStateStore = m); -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); +export const useStateFromStores = findByCode("useStateFromStores"); diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index 260a763a7..fa9396718 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -70,6 +70,13 @@ export type FormText = ComponentType & TextProps> & { Types: FormTextTypes; }; +export type Forms = { + FormTitle: t.FormTitle, + FormSection: t.FormSection, + FormDivider: t.FormDivider, + FormText: t.FormText, +}; + export type Tooltip = ComponentType<{ text: ReactNode | ComponentType; children: FunctionComponent<{ @@ -197,7 +204,7 @@ export type Switch = ComponentType>; -export type Timestamp = ComponentType>; +}>; export type TextInput = ComponentType; +export type TabBar = ComponentType> & { + Header: ComponentType>; + Item: ComponentType>; + Separator: ComponentType>; + Panel: ComponentType>; +}; + type Resolve = (data: { theme: "light" | "dark", saturation: number; }) => { hex(): string; hsl(): string; @@ -445,7 +459,7 @@ export type Paginator = ComponentType<{ hideMaxPage?: boolean; }>; -export type MaskedLink = ComponentType>; +}>; export type ScrollerThin = ComponentType; }>>; -export type Icon = ComponentType & { size?: string; colorClass?: string; -} & Record>; +}>; export type Icons = Record; diff --git a/src/webpack/common/types/fluxEvents.d.ts b/src/webpack/common/types/fluxEvents.d.ts index 1bd50f461..730496b94 100644 --- a/src/webpack/common/types/fluxEvents.d.ts +++ b/src/webpack/common/types/fluxEvents.d.ts @@ -32,9 +32,9 @@ function makeFluxEventList() { events.add(event); } - return Array.from(events, e => JSON.stringify(e)).sort().join("|"); + return Array.from(events, e => JSON.stringify(e)).sort().join(" | "); } */ -// 46kb worth of events ??????? -export type FluxEvents = "ACCESSIBILITY_COLORBLIND_TOGGLE" | "ACCESSIBILITY_DARK_SIDEBAR_TOGGLE" | "ACCESSIBILITY_DESATURATE_ROLES_TOGGLE" | "ACCESSIBILITY_FORCED_COLORS_MODAL_SEEN" | "ACCESSIBILITY_KEYBOARD_MODE_DISABLE" | "ACCESSIBILITY_KEYBOARD_MODE_ENABLE" | "ACCESSIBILITY_LOW_CONTRAST_TOGGLE" | "ACCESSIBILITY_RESET_TO_DEFAULT" | "ACCESSIBILITY_SET_ALWAYS_SHOW_LINK_DECORATIONS" | "ACCESSIBILITY_SET_CONTRAST" | "ACCESSIBILITY_SET_FONT_SIZE" | "ACCESSIBILITY_SET_MESSAGE_GROUP_SPACING" | "ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION" | "ACCESSIBILITY_SET_ROLE_STYLE" | "ACCESSIBILITY_SET_SATURATION" | "ACCESSIBILITY_SET_SYNC_FORCED_COLORS" | "ACCESSIBILITY_SET_ZOOM" | "ACCESSIBILITY_SUBMIT_BUTTON_TOGGLE" | "ACCESSIBILITY_SYNC_PROFILE_THEME_WITH_USER_THEME_TOGGLE" | "ACCESSIBILITY_SYSTEM_COLOR_PREFERENCES_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_CONTRAST_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_CROSSFADES_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED" | "ACKNOWLEDGE_CHANNEL_SAFETY_WARNING_TOOLTIP" | "ACK_APPROVED_GUILD_JOIN_REQUEST" | "ACTIVE_BOGO_PROMOTION_FETCH" | "ACTIVE_BOGO_PROMOTION_FETCH_FAIL" | "ACTIVE_BOGO_PROMOTION_FETCH_SUCCESS" | "ACTIVE_CHANNELS_FETCH_FAILURE" | "ACTIVE_CHANNELS_FETCH_START" | "ACTIVE_CHANNELS_FETCH_SUCCESS" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH_FAIL" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH_SUCCESS" | "ACTIVITIES_WHATS_NEW_ACKNOWLEDGE_SECTION" | "ACTIVITIES_WHATS_NEW_MARK_OPENED_SECTION" | "ACTIVITY_INVITE_EDUCATION_DISMISS" | "ACTIVITY_INVITE_MODAL_CLOSE" | "ACTIVITY_INVITE_MODAL_OPEN" | "ACTIVITY_JOIN" | "ACTIVITY_JOIN_FAILED" | "ACTIVITY_JOIN_LOADING" | "ACTIVITY_LAUNCH_FAIL" | "ACTIVITY_LAYOUT_MODE_UPDATE" | "ACTIVITY_METADATA_UPDATE" | "ACTIVITY_PLAY" | "ACTIVITY_SCREEN_ORIENTATION_UPDATE" | "ACTIVITY_START" | "ACTIVITY_SYNC" | "ACTIVITY_SYNC_STOP" | "ACTIVITY_UPDATE_FAIL" | "ACTIVITY_UPDATE_START" | "ACTIVITY_UPDATE_SUCCESS" | "ADD_STICKER_PREVIEW" | "ADMIN_ONBOARDING_GUIDE_HIDE" | "ADYEN_CASH_APP_PAY_SUBMIT_SUCCESS" | "ADYEN_CREATE_CASH_APP_PAY_COMPONENT_SUCCESS" | "ADYEN_CREATE_CLIENT_SUCCESS" | "ADYEN_TEARDOWN_CLIENT" | "AFK" | "AGE_GATE_FAILURE_MODAL_OPEN" | "AGE_GATE_LOGOUT_UNDERAGE_NEW_USER" | "AGE_GATE_MODAL_CLOSE" | "AGE_GATE_MODAL_OPEN" | "AGE_GATE_SUCCESS_MODAL_OPEN" | "APPLICATIONS_FETCH" | "APPLICATIONS_FETCH_FAIL" | "APPLICATIONS_FETCH_SUCCESS" | "APPLICATIONS_SHELF_FETCH_FAIL" | "APPLICATIONS_SHELF_FETCH_START" | "APPLICATIONS_SHELF_FETCH_SUCCESS" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_FAIL" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_START" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "APPLICATION_ASSETS_FETCH" | "APPLICATION_ASSETS_FETCH_SUCCESS" | "APPLICATION_ASSETS_UPDATE" | "APPLICATION_BRANCHES_FETCH_FAIL" | "APPLICATION_BRANCHES_FETCH_SUCCESS" | "APPLICATION_BUILD_FETCH_START" | "APPLICATION_BUILD_FETCH_SUCCESS" | "APPLICATION_BUILD_NOT_FOUND" | "APPLICATION_BUILD_SIZE_FETCH_FAIL" | "APPLICATION_BUILD_SIZE_FETCH_START" | "APPLICATION_BUILD_SIZE_FETCH_SUCCESS" | "APPLICATION_COMMAND_AUTOCOMPLETE_REQUEST" | "APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE" | "APPLICATION_COMMAND_EXECUTE_BAD_VERSION" | "APPLICATION_COMMAND_INDEX_FETCH_FAILURE" | "APPLICATION_COMMAND_INDEX_FETCH_REQUEST" | "APPLICATION_COMMAND_INDEX_FETCH_SUCCESS" | "APPLICATION_COMMAND_SET_ACTIVE_COMMAND" | "APPLICATION_COMMAND_SET_PREFERRED_COMMAND" | "APPLICATION_COMMAND_UPDATE_CHANNEL_STATE" | "APPLICATION_COMMAND_UPDATE_OPTIONS" | "APPLICATION_COMMAND_USED" | "APPLICATION_DIRECTORY_FETCH_APPLICATION" | "APPLICATION_DIRECTORY_FETCH_APPLICATION_FAILURE" | "APPLICATION_DIRECTORY_FETCH_APPLICATION_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_CATEGORIES_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS_FAILURE" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_SEARCH" | "APPLICATION_DIRECTORY_FETCH_SEARCH_FAILURE" | "APPLICATION_DIRECTORY_FETCH_SEARCH_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS_FAILURE" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS_SUCCESS" | "APPLICATION_FETCH" | "APPLICATION_FETCH_FAIL" | "APPLICATION_FETCH_SUCCESS" | "APPLICATION_STORE_ACCEPT_EULA" | "APPLICATION_STORE_ACCEPT_STORE_TERMS" | "APPLICATION_STORE_CLEAR_DATA" | "APPLICATION_STORE_LOCATION_CHANGE" | "APPLICATION_STORE_MATURE_AGREE" | "APPLICATION_STORE_RESET_NAVIGATION" | "APPLICATION_SUBSCRIPTIONS_CHANNEL_NOTICE_DISMISSED" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS_FAILURE" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS_SUCCESS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS_FAILURE" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS_SUCCESS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN_SUCCESS" | "APPLIED_BOOSTS_COOLDOWN_FETCH_SUCCESS" | "APPLIED_GUILD_BOOST_COUNT_UPDATE" | "APP_ICON_EDITOR_CLOSE" | "APP_ICON_EDITOR_OPEN" | "APP_ICON_TRACK_IMPRESSION" | "APP_ICON_UPDATED" | "APP_LAUNCHER_DISMISS_APP_DETAIL" | "APP_LAUNCHER_DISMISS_POPUP" | "APP_LAUNCHER_SET_ACTIVE_COMMAND" | "APP_LAUNCHER_SHOW_APP_DETAIL" | "APP_LAUNCHER_SHOW_POPUP" | "APP_STATE_UPDATE" | "APP_VIEW_SET_HOME_LINK" | "AUDIO_INPUT_DETECTED" | "AUDIO_RESET" | "AUDIO_SET_ATTENUATION" | "AUDIO_SET_AUTOMATIC_GAIN_CONTROL" | "AUDIO_SET_DEBUG_LOGGING" | "AUDIO_SET_DISPLAY_SILENCE_WARNING" | "AUDIO_SET_ECHO_CANCELLATION" | "AUDIO_SET_INPUT_DEVICE" | "AUDIO_SET_INPUT_VOLUME" | "AUDIO_SET_LOCAL_PAN" | "AUDIO_SET_LOCAL_VIDEO_DISABLED" | "AUDIO_SET_LOCAL_VOLUME" | "AUDIO_SET_LOOPBACK" | "AUDIO_SET_MODE" | "AUDIO_SET_NOISE_CANCELLATION" | "AUDIO_SET_NOISE_SUPPRESSION" | "AUDIO_SET_OUTPUT_DEVICE" | "AUDIO_SET_OUTPUT_VOLUME" | "AUDIO_SET_QOS" | "AUDIO_SET_SELF_MUTE" | "AUDIO_SET_SUBSYSTEM" | "AUDIO_SET_TEMPORARY_SELF_MUTE" | "AUDIO_TOGGLE_LOCAL_MUTE" | "AUDIO_TOGGLE_LOCAL_SOUNDBOARD_MUTE" | "AUDIO_TOGGLE_SELF_DEAF" | "AUDIO_TOGGLE_SELF_MUTE" | "AUDIO_VOLUME_CHANGE" | "AUDIT_LOG_FETCH_FAIL" | "AUDIT_LOG_FETCH_NEXT_PAGE_FAIL" | "AUDIT_LOG_FETCH_NEXT_PAGE_START" | "AUDIT_LOG_FETCH_NEXT_PAGE_SUCCESS" | "AUDIT_LOG_FETCH_START" | "AUDIT_LOG_FETCH_SUCCESS" | "AUDIT_LOG_FILTER_BY_ACTION" | "AUDIT_LOG_FILTER_BY_TARGET" | "AUDIT_LOG_FILTER_BY_USER" | "AUTHENTICATOR_CREATE" | "AUTHENTICATOR_DELETE" | "AUTHENTICATOR_UPDATE" | "AUTH_INVITE_UPDATE" | "AUTH_SESSION_CHANGE" | "AUTO_MODERATION_MENTION_RAID_DETECTION" | "AUTO_MODERATION_MENTION_RAID_NOTICE_DISMISS" | "BACKGROUND_SYNC" | "BACKGROUND_SYNC_CHANNEL_MESSAGES" | "BILLING_ANNUAL_USER_OFFER_FETCH_FAIL" | "BILLING_ANNUAL_USER_OFFER_FETCH_SUCCESS" | "BILLING_CREATE_REFERRAL_PREVIEW_FAIL" | "BILLING_CREATE_REFERRAL_PREVIEW_START" | "BILLING_CREATE_REFERRAL_PREVIEW_SUCCESS" | "BILLING_CREATE_REFERRAL_SUCCESS" | "BILLING_IP_COUNTRY_CODE_FAILURE" | "BILLING_IP_COUNTRY_CODE_FETCH_START" | "BILLING_LOCALIZED_PRICING_PROMO_FAILURE" | "BILLING_MOST_RECENT_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_NITRO_AFFINITY_FETCHED" | "BILLING_NITRO_AFFINITY_FETCH_SUCCEEDED" | "BILLING_PAYMENTS_FETCH_SUCCESS" | "BILLING_PAYMENT_FETCH_SUCCESS" | "BILLING_PAYMENT_SOURCES_FETCH_FAIL" | "BILLING_PAYMENT_SOURCES_FETCH_START" | "BILLING_PAYMENT_SOURCES_FETCH_SUCCESS" | "BILLING_PAYMENT_SOURCE_CREATE_FAIL" | "BILLING_PAYMENT_SOURCE_CREATE_START" | "BILLING_PAYMENT_SOURCE_CREATE_SUCCESS" | "BILLING_PAYMENT_SOURCE_REMOVE_CLEAR_ERROR" | "BILLING_PAYMENT_SOURCE_REMOVE_FAIL" | "BILLING_PAYMENT_SOURCE_REMOVE_START" | "BILLING_PAYMENT_SOURCE_REMOVE_SUCCESS" | "BILLING_PAYMENT_SOURCE_UPDATE_CLEAR_ERROR" | "BILLING_PAYMENT_SOURCE_UPDATE_FAIL" | "BILLING_PAYMENT_SOURCE_UPDATE_START" | "BILLING_PAYMENT_SOURCE_UPDATE_SUCCESS" | "BILLING_PERKS_RELEVANCE_FETCH_FAIL" | "BILLING_PERKS_RELEVANCE_FETCH_START" | "BILLING_PERKS_RELEVANCE_FETCH_SUCCESS" | "BILLING_POPUP_BRIDGE_CALLBACK" | "BILLING_POPUP_BRIDGE_STATE_UPDATE" | "BILLING_PREVIOUS_PREMIUM_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_PURCHASE_TOKEN_AUTH_CLEAR_STATE" | "BILLING_REFERRALS_REMAINING_FETCH_FAIL" | "BILLING_REFERRALS_REMAINING_FETCH_START" | "BILLING_REFERRALS_REMAINING_FETCH_SUCCESS" | "BILLING_REFERRAL_RESOLVE_FAIL" | "BILLING_REFERRAL_RESOLVE_SUCCESS" | "BILLING_REFERRAL_TRIAL_OFFER_UPDATE" | "BILLING_SET_IP_COUNTRY_CODE" | "BILLING_SET_LOCALIZED_PRICING_PROMO" | "BILLING_SUBSCRIPTION_CANCEL_FAIL" | "BILLING_SUBSCRIPTION_CANCEL_START" | "BILLING_SUBSCRIPTION_CANCEL_SUCCESS" | "BILLING_SUBSCRIPTION_FETCH_FAIL" | "BILLING_SUBSCRIPTION_FETCH_START" | "BILLING_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_SUBSCRIPTION_RESET" | "BILLING_SUBSCRIPTION_UPDATE_FAIL" | "BILLING_SUBSCRIPTION_UPDATE_START" | "BILLING_SUBSCRIPTION_UPDATE_SUCCESS" | "BILLING_USER_OFFER_ACKNOWLEDGED_SUCCESS" | "BILLING_USER_OFFER_FETCH_FAIL" | "BILLING_USER_OFFER_FETCH_SUCCESS" | "BILLING_USER_PREMIUM_LIKELIHOOD_FETCH" | "BILLING_USER_PREMIUM_LIKELIHOOD_FETCH_ERROR" | "BILLING_USER_PREMIUM_LIKELIHOOD_FETCH_SUCCESS" | "BILLING_USER_TRIAL_OFFER_ACKNOWLEDGED_SUCCESS" | "BILLING_USER_TRIAL_OFFER_FETCH_SUCCESS" | "BLOCKED_DOMAIN_LIST_FETCHED" | "BOOSTED_GUILD_GRACE_PERIOD_NOTICE_DISMISS" | "BRAINTREE_CREATE_CLIENT_SUCCESS" | "BRAINTREE_CREATE_PAYPAL_CLIENT_SUCCESS" | "BRAINTREE_CREATE_VENMO_CLIENT_SUCCESS" | "BRAINTREE_TEARDOWN_PAYPAL_CLIENT" | "BRAINTREE_TEARDOWN_VENMO_CLIENT" | "BRAINTREE_TOKENIZE_PAYPAL_FAIL" | "BRAINTREE_TOKENIZE_PAYPAL_START" | "BRAINTREE_TOKENIZE_PAYPAL_SUCCESS" | "BRAINTREE_TOKENIZE_VENMO_FAIL" | "BRAINTREE_TOKENIZE_VENMO_START" | "BRAINTREE_TOKENIZE_VENMO_SUCCESS" | "BROADCASTER_BUCKETS_RECEIVED" | "BROADCAST_START" | "BROADCAST_STOP" | "BROADCAST_VIEWERS_UPDATE" | "BROWSER_HANDOFF_BEGIN" | "BROWSER_HANDOFF_FROM_APP" | "BROWSER_HANDOFF_SET_USER" | "BROWSER_HANDOFF_UNAVAILABLE" | "BUILD_OVERRIDE_RESOLVED" | "BULK_ACK" | "BULK_CLEAR_RECENTS" | "BURST_REACTION_ANIMATION_ADD" | "BURST_REACTION_EFFECT_CLEAR" | "BURST_REACTION_EFFECT_PLAY" | "BURST_REACTION_PICKER_ANIMATION_ADD" | "BURST_REACTION_PICKER_ANIMATION_CLEAR" | "CACHED_EMOJIS_LOADED" | "CACHED_STICKERS_LOADED" | "CACHE_LOADED" | "CACHE_LOADED_LAZY" | "CACHE_LOADED_LAZY_NO_CACHE" | "CALL_CHAT_TOASTS_SET_ENABLED" | "CALL_CONNECT" | "CALL_CONNECT_MULTIPLE" | "CALL_CREATE" | "CALL_DELETE" | "CALL_ENQUEUE_RING" | "CALL_UPDATE" | "CANDIDATE_GAMES_CHANGE" | "CATEGORY_COLLAPSE" | "CATEGORY_COLLAPSE_ALL" | "CATEGORY_EXPAND" | "CATEGORY_EXPAND_ALL" | "CERTIFIED_DEVICES_SET" | "CHANGE_LOG_FETCH_FAILED" | "CHANGE_LOG_FETCH_SUCCESS" | "CHANGE_LOG_LOCK" | "CHANGE_LOG_MARK_SEEN" | "CHANGE_LOG_SET_CONFIG" | "CHANGE_LOG_SET_OVERRIDE" | "CHANGE_LOG_UNLOCK" | "CHANNEL_ACK" | "CHANNEL_CALL_POPOUT_WINDOW_OPEN" | "CHANNEL_COLLAPSE" | "CHANNEL_CREATE" | "CHANNEL_DELETE" | "CHANNEL_FOLLOWER_CREATED" | "CHANNEL_FOLLOWER_STATS_FETCH_FAILURE" | "CHANNEL_FOLLOWER_STATS_FETCH_SUCCESS" | "CHANNEL_FOLLOWING_PUBLISH_BUMP_DISMISSED" | "CHANNEL_FOLLOWING_PUBLISH_BUMP_HIDE_PERMANENTLY" | "CHANNEL_LOCAL_ACK" | "CHANNEL_MUTE_EXPIRED" | "CHANNEL_PINS_ACK" | "CHANNEL_PINS_UPDATE" | "CHANNEL_PRELOAD" | "CHANNEL_RECIPIENT_ADD" | "CHANNEL_RECIPIENT_REMOVE" | "CHANNEL_RTC_ACTIVE_CHANNELS" | "CHANNEL_RTC_SELECT_PARTICIPANT" | "CHANNEL_RTC_UPDATE_CHAT_OPEN" | "CHANNEL_RTC_UPDATE_LAYOUT" | "CHANNEL_RTC_UPDATE_PARTICIPANTS_OPEN" | "CHANNEL_RTC_UPDATE_STAGE_STREAM_SIZE" | "CHANNEL_RTC_UPDATE_STAGE_VIDEO_LIMIT_BOOST_UPSELL_DISMISSED" | "CHANNEL_RTC_UPDATE_VOICE_PARTICIPANTS_HIDDEN" | "CHANNEL_SAFETY_WARNING_FEEDBACK" | "CHANNEL_SELECT" | "CHANNEL_SETTINGS_CLOSE" | "CHANNEL_SETTINGS_INIT" | "CHANNEL_SETTINGS_LOADED_INVITES" | "CHANNEL_SETTINGS_OPEN" | "CHANNEL_SETTINGS_OVERWRITE_SELECT" | "CHANNEL_SETTINGS_PERMISSIONS_INIT" | "CHANNEL_SETTINGS_PERMISSIONS_SAVE_SUCCESS" | "CHANNEL_SETTINGS_PERMISSIONS_SELECT_PERMISSION" | "CHANNEL_SETTINGS_PERMISSIONS_SET_ADVANCED_MODE" | "CHANNEL_SETTINGS_PERMISSIONS_SUBMITTING" | "CHANNEL_SETTINGS_PERMISSIONS_UPDATE_PERMISSION" | "CHANNEL_SETTINGS_SET_SECTION" | "CHANNEL_SETTINGS_SUBMIT" | "CHANNEL_SETTINGS_SUBMIT_FAILURE" | "CHANNEL_SETTINGS_SUBMIT_SUCCESS" | "CHANNEL_SETTINGS_UPDATE" | "CHANNEL_STATUSES" | "CHANNEL_TOGGLE_MEMBERS_SECTION" | "CHANNEL_TOGGLE_SUMMARIES_SECTION" | "CHANNEL_UPDATES" | "CHECKING_FOR_UPDATES" | "CHECK_LAUNCHABLE_GAME" | "CLAN_SETUP_ERROR" | "CLAN_SETUP_RESET" | "CLAN_SETUP_SUBMIT" | "CLAN_SETUP_SUCCESS" | "CLAN_SETUP_UPDATE" | "CLEAR_AUTHENTICATION_ERRORS" | "CLEAR_CACHES" | "CLEAR_CHANNEL_SAFETY_WARNINGS" | "CLEAR_CONVERSATION_SUMMARIES" | "CLEAR_HANG_STATUS" | "CLEAR_INTERACTION_MODAL_STATE" | "CLEAR_LAST_SESSION_VOICE_CHANNEL_ID" | "CLEAR_MENTIONS" | "CLEAR_MESSAGES" | "CLEAR_OLDEST_UNREAD_MESSAGE" | "CLEAR_PENDING_CHANNEL_AND_ROLE_UPDATES" | "CLEAR_REMOTE_DISCONNECT_VOICE_CHANNEL_ID" | "CLEAR_STICKER_PREVIEW" | "CLIENT_THEMES_EDITOR_CLOSE" | "CLIENT_THEMES_EDITOR_OPEN" | "CLIPS_CLASSIFY_HARDWARE" | "CLIPS_CLEAR_CLIPS_SESSION" | "CLIPS_CLEAR_NEW_CLIP_IDS" | "CLIPS_DELETE_CLIP" | "CLIPS_DISMISS_EDUCATION" | "CLIPS_INIT" | "CLIPS_INIT_FAILURE" | "CLIPS_LOAD_DIRECTORY_SUCCESS" | "CLIPS_RESTART" | "CLIPS_SAVE_ANIMATION_END" | "CLIPS_SAVE_CLIP" | "CLIPS_SAVE_CLIP_ERROR" | "CLIPS_SAVE_CLIP_PLACEHOLDER" | "CLIPS_SAVE_CLIP_PLACEHOLDER_ERROR" | "CLIPS_SAVE_CLIP_START" | "CLIPS_SETTINGS_UPDATE" | "CLIPS_SHOW_CALL_WARNING" | "CLIPS_UPDATE_METADATA" | "CLOSE_SUSPENDED_USER" | "COLLECTIBLES_CATEGORIES_FETCH" | "COLLECTIBLES_CATEGORIES_FETCH_FAILURE" | "COLLECTIBLES_CATEGORIES_FETCH_SUCCESS" | "COLLECTIBLES_CATEGORY_ITEMS_VIEWED" | "COLLECTIBLES_CLAIM" | "COLLECTIBLES_CLAIM_FAILURE" | "COLLECTIBLES_CLAIM_SUCCESS" | "COLLECTIBLES_PRODUCT_DETAILS_OPEN" | "COLLECTIBLES_PRODUCT_FETCH" | "COLLECTIBLES_PRODUCT_FETCH_FAILURE" | "COLLECTIBLES_PRODUCT_FETCH_SUCCESS" | "COLLECTIBLES_PURCHASES_FETCH" | "COLLECTIBLES_PURCHASES_FETCH_FAILURE" | "COLLECTIBLES_PURCHASES_FETCH_SUCCESS" | "COLLECTIBLES_SHOP_CLOSE" | "COLLECTIBLES_SHOP_OPEN" | "COMMANDS_MIGRATION_NOTICE_DISMISSED" | "COMMANDS_MIGRATION_OVERVIEW_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_TOGGLE_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_UPDATE_SUCCESS" | "COMPLETE_NEW_MEMBER_ACTION" | "COMPLETE_SIGN_UP" | "CONNECTED_DEVICE_IGNORE" | "CONNECTED_DEVICE_NEVER_SHOW_MODAL" | "CONNECTED_DEVICE_SET" | "CONNECTIONS_GRID_MODAL_HIDE" | "CONNECTIONS_GRID_MODAL_SHOW" | "CONNECTION_CLOSED" | "CONNECTION_INTERRUPTED" | "CONNECTION_OPEN" | "CONNECTION_OPEN_SUPPLEMENTAL" | "CONNECTION_RESUMED" | "CONSOLE_COMMAND_UPDATE" | "CONTENT_INVENTORY_CLEAR_FEED" | "CONTENT_INVENTORY_DEBUG_CLEAR_IMPRESSIONS" | "CONTENT_INVENTORY_DEBUG_LOG_IMPRESSIONS" | "CONTENT_INVENTORY_DEBUG_TOGGLE_FAST_IMPRESSION_CAPPING" | "CONTENT_INVENTORY_DEBUG_TOGGLE_IMPRESSION_CAPPING" | "CONTENT_INVENTORY_MANUAL_REFRESH" | "CONTENT_INVENTORY_SET_FEED" | "CONTENT_INVENTORY_SET_FILTERS" | "CONTENT_INVENTORY_TOGGLE_FEED_HIDDEN" | "CONTENT_INVENTORY_TOGGLE_REPLY_MODE" | "CONTENT_INVENTORY_TRACK_ITEM_IMPRESSIONS" | "CONTEXT_MENU_CLOSE" | "CONTEXT_MENU_OPEN" | "CONVERSATION_SUMMARY_UPDATE" | "CREATE_PENDING_REPLY" | "CREATE_REFERRALS_SUCCESS" | "CREATE_SHALLOW_PENDING_REPLY" | "CREATOR_MONETIZATION_NAG_ACTIVATE_ELIGIBLITY_FETCH_SUCCESS" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH_FAILURE" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH_SUCCESS" | "CURRENT_BUILD_OVERRIDE_RESOLVED" | "CURRENT_USER_UPDATE" | "DCF_DAILY_CAP_OVERRIDE" | "DCF_HANDLE_DC_DISMISSED" | "DCF_HANDLE_DC_SHOWN" | "DCF_RESET" | "DECAY_READ_STATES" | "DELETED_ENTITY_IDS" | "DELETE_PENDING_REPLY" | "DELETE_SUMMARY" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH_FAILURE" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH_SUCCESS" | "DETECTED_OFF_PLATFORM_PREMIUM_PERKS_DISMISS" | "DEVELOPER_ACTIVITY_SHELF_FETCH_FAIL" | "DEVELOPER_ACTIVITY_SHELF_FETCH_START" | "DEVELOPER_ACTIVITY_SHELF_FETCH_SUCCESS" | "DEVELOPER_ACTIVITY_SHELF_MARK_ACTIVITY_USED" | "DEVELOPER_ACTIVITY_SHELF_SET_ACTIVITY_URL_OVERRIDE" | "DEVELOPER_ACTIVITY_SHELF_TOGGLE_USE_ACTIVITY_URL_OVERRIDE" | "DEVELOPER_ACTIVITY_SHELF_UPDATE_FILTER" | "DEVELOPER_OPTIONS_UPDATE_SETTINGS" | "DEVELOPER_TEST_MODE_AUTHORIZATION_FAIL" | "DEVELOPER_TEST_MODE_AUTHORIZATION_START" | "DEVELOPER_TEST_MODE_AUTHORIZATION_SUCCESS" | "DEVELOPER_TEST_MODE_RESET" | "DEVELOPER_TEST_MODE_RESET_ERROR" | "DEV_TOOLS_DESIGN_TOGGLE_SET" | "DEV_TOOLS_DESIGN_TOGGLE_WEB_SET" | "DEV_TOOLS_DEV_SETTING_SET" | "DEV_TOOLS_SETTINGS_UPDATE" | "DISABLE_AUTOMATIC_ACK" | "DISCOVER_CHECKLIST_FETCH_FAILURE" | "DISCOVER_CHECKLIST_FETCH_START" | "DISCOVER_CHECKLIST_FETCH_SUCCESS" | "DISMISS_CHANNEL_SAFETY_WARNINGS" | "DISMISS_FAVORITE_SUGGESTION" | "DISMISS_MEDIA_POST_SHARE_PROMPT" | "DISMISS_SIGN_UP" | "DISPATCH_APPLICATION_ADD_TO_INSTALLATIONS" | "DISPATCH_APPLICATION_CANCEL" | "DISPATCH_APPLICATION_ERROR" | "DISPATCH_APPLICATION_INSTALL" | "DISPATCH_APPLICATION_INSTALL_SCRIPTS_PROGRESS_UPDATE" | "DISPATCH_APPLICATION_LAUNCH_SETUP_COMPLETE" | "DISPATCH_APPLICATION_LAUNCH_SETUP_START" | "DISPATCH_APPLICATION_MOVE_UP" | "DISPATCH_APPLICATION_REMOVE_FINISHED" | "DISPATCH_APPLICATION_REPAIR" | "DISPATCH_APPLICATION_STATE_UPDATE" | "DISPATCH_APPLICATION_UNINSTALL" | "DISPATCH_APPLICATION_UPDATE" | "DISPLAYED_INVITE_SHOW" | "DOMAIN_MIGRATION_FAILURE" | "DOMAIN_MIGRATION_SKIP" | "DOMAIN_MIGRATION_START" | "DRAFT_CHANGE" | "DRAFT_CLEAR" | "DRAFT_SAVE" | "DRAWER_CLOSE" | "DRAWER_OPEN" | "DRAWER_SELECT_TAB" | "DROPS_ELIGIBILITY_FETCH_SUCCESS" | "DROPS_ENROLLED_USER_FETCH_SUCCESS" | "DROPS_FETCH_PROGRESS_FAILURE" | "DROPS_FETCH_PROGRESS_SUCCESS" | "DROPS_HEARTBEAT_FAILURE" | "DROPS_HEARTBEAT_SUCCESS" | "DROPS_PLATFORM_AVAILABILITY_SUCCESS" | "DROPS_UNENROLL_USER" | "DROPS_USER_STATUS_FETCH_FAILURE" | "DROPS_USER_STATUS_FETCH_SUCCESS" | "EMAIL_SETTINGS_FETCH_SUCCESS" | "EMAIL_SETTINGS_UPDATE" | "EMAIL_SETTINGS_UPDATE_SUCCESS" | "EMBEDDED_ACTIVITY_CLOSE" | "EMBEDDED_ACTIVITY_DEFERRED_OPEN" | "EMBEDDED_ACTIVITY_DISCONNECT" | "EMBEDDED_ACTIVITY_DISMISS_NEW_INDICATOR" | "EMBEDDED_ACTIVITY_FETCH_SHELF" | "EMBEDDED_ACTIVITY_FETCH_SHELF_FAIL" | "EMBEDDED_ACTIVITY_FETCH_SHELF_SUCCESS" | "EMBEDDED_ACTIVITY_LAUNCH_FAIL" | "EMBEDDED_ACTIVITY_LAUNCH_START" | "EMBEDDED_ACTIVITY_LAUNCH_SUCCESS" | "EMBEDDED_ACTIVITY_OPEN" | "EMBEDDED_ACTIVITY_SET_CONFIG" | "EMBEDDED_ACTIVITY_SET_FOCUSED_LAYOUT" | "EMBEDDED_ACTIVITY_SET_ORIENTATION_LOCK_STATE" | "EMBEDDED_ACTIVITY_SET_PANEL_MODE" | "EMBEDDED_ACTIVITY_UPDATE" | "EMBEDDED_ACTIVITY_UPDATE_V2" | "EMOJI_AUTOSUGGESTION_UPDATE" | "EMOJI_CAPTIONS_FETCH" | "EMOJI_CAPTIONS_FETCH_ERROR" | "EMOJI_CAPTIONS_FETCH_SUCCESS" | "EMOJI_INTERACTION_INITIATED" | "EMOJI_TRACK_USAGE" | "ENABLE_AUTOMATIC_ACK" | "ENABLE_GUILD_SIGN_UP" | "ENABLE_USER_SIGN_UP" | "ENTITLEMENTS_FETCH_FOR_USER_FAIL" | "ENTITLEMENTS_FETCH_FOR_USER_START" | "ENTITLEMENTS_FETCH_FOR_USER_SUCCESS" | "ENTITLEMENTS_GIFTABLE_FETCH_SUCCESS" | "ENTITLEMENT_CREATE" | "ENTITLEMENT_DELETE" | "ENTITLEMENT_FETCH_APPLICATION_FAIL" | "ENTITLEMENT_FETCH_APPLICATION_START" | "ENTITLEMENT_FETCH_APPLICATION_SUCCESS" | "ENTITLEMENT_UPDATE" | "EVENT_DIRECTORY_FETCH_FAILURE" | "EVENT_DIRECTORY_FETCH_START" | "EVENT_DIRECTORY_FETCH_SUCCESS" | "EXPERIMENTS_FETCH" | "EXPERIMENTS_FETCH_FAILURE" | "EXPERIMENTS_FETCH_SUCCESS" | "EXPERIMENT_OVERRIDE_BUCKET" | "EXPERIMENT_REGISTER_LEGACY" | "FAMILY_CENTER_FETCH_START" | "FAMILY_CENTER_HANDLE_TAB_SELECT" | "FAMILY_CENTER_INITIAL_LOAD" | "FAMILY_CENTER_LINKED_USERS_FETCH_SUCCESS" | "FAMILY_CENTER_LINK_CODE_FETCH_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_REMOVE_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_UPDATE_SUCCESS" | "FAMILY_CENTER_TEEN_ACTIVITY_FETCH_SUCCESS" | "FAMILY_CENTER_TEEN_ACTIVITY_MORE_FETCH_SUCCESS" | "FETCH_AUTH_SESSIONS_SUCCESS" | "FETCH_CLAN_DISCOVERY_SEARCH_RESULT_SUCCESS" | "FETCH_GUILD_EVENT" | "FETCH_GUILD_EVENTS_FOR_GUILD" | "FETCH_GUILD_MEMBER_SUPPLEMENTAL_SUCCESS" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS_FAILURE" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS_SUCCESS" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_FAIL" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_START" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_SUCCESS" | "FETCH_STATIC_CLAN_LIST_SUCCESS" | "FINGERPRINT" | "FORCE_INVISIBLE" | "FORGOT_PASSWORD_REQUEST" | "FORGOT_PASSWORD_SENT" | "FORUM_SEARCH_CLEAR" | "FORUM_SEARCH_FAILURE" | "FORUM_SEARCH_QUERY_UPDATED" | "FORUM_SEARCH_START" | "FORUM_SEARCH_SUCCESS" | "FORUM_UNREADS" | "FRIENDS_SET_INITIAL_SECTION" | "FRIENDS_SET_SECTION" | "FRIEND_INVITES_FETCH_REQUEST" | "FRIEND_INVITES_FETCH_RESPONSE" | "FRIEND_INVITE_CREATE_FAILURE" | "FRIEND_INVITE_CREATE_REQUEST" | "FRIEND_INVITE_CREATE_SUCCESS" | "FRIEND_INVITE_REVOKE_REQUEST" | "FRIEND_INVITE_REVOKE_SUCCESS" | "FRIEND_SUGGESTION_CREATE" | "FRIEND_SUGGESTION_DELETE" | "GAMES_DATABASE_FETCH" | "GAMES_DATABASE_FETCH_FAIL" | "GAMES_DATABASE_UPDATE" | "GAME_CLOUD_SYNC_COMPLETE" | "GAME_CLOUD_SYNC_CONFLICT" | "GAME_CLOUD_SYNC_ERROR" | "GAME_CLOUD_SYNC_START" | "GAME_CLOUD_SYNC_UPDATE" | "GAME_CONSOLE_FETCH_DEVICES_FAIL" | "GAME_CONSOLE_FETCH_DEVICES_START" | "GAME_CONSOLE_FETCH_DEVICES_SUCCESS" | "GAME_CONSOLE_SELECT_DEVICE" | "GAME_DETECTION_WATCH_CANDIDATE_GAMES_START" | "GAME_ICON_UPDATE" | "GAME_INVITE_CLEAR_UNSEEN" | "GAME_INVITE_CREATE" | "GAME_INVITE_DELETE" | "GAME_INVITE_DELETE_MANY" | "GAME_INVITE_UPDATE_STATUS" | "GAME_LAUNCHABLE_UPDATE" | "GAME_LAUNCH_FAIL" | "GAME_LAUNCH_START" | "GAME_LAUNCH_SUCCESS" | "GENERIC_PUSH_NOTIFICATION_SENT" | "GIFT_CODES_FETCH" | "GIFT_CODES_FETCH_FAILURE" | "GIFT_CODES_FETCH_SUCCESS" | "GIFT_CODE_CREATE" | "GIFT_CODE_CREATE_SUCCESS" | "GIFT_CODE_REDEEM" | "GIFT_CODE_REDEEM_FAILURE" | "GIFT_CODE_REDEEM_SUCCESS" | "GIFT_CODE_RESOLVE" | "GIFT_CODE_RESOLVE_FAILURE" | "GIFT_CODE_RESOLVE_SUCCESS" | "GIFT_CODE_REVOKE_SUCCESS" | "GIFT_CODE_UPDATE" | "GIF_PICKER_INITIALIZE" | "GIF_PICKER_QUERY" | "GIF_PICKER_QUERY_FAILURE" | "GIF_PICKER_QUERY_SUCCESS" | "GIF_PICKER_SUGGESTIONS_SUCCESS" | "GIF_PICKER_TRENDING_FETCH_SUCCESS" | "GIF_PICKER_TRENDING_SEARCH_TERMS_SUCCESS" | "GUILD_ACK" | "GUILD_APPLICATIONS_FETCH_SUCCESS" | "GUILD_APPLICATION_COMMAND_INDEX_UPDATE" | "GUILD_APPLIED_BOOSTS_FETCH_SUCCESS" | "GUILD_APPLY_BOOST_FAIL" | "GUILD_APPLY_BOOST_START" | "GUILD_APPLY_BOOST_SUCCESS" | "GUILD_BAN_ADD" | "GUILD_BAN_REMOVE" | "GUILD_BOOST_SLOTS_FETCH_SUCCESS" | "GUILD_BOOST_SLOT_CREATE" | "GUILD_BOOST_SLOT_UPDATE" | "GUILD_BOOST_SLOT_UPDATE_SUCCESS" | "GUILD_CREATE" | "GUILD_DELETE" | "GUILD_DIRECTORY_ADMIN_ENTRIES_FETCH_SUCCESS" | "GUILD_DIRECTORY_CACHED_SEARCH" | "GUILD_DIRECTORY_CATEGORY_SELECT" | "GUILD_DIRECTORY_COUNTS_FETCH_SUCCESS" | "GUILD_DIRECTORY_ENTRY_CREATE" | "GUILD_DIRECTORY_ENTRY_DELETE" | "GUILD_DIRECTORY_ENTRY_UPDATE" | "GUILD_DIRECTORY_FETCH_FAILURE" | "GUILD_DIRECTORY_FETCH_START" | "GUILD_DIRECTORY_FETCH_SUCCESS" | "GUILD_DIRECTORY_SEARCH_CLEAR" | "GUILD_DIRECTORY_SEARCH_FAILURE" | "GUILD_DIRECTORY_SEARCH_START" | "GUILD_DIRECTORY_SEARCH_SUCCESS" | "GUILD_DISCOVERY_CATEGORY_ADD" | "GUILD_DISCOVERY_CATEGORY_DELETE" | "GUILD_DISCOVERY_CATEGORY_FETCH_SUCCESS" | "GUILD_DISCOVERY_CATEGORY_UPDATE_FAIL" | "GUILD_DISCOVERY_CLEAR_SEARCH" | "GUILD_DISCOVERY_CLEAR_SEEN_GUILDS" | "GUILD_DISCOVERY_FETCH_FAILURE" | "GUILD_DISCOVERY_FETCH_START" | "GUILD_DISCOVERY_FETCH_SUCCESS" | "GUILD_DISCOVERY_GUILD_SEEN" | "GUILD_DISCOVERY_METADATA_FETCH_FAIL" | "GUILD_DISCOVERY_POPULAR_FETCH_FAILURE" | "GUILD_DISCOVERY_POPULAR_FETCH_START" | "GUILD_DISCOVERY_POPULAR_FETCH_SUCCESS" | "GUILD_DISCOVERY_SEARCH_COUNTS_FAIL" | "GUILD_DISCOVERY_SEARCH_FETCH_FAILURE" | "GUILD_DISCOVERY_SEARCH_FETCH_START" | "GUILD_DISCOVERY_SEARCH_FETCH_SUCCESS" | "GUILD_DISCOVERY_SEARCH_INIT" | "GUILD_DISCOVERY_SEARCH_UPDATE_COUNTS" | "GUILD_DISCOVERY_SELECT_CATEGORY" | "GUILD_DISCOVERY_SLUG_FETCH_FAIL" | "GUILD_DISCOVERY_SLUG_FETCH_SUCCESS" | "GUILD_EMOJIS_UPDATE" | "GUILD_FEATURE_ACK" | "GUILD_FEED_FEATURED_ITEMS_FETCH_FAILURE" | "GUILD_FEED_FEATURED_ITEMS_FETCH_SUCCESS" | "GUILD_FEED_FEATURE_ITEM" | "GUILD_FEED_FETCH_FAILURE" | "GUILD_FEED_FETCH_FRESH_START" | "GUILD_FEED_FETCH_PAGE_START" | "GUILD_FEED_FETCH_SUCCESS" | "GUILD_FEED_ITEM_HIDE" | "GUILD_FEED_ITEM_READ_ACK" | "GUILD_FEED_ITEM_REMOVE" | "GUILD_FEED_ITEM_UNHIDE" | "GUILD_FEED_UNFEATURE_ITEM" | "GUILD_FOLDER_COLLAPSE" | "GUILD_FOLDER_CREATE_LOCAL" | "GUILD_FOLDER_DELETE_LOCAL" | "GUILD_FOLDER_EDIT_LOCAL" | "GUILD_GEO_RESTRICTED" | "GUILD_HOME_ENSURE_HOME_SESSION" | "GUILD_HOME_SETTINGS_FETCH_FAIL" | "GUILD_HOME_SETTINGS_FETCH_START" | "GUILD_HOME_SETTINGS_FETCH_SUCCESS" | "GUILD_HOME_SETTINGS_TOGGLE_ENABLED" | "GUILD_HOME_SETTINGS_UPDATE_SUCCESS" | "GUILD_HOME_SET_SCROLL_POSITION" | "GUILD_HOME_SET_SOURCE" | "GUILD_IDENTITY_SETTINGS_CLEAR_ERRORS" | "GUILD_IDENTITY_SETTINGS_CLOSE" | "GUILD_IDENTITY_SETTINGS_INIT" | "GUILD_IDENTITY_SETTINGS_RESET_ALL_PENDING" | "GUILD_IDENTITY_SETTINGS_RESET_AND_CLOSE_FORM" | "GUILD_IDENTITY_SETTINGS_RESET_PENDING_MEMBER_CHANGES" | "GUILD_IDENTITY_SETTINGS_RESET_PENDING_PROFILE_CHANGES" | "GUILD_IDENTITY_SETTINGS_SET_GUILD" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_AVATAR" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_AVATAR_DECORATION" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BANNER" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BIO" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_NICKNAME" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_PROFILE_EFFECT_ID" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_PRONOUNS" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_THEME_COLORS" | "GUILD_IDENTITY_SETTINGS_SUBMIT" | "GUILD_IDENTITY_SETTINGS_SUBMIT_FAILURE" | "GUILD_IDENTITY_SETTINGS_SUBMIT_SUCCESS" | "GUILD_INTEGRATIONS_UPDATE" | "GUILD_JOIN" | "GUILD_JOIN_REQUESTS_BULK_ACTION" | "GUILD_JOIN_REQUESTS_FETCH_FAILURE" | "GUILD_JOIN_REQUESTS_FETCH_START" | "GUILD_JOIN_REQUESTS_FETCH_SUCCESS" | "GUILD_JOIN_REQUESTS_SET_APPLICATION_TAB" | "GUILD_JOIN_REQUESTS_SET_SELECTED" | "GUILD_JOIN_REQUESTS_SET_SORT_ORDER" | "GUILD_JOIN_REQUEST_BY_ID_FETCH_SUCCESS" | "GUILD_JOIN_REQUEST_CREATE" | "GUILD_JOIN_REQUEST_DELETE" | "GUILD_JOIN_REQUEST_UPDATE" | "GUILD_MEMBERS_CHUNK_BATCH" | "GUILD_MEMBERS_REQUEST" | "GUILD_MEMBER_ADD" | "GUILD_MEMBER_LIST_UPDATE" | "GUILD_MEMBER_PROFILE_UPDATE" | "GUILD_MEMBER_REMOVE" | "GUILD_MEMBER_UPDATE" | "GUILD_MEMBER_UPDATE_LOCAL" | "GUILD_MOVE_BY_ID" | "GUILD_MUTE_EXPIRED" | "GUILD_NEW_MEMBER_ACTIONS_DELETE_SUCCESS" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_FAIL" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_START" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_SUCCESS" | "GUILD_NEW_MEMBER_ACTION_UPDATE_SUCCESS" | "GUILD_NSFW_AGREE" | "GUILD_ONBOARDING_COMPLETE" | "GUILD_ONBOARDING_PROMPTS_FETCH_FAILURE" | "GUILD_ONBOARDING_PROMPTS_FETCH_START" | "GUILD_ONBOARDING_PROMPTS_FETCH_SUCCESS" | "GUILD_ONBOARDING_PROMPTS_LOCAL_UPDATE" | "GUILD_ONBOARDING_SELECT_OPTION" | "GUILD_ONBOARDING_SET_STEP" | "GUILD_ONBOARDING_START" | "GUILD_ONBOARDING_UPDATE_RESPONSES_SUCCESS" | "GUILD_POPOUT_FETCH_FAILURE" | "GUILD_POPOUT_FETCH_START" | "GUILD_POPOUT_FETCH_SUCCESS" | "GUILD_PRODUCTS_FETCH" | "GUILD_PRODUCTS_FETCH_FAILURE" | "GUILD_PRODUCTS_FETCH_SUCCESS" | "GUILD_PRODUCT_CREATE" | "GUILD_PRODUCT_DELETE" | "GUILD_PRODUCT_FETCH" | "GUILD_PRODUCT_FETCH_FAILURE" | "GUILD_PRODUCT_FETCH_SUCCESS" | "GUILD_PRODUCT_UPDATE" | "GUILD_PROGRESS_COMPLETED_SEEN" | "GUILD_PROGRESS_DISMISS" | "GUILD_PROGRESS_INITIALIZE" | "GUILD_PROMPT_VIEWED" | "GUILD_RECOMMENDATION_FETCH" | "GUILD_RECOMMENDATION_FETCH_FAILURE" | "GUILD_RECOMMENDATION_FETCH_SUCCESS" | "GUILD_RESOURCE_CHANNEL_UPDATE_SUCCESS" | "GUILD_ROLE_CONNECTION_ELIGIBILITY_FETCH_SUCCESS" | "GUILD_ROLE_CREATE" | "GUILD_ROLE_DELETE" | "GUILD_ROLE_MEMBER_ADD" | "GUILD_ROLE_MEMBER_BULK_ADD" | "GUILD_ROLE_MEMBER_COUNT_FETCH_SUCCESS" | "GUILD_ROLE_MEMBER_COUNT_UPDATE" | "GUILD_ROLE_MEMBER_REMOVE" | "GUILD_ROLE_SUBSCRIPTIONS_CREATE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_DELETE_GROUP_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_DELETE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS_FAILURE" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_ABORTED" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_FAILURE" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_TEMPLATES" | "GUILD_ROLE_SUBSCRIPTIONS_STASH_TEMPLATE_CHANNELS" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_GROUP_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_SUBSCRIPTIONS_SETTINGS" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_SUBSCRIPTION_TRIAL" | "GUILD_ROLE_UPDATE" | "GUILD_SCHEDULED_EVENT_CREATE" | "GUILD_SCHEDULED_EVENT_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTIONS_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_CREATE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_UPDATE" | "GUILD_SCHEDULED_EVENT_RSVPS_FETCH_SUCESS" | "GUILD_SCHEDULED_EVENT_UPDATE" | "GUILD_SCHEDULED_EVENT_USERS_FETCH_SUCCESS" | "GUILD_SCHEDULED_EVENT_USER_ADD" | "GUILD_SCHEDULED_EVENT_USER_COUNTS_FETCH_SUCCESS" | "GUILD_SCHEDULED_EVENT_USER_REMOVE" | "GUILD_SEARCH_RECENT_MEMBERS" | "GUILD_SETTINGS_CANCEL_CHANGES" | "GUILD_SETTINGS_CLOSE" | "GUILD_SETTINGS_DEFAULT_CHANNELS_SAVE_SUCCESS" | "GUILD_SETTINGS_INIT" | "GUILD_SETTINGS_LOADED_BANS" | "GUILD_SETTINGS_LOADED_BANS_BATCH" | "GUILD_SETTINGS_LOADED_INTEGRATIONS" | "GUILD_SETTINGS_LOADED_INVITES" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_SAVE_SUCCESS" | "GUILD_SETTINGS_ONBOARDING_SET_MODE" | "GUILD_SETTINGS_OPEN" | "GUILD_SETTINGS_ROLE_SELECT" | "GUILD_SETTINGS_SAVE_ROUTE_STACK" | "GUILD_SETTINGS_SET_MFA_SUCCESS" | "GUILD_SETTINGS_SET_SEARCH_QUERY" | "GUILD_SETTINGS_SET_SECTION" | "GUILD_SETTINGS_SET_VANITY_URL" | "GUILD_SETTINGS_SET_WIDGET" | "GUILD_SETTINGS_SUBMIT" | "GUILD_SETTINGS_SUBMIT_FAILURE" | "GUILD_SETTINGS_SUBMIT_SUCCESS" | "GUILD_SETTINGS_UPDATE" | "GUILD_SOUNDBOARD_FETCH" | "GUILD_SOUNDBOARD_SOUNDS_UPDATE" | "GUILD_SOUNDBOARD_SOUND_CREATE" | "GUILD_SOUNDBOARD_SOUND_DELETE" | "GUILD_SOUNDBOARD_SOUND_PLAY_END" | "GUILD_SOUNDBOARD_SOUND_PLAY_LOCALLY" | "GUILD_SOUNDBOARD_SOUND_PLAY_START" | "GUILD_SOUNDBOARD_SOUND_UPDATE" | "GUILD_STICKERS_CREATE_SUCCESS" | "GUILD_STICKERS_FETCH_SUCCESS" | "GUILD_STICKERS_UPDATE" | "GUILD_STOP_LURKING" | "GUILD_STOP_LURKING_FAILURE" | "GUILD_SUBSCRIPTIONS" | "GUILD_SUBSCRIPTIONS_ADD_MEMBER_UPDATES" | "GUILD_SUBSCRIPTIONS_CHANNEL" | "GUILD_SUBSCRIPTIONS_FLUSH" | "GUILD_SUBSCRIPTIONS_MEMBERS_ADD" | "GUILD_SUBSCRIPTIONS_MEMBERS_REMOVE" | "GUILD_SUBSCRIPTIONS_REMOVE_MEMBER_UPDATES" | "GUILD_TEMPLATE_ACCEPT" | "GUILD_TEMPLATE_ACCEPT_FAILURE" | "GUILD_TEMPLATE_ACCEPT_SUCCESS" | "GUILD_TEMPLATE_CREATE_SUCCESS" | "GUILD_TEMPLATE_DELETE_SUCCESS" | "GUILD_TEMPLATE_DIRTY_TOOLTIP_HIDE" | "GUILD_TEMPLATE_DIRTY_TOOLTIP_REFRESH" | "GUILD_TEMPLATE_LOAD_FOR_GUILD_SUCCESS" | "GUILD_TEMPLATE_MODAL_HIDE" | "GUILD_TEMPLATE_MODAL_SHOW" | "GUILD_TEMPLATE_PROMOTION_TOOLTIP_HIDE" | "GUILD_TEMPLATE_RESOLVE" | "GUILD_TEMPLATE_RESOLVE_FAILURE" | "GUILD_TEMPLATE_RESOLVE_SUCCESS" | "GUILD_TEMPLATE_SYNC_SUCCESS" | "GUILD_TOGGLE_COLLAPSE_MUTED" | "GUILD_UNAPPLY_BOOST_FAIL" | "GUILD_UNAPPLY_BOOST_START" | "GUILD_UNAPPLY_BOOST_SUCCESS" | "GUILD_UNAVAILABLE" | "GUILD_UPDATE" | "GUILD_UPDATE_DISCOVERY_METADATA" | "GUILD_UPDATE_DISCOVERY_METADATA_FAIL" | "GUILD_UPDATE_DISCOVERY_METADATA_FROM_SERVER" | "GUILD_VERIFICATION_CHECK" | "HABITUAL_DND_CLEAR" | "HIDE_ACTION_SHEET" | "HIDE_ACTION_SHEET_QUICK_SWITCHER" | "HIDE_KEYBOARD_SHORTCUTS" | "HIGH_FIVE_COMPLETE" | "HIGH_FIVE_COMPLETE_CLEAR" | "HIGH_FIVE_QUEUE" | "HIGH_FIVE_REMOVE" | "HIGH_FIVE_SET_ENABLED" | "HOTSPOT_HIDE" | "HOTSPOT_OVERRIDE_CLEAR" | "HOTSPOT_OVERRIDE_SET" | "HYPESQUAD_ONLINE_MEMBERSHIP_JOIN_SUCCESS" | "HYPESQUAD_ONLINE_MEMBERSHIP_LEAVE_SUCCESS" | "I18N_LOAD_ERROR" | "I18N_LOAD_START" | "I18N_LOAD_SUCCESS" | "IDLE" | "IMPERSONATE_STOP" | "IMPERSONATE_UPDATE" | "INBOX_OPEN" | "INCOMING_CALL_MOVE" | "INITIALIZE_MEMBER_SAFETY_STORE" | "INSTALLATION_LOCATION_ADD" | "INSTALLATION_LOCATION_FETCH_METADATA" | "INSTALLATION_LOCATION_REMOVE" | "INSTALLATION_LOCATION_UPDATE" | "INSTANT_INVITE_CLEAR" | "INSTANT_INVITE_CREATE" | "INSTANT_INVITE_CREATE_FAILURE" | "INSTANT_INVITE_CREATE_SUCCESS" | "INSTANT_INVITE_REVOKE_SUCCESS" | "INTEGRATION_CREATE" | "INTEGRATION_DELETE" | "INTEGRATION_QUERY" | "INTEGRATION_QUERY_FAILURE" | "INTEGRATION_QUERY_SUCCESS" | "INTEGRATION_SETTINGS_INIT" | "INTEGRATION_SETTINGS_SAVE_FAILURE" | "INTEGRATION_SETTINGS_SAVE_SUCCESS" | "INTEGRATION_SETTINGS_SET_SECTION" | "INTEGRATION_SETTINGS_START_EDITING_WEBHOOK" | "INTEGRATION_SETTINGS_STOP_EDITING_WEBHOOK" | "INTEGRATION_SETTINGS_SUBMITTING" | "INTEGRATION_SETTINGS_UPDATE_WEBHOOK" | "INTERACTION_CREATE" | "INTERACTION_FAILURE" | "INTERACTION_IFRAME_MODAL_CLOSE" | "INTERACTION_IFRAME_MODAL_CREATE" | "INTERACTION_IFRAME_MODAL_KEY_CREATE" | "INTERACTION_MODAL_CREATE" | "INTERACTION_QUEUE" | "INTERACTION_SUCCESS" | "INVITE_ACCEPT" | "INVITE_ACCEPT_FAILURE" | "INVITE_ACCEPT_SUCCESS" | "INVITE_APP_NOT_OPENED" | "INVITE_APP_OPENED" | "INVITE_APP_OPENING" | "INVITE_MODAL_CLOSE" | "INVITE_MODAL_ERROR" | "INVITE_MODAL_OPEN" | "INVITE_RESOLVE" | "INVITE_RESOLVE_FAILURE" | "INVITE_RESOLVE_SUCCESS" | "KEYBINDS_ADD_KEYBIND" | "KEYBINDS_DELETE_KEYBIND" | "KEYBINDS_ENABLE_ALL_KEYBINDS" | "KEYBINDS_REGISTER_GLOBAL_KEYBIND_ACTIONS" | "KEYBINDS_SET_KEYBIND" | "KEYBOARD_NAVIGATION_EXPLAINER_MODAL_SEEN" | "LAYER_POP" | "LAYER_POP_ALL" | "LAYER_PUSH" | "LAYOUT_CREATE" | "LAYOUT_CREATE_WIDGETS" | "LAYOUT_DELETE_ALL_WIDGETS" | "LAYOUT_DELETE_WIDGET" | "LAYOUT_SET_PINNED" | "LAYOUT_SET_TOP_WIDGET" | "LAYOUT_UPDATE_WIDGET" | "LIBRARY_APPLICATIONS_TEST_MODE_ENABLED" | "LIBRARY_APPLICATION_ACTIVE_BRANCH_UPDATE" | "LIBRARY_APPLICATION_ACTIVE_LAUNCH_OPTION_UPDATE" | "LIBRARY_APPLICATION_FILTER_UPDATE" | "LIBRARY_APPLICATION_FLAGS_UPDATE_START" | "LIBRARY_APPLICATION_FLAGS_UPDATE_SUCCESS" | "LIBRARY_APPLICATION_UPDATE" | "LIBRARY_FETCH_SUCCESS" | "LIBRARY_TABLE_ACTIVE_ROW_ID_UPDATE" | "LIBRARY_TABLE_SORT_UPDATE" | "LIGHTNING_CHECKOUT_CLOSE" | "LIGHTNING_CHECKOUT_OPEN" | "LIVE_CHANNEL_NOTICE_HIDE" | "LOAD_ARCHIVED_THREADS" | "LOAD_ARCHIVED_THREADS_FAIL" | "LOAD_ARCHIVED_THREADS_SUCCESS" | "LOAD_CHANNELS" | "LOAD_FORUM_POSTS" | "LOAD_FRIEND_SUGGESTIONS_FAILURE" | "LOAD_FRIEND_SUGGESTIONS_SUCCESS" | "LOAD_GUILD_AFFINITIES_SUCCESS" | "LOAD_MESSAGES" | "LOAD_MESSAGES_AROUND_SUCCESS" | "LOAD_MESSAGES_FAILURE" | "LOAD_MESSAGES_SUCCESS" | "LOAD_MESSAGES_SUCCESS_CACHED" | "LOAD_MESSAGE_INTERACTION_DATA_SUCCESS" | "LOAD_MESSAGE_REQUESTS_SUPPLEMENTAL_DATA_ERROR" | "LOAD_MESSAGE_REQUESTS_SUPPLEMENTAL_DATA_SUCCESS" | "LOAD_NOTIFICATION_CENTER_ITEMS" | "LOAD_NOTIFICATION_CENTER_ITEMS_FAILURE" | "LOAD_NOTIFICATION_CENTER_ITEMS_SUCCESS" | "LOAD_PINNED_MESSAGES" | "LOAD_PINNED_MESSAGES_FAILURE" | "LOAD_PINNED_MESSAGES_SUCCESS" | "LOAD_RECENT_MENTIONS" | "LOAD_RECENT_MENTIONS_FAILURE" | "LOAD_RECENT_MENTIONS_SUCCESS" | "LOAD_REGIONS" | "LOAD_RELATIONSHIPS_FAILURE" | "LOAD_RELATIONSHIPS_SUCCESS" | "LOAD_THREADS_SUCCESS" | "LOAD_USER_AFFINITIES" | "LOAD_USER_AFFINITIES_FAILURE" | "LOAD_USER_AFFINITIES_SUCCESS" | "LOCAL_ACTIVITY_UPDATE" | "LOCAL_MESSAGES_LOADED" | "LOCAL_MESSAGE_CREATE" | "LOGIN" | "LOGIN_ACCOUNT_DISABLED" | "LOGIN_ACCOUNT_SCHEDULED_FOR_DELETION" | "LOGIN_ATTEMPTED" | "LOGIN_FAILURE" | "LOGIN_MFA" | "LOGIN_MFA_FAILURE" | "LOGIN_MFA_SMS" | "LOGIN_MFA_SMS_FAILURE" | "LOGIN_MFA_SMS_REQUEST_SUCCESS" | "LOGIN_MFA_STEP" | "LOGIN_PASSWORD_RECOVERY_PHONE_VERIFICATION" | "LOGIN_PHONE_IP_AUTHORIZATION_REQUIRED" | "LOGIN_RESET" | "LOGIN_STATUS_RESET" | "LOGIN_SUCCESS" | "LOGIN_SUSPENDED_USER" | "LOGOUT" | "LOGOUT_AUTH_SESSIONS_SUCCESS" | "MASKED_LINK_ADD_TRUSTED_DOMAIN" | "MASKED_LINK_ADD_TRUSTED_PROTOCOL" | "MAX_MEMBER_COUNT_NOTICE_DISMISS" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS_ERROR" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS_START" | "MEDIA_ENGINE_DEVICES" | "MEDIA_ENGINE_INTERACTION_REQUIRED" | "MEDIA_ENGINE_NOISE_CANCELLATION_ERROR_RESET" | "MEDIA_ENGINE_PERMISSION" | "MEDIA_ENGINE_SET_AEC_DUMP" | "MEDIA_ENGINE_SET_AUDIO_ENABLED" | "MEDIA_ENGINE_SET_EXPERIMENTAL_ENCODERS" | "MEDIA_ENGINE_SET_EXPERIMENTAL_SOUNDSHARE" | "MEDIA_ENGINE_SET_GO_LIVE_SOURCE" | "MEDIA_ENGINE_SET_HARDWARE_H264" | "MEDIA_ENGINE_SET_OPEN_H264" | "MEDIA_ENGINE_SET_VIDEO_DEVICE" | "MEDIA_ENGINE_SET_VIDEO_ENABLED" | "MEDIA_ENGINE_SET_VIDEO_HOOK" | "MEDIA_ENGINE_SOUNDSHARE_FAILED" | "MEDIA_ENGINE_SOUNDSHARE_TRANSMITTING" | "MEDIA_ENGINE_VIDEO_SOURCE_QUALITY_CHANGED" | "MEDIA_ENGINE_VIDEO_STATE_CHANGED" | "MEDIA_POST_EMBED_FETCH" | "MEDIA_POST_EMBED_FETCH_FAILURE" | "MEDIA_POST_EMBED_FETCH_SUCCESS" | "MEDIA_SESSION_JOINED" | "MEMBER_SAFETY_GUILD_MEMBER_SEARCH_SUCCESS" | "MEMBER_SAFETY_GUILD_MEMBER_UPDATE_BATCH" | "MEMBER_SAFETY_NEW_MEMBER_TIMESTAMP_REFRESH" | "MEMBER_SAFETY_PAGINATION_TOKEN_UPDATE" | "MEMBER_SAFETY_PAGINATION_UPDATE" | "MEMBER_SAFETY_SEARCH_STATE_UPDATE" | "MEMBER_VERIFICATION_FORM_FETCH_FAIL" | "MEMBER_VERIFICATION_FORM_UPDATE" | "MENTION_MODAL_CLOSE" | "MENTION_MODAL_OPEN" | "MESSAGE_ACK" | "MESSAGE_ACKED" | "MESSAGE_CREATE" | "MESSAGE_DELETE" | "MESSAGE_DELETE_BULK" | "MESSAGE_EDIT_FAILED_AUTOMOD" | "MESSAGE_END_EDIT" | "MESSAGE_EXPLICIT_CONTENT_FP_CREATE" | "MESSAGE_EXPLICIT_CONTENT_FP_SUBMIT" | "MESSAGE_EXPLICIT_CONTENT_SCAN_TIMEOUT" | "MESSAGE_LENGTH_UPSELL" | "MESSAGE_NOTIFICATION_SHOWN" | "MESSAGE_PREVIEWS_LOADED" | "MESSAGE_PREVIEWS_LOCALLY_LOADED" | "MESSAGE_REACTION_ADD" | "MESSAGE_REACTION_ADD_MANY" | "MESSAGE_REACTION_ADD_USERS" | "MESSAGE_REACTION_REMOVE" | "MESSAGE_REACTION_REMOVE_ALL" | "MESSAGE_REACTION_REMOVE_EMOJI" | "MESSAGE_REMINDER_NOTIFIED" | "MESSAGE_REMINDER_TOGGLE" | "MESSAGE_REQUEST_ACCEPT_OPTIMISTIC" | "MESSAGE_REVEAL" | "MESSAGE_SEND_FAILED" | "MESSAGE_SEND_FAILED_AUTOMOD" | "MESSAGE_START_EDIT" | "MESSAGE_UPDATE" | "MESSAGE_UPDATE_EDIT" | "MFA_CLEAR_BACKUP_CODES" | "MFA_DISABLE_SUCCESS" | "MFA_ENABLE_EMAIL_TOKEN" | "MFA_ENABLE_SUCCESS" | "MFA_SEEN_BACKUP_CODE_PROMPT" | "MFA_SEND_VERIFICATION_KEY" | "MFA_SMS_TOGGLE" | "MFA_SMS_TOGGLE_COMPLETE" | "MFA_VIEW_BACKUP_CODES" | "MFA_WEBAUTHN_CREDENTIALS_LOADED" | "MFA_WEBAUTHN_CREDENTIALS_LOADING" | "MOBILE_NATIVE_UPDATE_CHECK_FINISHED" | "MOBILE_WEB_SIDEBAR_CLOSE" | "MOBILE_WEB_SIDEBAR_OPEN" | "MODAL_POP" | "MODAL_PUSH" | "MOD_VIEW_SEARCH_FINISH" | "MULTI_ACCOUNT_INVALIDATE_PUSH_SYNC_TOKENS" | "MULTI_ACCOUNT_MOBILE_EXPERIMENT_UPDATE" | "MULTI_ACCOUNT_MOVE_ACCOUNT" | "MULTI_ACCOUNT_REMOVE_ACCOUNT" | "MULTI_ACCOUNT_UPDATE_PUSH_SYNC_TOKEN" | "MULTI_ACCOUNT_VALIDATE_TOKEN_FAILURE" | "MULTI_ACCOUNT_VALIDATE_TOKEN_REQUEST" | "MULTI_ACCOUNT_VALIDATE_TOKEN_SUCCESS" | "MUTUAL_FRIENDS_FETCH_FAILURE" | "MUTUAL_FRIENDS_FETCH_START" | "MUTUAL_FRIENDS_FETCH_SUCCESS" | "NEWLY_ADDED_EMOJI_SEEN_ACKNOWLEDGED" | "NEWLY_ADDED_EMOJI_SEEN_PENDING" | "NEWLY_ADDED_EMOJI_SEEN_UPDATED" | "NEW_PAYMENT_SOURCE_ADDRESS_INFO_UPDATE" | "NEW_PAYMENT_SOURCE_CARD_INFO_UPDATE" | "NEW_PAYMENT_SOURCE_CLEAR_ERROR" | "NEW_PAYMENT_SOURCE_STRIPE_PAYMENT_REQUEST_UPDATE" | "NOTICE_DISABLE" | "NOTICE_DISMISS" | "NOTICE_SHOW" | "NOTIFICATIONS_SET_DESKTOP_TYPE" | "NOTIFICATIONS_SET_DISABLED_SOUNDS" | "NOTIFICATIONS_SET_DISABLE_UNREAD_BADGE" | "NOTIFICATIONS_SET_NOTIFY_MESSAGES_IN_SELECTED_CHANNEL" | "NOTIFICATIONS_SET_PERMISSION_STATE" | "NOTIFICATIONS_SET_TASKBAR_FLASH" | "NOTIFICATIONS_SET_TTS_TYPE" | "NOTIFICATIONS_TOGGLE_ALL_DISABLED" | "NOTIFICATION_CENTER_CLEAR_GUILD_MENTIONS" | "NOTIFICATION_CENTER_ITEMS_ACK" | "NOTIFICATION_CENTER_ITEMS_ACK_FAILURE" | "NOTIFICATION_CENTER_ITEMS_LOCAL_ACK" | "NOTIFICATION_CENTER_ITEM_COMPLETED" | "NOTIFICATION_CENTER_ITEM_CREATE" | "NOTIFICATION_CENTER_ITEM_DELETE" | "NOTIFICATION_CENTER_ITEM_DELETE_FAILURE" | "NOTIFICATION_CENTER_REFRESH" | "NOTIFICATION_CENTER_SET_ACTIVE" | "NOTIFICATION_CENTER_SET_TAB" | "NOTIFICATION_CENTER_TAB_FOCUSED" | "NOTIFICATION_CLICK" | "NOTIFICATION_CREATE" | "NOTIFICATION_SETTINGS_UPDATE" | "NOW_PLAYING_MOUNTED" | "NOW_PLAYING_UNMOUNTED" | "NUF_COMPLETE" | "NUF_NEW_USER" | "OAUTH2_TOKEN_REVOKE" | "ONLINE_GUILD_MEMBER_COUNT_UPDATE" | "OUTBOUND_PROMOTIONS_SEEN" | "OUTBOUND_PROMOTION_NOTICE_DISMISS" | "OVERLAY_ACTIVATE_REGION" | "OVERLAY_CALL_PRIVATE_CHANNEL" | "OVERLAY_CRASHED" | "OVERLAY_DEACTIVATE_ALL_REGIONS" | "OVERLAY_DISABLE_EXTERNAL_LINK_ALERT" | "OVERLAY_FOCUSED" | "OVERLAY_INCOMPATIBLE_APP" | "OVERLAY_INITIALIZE" | "OVERLAY_JOIN_GAME" | "OVERLAY_MESSAGE_EVENT_ACTION" | "OVERLAY_NOTIFICATION_EVENT" | "OVERLAY_NOTIFY_READY_TO_SHOW" | "OVERLAY_READY" | "OVERLAY_SELECT_CALL" | "OVERLAY_SELECT_CHANNEL" | "OVERLAY_SET_ASSOCIATED_GAME" | "OVERLAY_SET_AVATAR_SIZE_MODE" | "OVERLAY_SET_CLICK_ZONES" | "OVERLAY_SET_DISPLAY_NAME_MODE" | "OVERLAY_SET_DISPLAY_USER_MODE" | "OVERLAY_SET_ENABLED" | "OVERLAY_SET_INPUT_LOCKED" | "OVERLAY_SET_NOTIFICATION_POSITION_MODE" | "OVERLAY_SET_NOT_IDLE" | "OVERLAY_SET_PREVIEW_IN_GAME_MODE" | "OVERLAY_SET_SHOW_KEYBIND_INDICATORS" | "OVERLAY_SET_TEXT_CHAT_NOTIFICATION_MODE" | "OVERLAY_SET_TEXT_WIDGET_OPACITY" | "OVERLAY_SET_UI_LOCKED" | "OVERLAY_SOUNDBOARD_SOUNDS_FETCH_REQUEST" | "OVERLAY_START_SESSION" | "OVERLAY_SUCCESSFULLY_SHOWN" | "OVERLAY_WIDGET_CHANGED" | "PASSIVE_UPDATE_V1" | "PASSWORD_UPDATED" | "PAYMENT_AUTHENTICATION_CLEAR_ERROR" | "PAYMENT_AUTHENTICATION_ERROR" | "PAYMENT_UPDATE" | "PERMISSION_CLEAR_ELEVATED_PROCESS" | "PERMISSION_CLEAR_PTT_ADMIN_WARNING" | "PERMISSION_CLEAR_SUPPRESS_WARNING" | "PERMISSION_CLEAR_VAD_WARNING" | "PERMISSION_CONTINUE_NONELEVATED_PROCESS" | "PERMISSION_REQUEST_ELEVATED_PROCESS" | "PHONE_SET_COUNTRY_CODE" | "PICTURE_IN_PICTURE_CLOSE" | "PICTURE_IN_PICTURE_HIDE" | "PICTURE_IN_PICTURE_MOVE" | "PICTURE_IN_PICTURE_OPEN" | "PICTURE_IN_PICTURE_SHOW" | "PICTURE_IN_PICTURE_UPDATE_RECT" | "PICTURE_IN_PICTURE_UPDATE_SELECTED_WINDOW" | "POGGERMODE_ACHIEVEMENT_UNLOCK" | "POGGERMODE_SETTINGS_UPDATE" | "POGGERMODE_TEMPORARILY_DISABLED" | "POGGERMODE_UPDATE_COMBO" | "POGGERMODE_UPDATE_MESSAGE_COMBO" | "POPOUT_WINDOW_CLOSE" | "POPOUT_WINDOW_OPEN" | "POPOUT_WINDOW_SET_ALWAYS_ON_TOP" | "POST_CONNECTION_OPEN" | "PREMIUM_MARKETING_DATA_READY" | "PREMIUM_MARKETING_PREVIEW" | "PREMIUM_PAYMENT_ERROR_CLEAR" | "PREMIUM_PAYMENT_MODAL_CLOSE" | "PREMIUM_PAYMENT_MODAL_OPEN" | "PREMIUM_PAYMENT_SUBSCRIBE_FAIL" | "PREMIUM_PAYMENT_SUBSCRIBE_START" | "PREMIUM_PAYMENT_SUBSCRIBE_SUCCESS" | "PREMIUM_PAYMENT_UPDATE_FAIL" | "PREMIUM_PAYMENT_UPDATE_SUCCESS" | "PREMIUM_PERKS_DEMOS_FETCH_FAILURE" | "PREMIUM_PERKS_DEMOS_FETCH_SUCCESS" | "PREMIUM_PERKS_DEMO_ACTIVATE_FAILURE" | "PREMIUM_PERKS_DEMO_ACTIVATE_SUCCESS" | "PREMIUM_PERKS_DEMO_COMPLETE" | "PREMIUM_PERKS_DEMO_OVERRIDE" | "PREMIUM_REQUIRED_MODAL_CLOSE" | "PREMIUM_REQUIRED_MODAL_OPEN" | "PRESENCES_REPLACE" | "PRESENCE_UPDATES" | "PRIVATE_CHANNEL_INTEGRATION_CREATE" | "PRIVATE_CHANNEL_INTEGRATION_DELETE" | "PRIVATE_CHANNEL_INTEGRATION_UPDATE" | "PRIVATE_CHANNEL_RECIPIENTS_ADD_USER" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_CLOSE" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_OPEN" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_QUERY" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_SELECT" | "PRIVATE_CHANNEL_RECIPIENTS_REMOVE_USER" | "PROFILE_CUSTOMIZATION_OPEN_PREVIEW_MODAL" | "PROFILE_EFFECTS_SET_TRY_IT_OUT" | "PROFILE_PANEL_TOGGLE_SECTION" | "PROXY_BLOCKED_REQUEST" | "PUBLIC_UPSELL_NOTICE_DISMISS" | "PURCHASED_ITEMS_FESTIVITY_FETCH_WOW_MOMENT_MEDIA_SUCCESS" | "PURCHASED_ITEMS_FESTIVITY_IS_FETCHING_WOW_MOMENT_MEDIA" | "PURCHASED_ITEMS_FESTIVITY_SET_CAN_PLAY_WOW_MOMENT" | "PURCHASE_CONFIRMATION_MODAL_CLOSE" | "PURCHASE_CONFIRMATION_MODAL_OPEN" | "PUSH_NOTIFICATION_CLICK" | "QUESTS_CLAIM_REWARD_BEGIN" | "QUESTS_CLAIM_REWARD_CODE_BEGIN" | "QUESTS_CLAIM_REWARD_CODE_FAILURE" | "QUESTS_CLAIM_REWARD_CODE_SUCCESS" | "QUESTS_CLAIM_REWARD_FAILURE" | "QUESTS_CLAIM_REWARD_SUCCESS" | "QUESTS_DELIVERY_OVERRIDE" | "QUESTS_DISMISS_CONTENT_BEGIN" | "QUESTS_DISMISS_CONTENT_FAILURE" | "QUESTS_DISMISS_CONTENT_SUCCESS" | "QUESTS_DISMISS_PROGRESS_TRACKING_FAILURE_NOTICE" | "QUESTS_ENROLL_BEGIN" | "QUESTS_ENROLL_FAILURE" | "QUESTS_ENROLL_SUCCESS" | "QUESTS_FETCH_CURRENT_QUESTS_BEGIN" | "QUESTS_FETCH_CURRENT_QUESTS_FAILURE" | "QUESTS_FETCH_CURRENT_QUESTS_SUCCESS" | "QUESTS_FETCH_REWARD_CODE_BEGIN" | "QUESTS_FETCH_REWARD_CODE_FAILURE" | "QUESTS_FETCH_REWARD_CODE_SUCCESS" | "QUESTS_OPTIMISTIC_PROGRESS_UPDATE" | "QUESTS_PREVIEW_UPDATE_SUCCESS" | "QUESTS_SEND_HEARTBEAT_FAILURE" | "QUESTS_SEND_HEARTBEAT_SUCCESS" | "QUEUE_INTERACTION_COMPONENT_STATE" | "QUICKSWITCHER_HIDE" | "QUICKSWITCHER_SEARCH" | "QUICKSWITCHER_SELECT" | "QUICKSWITCHER_SHOW" | "QUICKSWITCHER_SWITCH_TO" | "RECEIVE_CHANNEL_AFFINITIES" | "RECEIVE_CHANNEL_SUMMARIES" | "RECEIVE_CHANNEL_SUMMARIES_BULK" | "RECEIVE_CHANNEL_SUMMARY" | "RECENT_MENTION_DELETE" | "RECOMPUTE_READ_STATES" | "REFERRALS_FETCH_ELIGIBLE_USER_FAIL" | "REFERRALS_FETCH_ELIGIBLE_USER_START" | "REFERRALS_FETCH_ELIGIBLE_USER_SUCCESS" | "REGISTER" | "REGISTER_FAILURE" | "REGISTER_SAVE_FORM" | "REGISTER_SUCCESS" | "RELATIONSHIP_ADD" | "RELATIONSHIP_PENDING_INCOMING_REMOVED" | "RELATIONSHIP_REMOVE" | "RELATIONSHIP_UPDATE" | "REMOTE_COMMAND" | "REMOTE_SESSION_CONNECT" | "REMOTE_SESSION_DISCONNECT" | "REMOVE_AUTOMOD_MESSAGE_NOTICE" | "REQUEST_CHANNEL_AFFINITIES" | "REQUEST_CHANNEL_SUMMARIES" | "REQUEST_CHANNEL_SUMMARIES_BULK" | "REQUEST_CHANNEL_SUMMARY" | "REQUEST_FORUM_UNREADS" | "REQUEST_SOUNDBOARD_SOUNDS" | "RESET_NOTIFICATION_CENTER" | "RESET_PAYMENT_ID" | "RESET_PREVIEW_CLIENT_THEME" | "RESET_SOCKET" | "RESORT_THREADS" | "RPC_APP_AUTHENTICATED" | "RPC_APP_CONNECTED" | "RPC_APP_DISCONNECTED" | "RPC_NOTIFICATION_CREATE" | "RPC_SERVER_READY" | "RTC_CONNECTION_FLAGS" | "RTC_CONNECTION_LOSS_RATE" | "RTC_CONNECTION_PING" | "RTC_CONNECTION_PLATFORM" | "RTC_CONNECTION_STATE" | "RTC_CONNECTION_UPDATE_ID" | "RTC_CONNECTION_USER_CREATE" | "RTC_CONNECTION_VIDEO" | "RTC_DEBUG_MODAL_CLOSE" | "RTC_DEBUG_MODAL_OPEN" | "RTC_DEBUG_MODAL_OPEN_REPLAY" | "RTC_DEBUG_MODAL_OPEN_REPLAY_AT_PATH" | "RTC_DEBUG_MODAL_SET_SECTION" | "RTC_DEBUG_MODAL_UPDATE" | "RTC_DEBUG_MODAL_UPDATE_VIDEO_OUTPUT" | "RTC_DEBUG_POPOUT_WINDOW_OPEN" | "RTC_DEBUG_SET_RECORDING_FLAG" | "RTC_LATENCY_TEST_COMPLETE" | "RTC_SPEED_TEST_START_TEST" | "RTC_SPEED_TEST_STOP_TEST" | "RUNNING_GAMES_CHANGE" | "RUNNING_GAME_ADD_OVERRIDE" | "RUNNING_GAME_DELETE_ENTRY" | "RUNNING_GAME_EDIT_NAME" | "RUNNING_GAME_TOGGLE_DETECTION" | "RUNNING_GAME_TOGGLE_OVERLAY" | "RUNNING_STREAMER_TOOLS_CHANGE" | "SAFETY_HUB_APPEAL_CLOSE" | "SAFETY_HUB_APPEAL_OPEN" | "SAFETY_HUB_APPEAL_SIGNAL_CUSTOM_INPUT_CHANGE" | "SAFETY_HUB_APPEAL_SIGNAL_SELECT" | "SAFETY_HUB_FETCH_CLASSIFICATION_FAILURE" | "SAFETY_HUB_FETCH_CLASSIFICATION_START" | "SAFETY_HUB_FETCH_CLASSIFICATION_SUCCESS" | "SAFETY_HUB_FETCH_FAILURE" | "SAFETY_HUB_FETCH_START" | "SAFETY_HUB_FETCH_SUCCESS" | "SAFETY_HUB_REQUEST_REVIEW_FAILURE" | "SAFETY_HUB_REQUEST_REVIEW_START" | "SAFETY_HUB_REQUEST_REVIEW_SUCCESS" | "SAVED_MESSAGES_UPDATE" | "SAVE_LAST_NON_VOICE_ROUTE" | "SAVE_LAST_ROUTE" | "SEARCH_ADD_HISTORY" | "SEARCH_AUTOCOMPLETE_QUERY_UPDATE" | "SEARCH_CLEAR_HISTORY" | "SEARCH_EDITOR_STATE_CHANGE" | "SEARCH_EDITOR_STATE_CLEAR" | "SEARCH_ENSURE_SEARCH_STATE" | "SEARCH_FINISH" | "SEARCH_INDEXING" | "SEARCH_MODAL_CLOSE" | "SEARCH_MODAL_OPEN" | "SEARCH_REMOVE_HISTORY" | "SEARCH_SCREEN_OPEN" | "SEARCH_SET_SHOW_BLOCKED_RESULTS" | "SEARCH_START" | "SELECTIVELY_SYNCED_USER_SETTINGS_UPDATE" | "SELECT_HOME_RESOURCE_CHANNEL" | "SELECT_NEW_MEMBER_ACTION_CHANNEL" | "SELF_PRESENCE_STORE_UPDATE" | "SESSIONS_REPLACE" | "SET_CHANNEL_BITRATE" | "SET_CHANNEL_VIDEO_QUALITY_MODE" | "SET_CONSENT_REQUIRED" | "SET_CREATED_AT_OVERRIDE" | "SET_GUILD_FOLDER_EXPANDED" | "SET_HIGHLIGHTED_SUMMARY" | "SET_INTERACTION_COMPONENT_STATE" | "SET_LOCATION_METADATA" | "SET_LOGIN_CREDENTIALS" | "SET_NATIVE_PERMISSION" | "SET_PENDING_REPLY_SHOULD_MENTION" | "SET_PREMIUM_TYPE_OVERRIDE" | "SET_RECENTLY_ACTIVE_COLLAPSED" | "SET_RECENT_MENTIONS_FILTER" | "SET_RECENT_MENTIONS_STALE" | "SET_SELECTED_SUMMARY" | "SET_SOUNDPACK" | "SET_STREAM_APP_INTENT" | "SET_SUMMARY_FEEDBACK" | "SET_TTS_SPEECH_RATE" | "SET_VAD_PERMISSION" | "SHARED_CANVAS_CLEAR_DRAWABLES" | "SHARED_CANVAS_DRAW_LINE_POINT" | "SHARED_CANVAS_SET_DRAW_MODE" | "SHARED_CANVAS_UPDATE_EMOJI_HOSE" | "SHARED_CANVAS_UPDATE_LINE_POINTS" | "SHOW_ACTION_SHEET" | "SHOW_ACTION_SHEET_QUICK_SWITCHER" | "SHOW_KEYBOARD_SHORTCUTS" | "SIDEBAR_CLOSE" | "SIDEBAR_CLOSE_GUILD" | "SIDEBAR_CREATE_THREAD" | "SIDEBAR_VIEW_CHANNEL" | "SIDEBAR_VIEW_GUILD" | "SKUS_FETCH_SUCCESS" | "SKU_FETCH_FAIL" | "SKU_FETCH_START" | "SKU_FETCH_SUCCESS" | "SKU_PURCHASE_AWAIT_CONFIRMATION" | "SKU_PURCHASE_CLEAR_ERROR" | "SKU_PURCHASE_FAIL" | "SKU_PURCHASE_MODAL_CLOSE" | "SKU_PURCHASE_MODAL_OPEN" | "SKU_PURCHASE_PREVIEW_FETCH" | "SKU_PURCHASE_PREVIEW_FETCH_FAILURE" | "SKU_PURCHASE_PREVIEW_FETCH_SUCCESS" | "SKU_PURCHASE_SHOW_CONFIRMATION_STEP" | "SKU_PURCHASE_START" | "SKU_PURCHASE_SUCCESS" | "SKU_PURCHASE_UPDATE_IS_GIFT" | "SLOWMODE_RESET_COOLDOWN" | "SLOWMODE_SET_COOLDOWN" | "SOUNDBOARD_FETCH_DEFAULT_SOUNDS" | "SOUNDBOARD_FETCH_DEFAULT_SOUNDS_SUCCESS" | "SOUNDBOARD_MUTE_JOIN_SOUND" | "SOUNDBOARD_SET_OVERLAY_ENABLED" | "SOUNDBOARD_SOUNDS_RECEIVED" | "SPEAKING" | "SPEAKING_MESSAGE" | "SPEAK_MESSAGE" | "SPEAK_TEXT" | "SPEED_TEST_CREATE" | "SPEED_TEST_DELETE" | "SPEED_TEST_SERVER_UPDATE" | "SPELLCHECK_LEARN_WORD" | "SPELLCHECK_TOGGLE" | "SPELLCHECK_UNLEARN_WORD" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN_REVOKE" | "SPOTIFY_NEW_TRACK" | "SPOTIFY_PLAYER_PAUSE" | "SPOTIFY_PLAYER_PLAY" | "SPOTIFY_PLAYER_STATE" | "SPOTIFY_PROFILE_UPDATE" | "SPOTIFY_SET_ACTIVE_DEVICE" | "SPOTIFY_SET_DEVICES" | "SPOTIFY_SET_PROTOCOL_REGISTERED" | "STAGE_INSTANCE_CREATE" | "STAGE_INSTANCE_DELETE" | "STAGE_INSTANCE_UPDATE" | "STAGE_MUSIC_MUTE" | "STAGE_MUSIC_PLAY" | "START_BROADCAST_STREAM" | "START_SESSION" | "STATUS_PAGE_INCIDENT" | "STATUS_PAGE_SCHEDULED_MAINTENANCE" | "STATUS_PAGE_SCHEDULED_MAINTENANCE_ACK" | "STICKER_FETCH_SUCCESS" | "STICKER_PACKS_FETCH_START" | "STICKER_PACKS_FETCH_SUCCESS" | "STICKER_PACK_FETCH_SUCCESS" | "STICKER_TRACK_USAGE" | "STOP_SPEAKING" | "STORE_LISTINGS_FETCH_SUCCESS" | "STORE_LISTING_FETCH_SUCCESS" | "STREAMER_MODE_UPDATE" | "STREAMING_UPDATE" | "STREAM_CLOSE" | "STREAM_CREATE" | "STREAM_DELETE" | "STREAM_LAYOUT_UPDATE" | "STREAM_PREVIEW_FETCH_FAIL" | "STREAM_PREVIEW_FETCH_START" | "STREAM_PREVIEW_FETCH_SUCCESS" | "STREAM_SERVER_UPDATE" | "STREAM_SET_PAUSED" | "STREAM_START" | "STREAM_STATS_UPDATE" | "STREAM_STOP" | "STREAM_TIMED_OUT" | "STREAM_UPDATE" | "STREAM_UPDATE_SELF_HIDDEN" | "STREAM_UPDATE_SETTINGS" | "STREAM_WATCH" | "STRIPE_TOKEN_FAILURE" | "SUBSCRIPTION_PLANS_FETCH" | "SUBSCRIPTION_PLANS_FETCH_FAILURE" | "SUBSCRIPTION_PLANS_FETCH_SUCCESS" | "SUBSCRIPTION_PLANS_RESET" | "SURVEY_FETCHED" | "SURVEY_HIDE" | "SURVEY_OVERRIDE" | "SURVEY_SEEN" | "SYSTEM_THEME_CHANGE" | "THERMAL_STATE_CHANGE" | "THREAD_CREATE" | "THREAD_CREATE_LOCAL" | "THREAD_DELETE" | "THREAD_LIST_SYNC" | "THREAD_MEMBERS_UPDATE" | "THREAD_MEMBER_LIST_UPDATE" | "THREAD_MEMBER_LOCAL_UPDATE" | "THREAD_MEMBER_UPDATE" | "THREAD_SETTINGS_DRAFT_CHANGE" | "THREAD_UPDATE" | "TOGGLE_GUILD_FOLDER_EXPAND" | "TOGGLE_OVERLAY_CANVAS" | "TOGGLE_TOPICS_BAR" | "TOP_EMOJIS_FETCH" | "TOP_EMOJIS_FETCH_SUCCESS" | "TRUNCATE_MENTIONS" | "TRUNCATE_MESSAGES" | "TRY_ACK" | "TUTORIAL_INDICATOR_DISMISS" | "TUTORIAL_INDICATOR_HIDE" | "TUTORIAL_INDICATOR_SHOW" | "TUTORIAL_INDICATOR_SUPPRESS_ALL" | "TYPING_START" | "TYPING_START_LOCAL" | "TYPING_STOP" | "TYPING_STOP_LOCAL" | "UNREAD_SETTING_NOTICE_CHANNEL_VISIT" | "UNREAD_SETTING_NOTICE_RENDERED" | "UNSYNCED_USER_SETTINGS_UPDATE" | "UNVERIFIED_GAME_UPDATE" | "UPCOMING_GUILD_EVENT_NOTICE_HIDE" | "UPCOMING_GUILD_EVENT_NOTICE_SEEN" | "UPDATE_AVAILABLE" | "UPDATE_BACKGROUND_GRADIENT_PRESET" | "UPDATE_CHANNEL_DIMENSIONS" | "UPDATE_CHANNEL_LIST_DIMENSIONS" | "UPDATE_CHANNEL_LIST_SUBTITLES" | "UPDATE_CLIENT_PREMIUM_TYPE" | "UPDATE_CONSENTS" | "UPDATE_DOWNLOADED" | "UPDATE_ERROR" | "UPDATE_GUILD_LIST_DIMENSIONS" | "UPDATE_HANG_STATUS" | "UPDATE_HANG_STATUS_CUSTOM" | "UPDATE_MANUALLY" | "UPDATE_MOBILE_PENDING_THEME_INDEX" | "UPDATE_NOT_AVAILABLE" | "UPDATE_TOKEN" | "UPDATE_VISIBLE_MESSAGES" | "UPLOAD_ATTACHMENT_ADD_FILES" | "UPLOAD_ATTACHMENT_CLEAR_ALL_FILES" | "UPLOAD_ATTACHMENT_POP_FILE" | "UPLOAD_ATTACHMENT_REMOVE_FILE" | "UPLOAD_ATTACHMENT_REMOVE_FILES" | "UPLOAD_ATTACHMENT_SET_FILE" | "UPLOAD_ATTACHMENT_SET_UPLOADS" | "UPLOAD_ATTACHMENT_UPDATE_FILE" | "UPLOAD_CANCEL_REQUEST" | "UPLOAD_COMPLETE" | "UPLOAD_COMPRESSION_PROGRESS" | "UPLOAD_FAIL" | "UPLOAD_FILE_UPDATE" | "UPLOAD_ITEM_CANCEL_REQUEST" | "UPLOAD_PROGRESS" | "UPLOAD_RESTORE_FAILED_UPLOAD" | "UPLOAD_START" | "USER_ACHIEVEMENT_UPDATE" | "USER_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "USER_APPLICATION_REMOVE" | "USER_APPLICATION_UPDATE" | "USER_APPLIED_BOOSTS_FETCH_START" | "USER_APPLIED_BOOSTS_FETCH_SUCCESS" | "USER_AUTHORIZED_APPS_UPDATE" | "USER_CONNECTIONS_INTEGRATION_JOINING" | "USER_CONNECTIONS_INTEGRATION_JOINING_ERROR" | "USER_CONNECTIONS_UPDATE" | "USER_CONNECTION_UPDATE" | "USER_GUILD_JOIN_REQUEST_UPDATE" | "USER_GUILD_SETTINGS_CHANNEL_UPDATE" | "USER_GUILD_SETTINGS_CHANNEL_UPDATE_BULK" | "USER_GUILD_SETTINGS_FULL_UPDATE" | "USER_GUILD_SETTINGS_GUILD_AND_CHANNELS_UPDATE" | "USER_GUILD_SETTINGS_GUILD_UPDATE" | "USER_GUILD_SETTINGS_REMOVE_PENDING_CHANNEL_UPDATES" | "USER_JOIN_REQUEST_GUILDS_FETCH" | "USER_NON_CHANNEL_ACK" | "USER_NOTE_LOADED" | "USER_NOTE_LOAD_START" | "USER_NOTE_UPDATE" | "USER_PAYMENT_BROWSER_CHECKOUT_DONE" | "USER_PAYMENT_BROWSER_CHECKOUT_STARTED" | "USER_PAYMENT_CLIENT_ADD" | "USER_PROFILE_ACCESSIBILITY_TOOLTIP_VIEWED" | "USER_PROFILE_EFFECTS_FETCH" | "USER_PROFILE_EFFECTS_FETCH_FAILURE" | "USER_PROFILE_EFFECTS_FETCH_SUCCESS" | "USER_PROFILE_FETCH_FAILURE" | "USER_PROFILE_FETCH_START" | "USER_PROFILE_FETCH_SUCCESS" | "USER_PROFILE_MODAL_CLOSE" | "USER_PROFILE_MODAL_OPEN" | "USER_PROFILE_UPDATE_FAILURE" | "USER_PROFILE_UPDATE_START" | "USER_PROFILE_UPDATE_SUCCESS" | "USER_RECENT_GAMES_FETCH_ERROR" | "USER_RECENT_GAMES_FETCH_START" | "USER_RECENT_GAMES_FETCH_SUCCESS" | "USER_RECENT_GAMES_UPDATE_LOCAL" | "USER_REQUIRED_ACTION_UPDATE" | "USER_SETTINGS_ACCOUNT_CLOSE" | "USER_SETTINGS_ACCOUNT_INIT" | "USER_SETTINGS_ACCOUNT_RESET_AND_CLOSE_FORM" | "USER_SETTINGS_ACCOUNT_SET_PENDING_ACCENT_COLOR" | "USER_SETTINGS_ACCOUNT_SET_PENDING_AVATAR" | "USER_SETTINGS_ACCOUNT_SET_PENDING_AVATAR_DECORATION" | "USER_SETTINGS_ACCOUNT_SET_PENDING_BANNER" | "USER_SETTINGS_ACCOUNT_SET_PENDING_BIO" | "USER_SETTINGS_ACCOUNT_SET_PENDING_GLOBAL_NAME" | "USER_SETTINGS_ACCOUNT_SET_PENDING_PROFILE_EFFECT_ID" | "USER_SETTINGS_ACCOUNT_SET_PENDING_PRONOUNS" | "USER_SETTINGS_ACCOUNT_SET_PENDING_THEME_COLORS" | "USER_SETTINGS_ACCOUNT_SET_SINGLE_TRY_IT_OUT_COLLECTIBLES_ITEM" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_AVATAR" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_AVATAR_DECORATION" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_BANNER" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_PROFILE_EFFECT_ID" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_THEME_COLORS" | "USER_SETTINGS_ACCOUNT_SUBMIT" | "USER_SETTINGS_ACCOUNT_SUBMIT_FAILURE" | "USER_SETTINGS_ACCOUNT_SUBMIT_SUCCESS" | "USER_SETTINGS_CLEAR_ERRORS" | "USER_SETTINGS_LOCALE_OVERRIDE" | "USER_SETTINGS_MODAL_CLEAR_SCROLL_POSITION" | "USER_SETTINGS_MODAL_CLEAR_SUBSECTION" | "USER_SETTINGS_MODAL_CLOSE" | "USER_SETTINGS_MODAL_INIT" | "USER_SETTINGS_MODAL_OPEN" | "USER_SETTINGS_MODAL_RESET" | "USER_SETTINGS_MODAL_SET_SECTION" | "USER_SETTINGS_MODAL_SUBMIT" | "USER_SETTINGS_MODAL_SUBMIT_COMPLETE" | "USER_SETTINGS_MODAL_SUBMIT_FAILURE" | "USER_SETTINGS_MODAL_UPDATE_ACCOUNT" | "USER_SETTINGS_OVERRIDE_APPLY" | "USER_SETTINGS_OVERRIDE_CLEAR" | "USER_SETTINGS_PROTO_ENQUEUE_UPDATE" | "USER_SETTINGS_PROTO_LOAD_IF_NECESSARY" | "USER_SETTINGS_PROTO_UPDATE" | "USER_SETTINGS_PROTO_UPDATE_EDIT_INFO" | "USER_SETTINGS_RESET_ALL_PENDING" | "USER_SETTINGS_RESET_ALL_TRY_IT_OUT" | "USER_SETTINGS_RESET_PENDING_ACCOUNT_CHANGES" | "USER_SETTINGS_RESET_PENDING_AVATAR_DECORATION" | "USER_SETTINGS_RESET_PENDING_PROFILE_CHANGES" | "USER_SOUNDBOARD_SET_VOLUME" | "USER_TENURE_REWARD_STATUS_DELETE" | "USER_TENURE_REWARD_STATUS_RESET" | "USER_TENURE_REWARD_SYNC_START" | "USER_TENURE_REWARD_SYNC_SUCCESS" | "USER_UPDATE" | "VERIFY_FAILURE" | "VERIFY_SUCCESS" | "VIDEO_FILTER_ASSETS_FETCH_SUCCESS" | "VIDEO_FILTER_ASSET_DELETE_SUCCESS" | "VIDEO_FILTER_ASSET_UPLOAD_SUCCESS" | "VIDEO_SAVE_LAST_USED_BACKGROUND_OPTION" | "VIEW_HISTORY_MARK_VIEW" | "VOICE_CATEGORY_COLLAPSE" | "VOICE_CATEGORY_EXPAND" | "VOICE_CHANNEL_EFFECT_CLEAR" | "VOICE_CHANNEL_EFFECT_RECENT_EMOJI" | "VOICE_CHANNEL_EFFECT_SEND" | "VOICE_CHANNEL_EFFECT_SENT_LOCAL" | "VOICE_CHANNEL_EFFECT_TOGGLE_ANIMATION_TYPE" | "VOICE_CHANNEL_EFFECT_UPDATE_TIME_STAMP" | "VOICE_CHANNEL_SELECT" | "VOICE_CHANNEL_STATUS_UPDATE" | "VOICE_SERVER_UPDATE" | "VOICE_STATE_UPDATES" | "WAIT_FOR_REMOTE_SESSION" | "WEBHOOKS_FETCHING" | "WEBHOOKS_UPDATE" | "WEBHOOK_CREATE" | "WEBHOOK_DELETE" | "WEBHOOK_UPDATE" | "WELCOME_SCREEN_FETCH_FAIL" | "WELCOME_SCREEN_FETCH_START" | "WELCOME_SCREEN_FETCH_SUCCESS" | "WELCOME_SCREEN_SUBMIT_SUCCESS" | "WELCOME_SCREEN_UPDATE" | "WELCOME_SCREEN_VIEW" | "WINDOW_FOCUS" | "WINDOW_FULLSCREEN_CHANGE" | "WINDOW_HIDDEN" | "WINDOW_INIT" | "WINDOW_RESIZED" | "WINDOW_UNLOAD" | "WINDOW_VISIBILITY_CHANGE" | "WRITE_CACHES"; +// 61.8kb worth of events ??????? +export type FluxEvents = "ACCESSIBILITY_COLORBLIND_TOGGLE" | "ACCESSIBILITY_DARK_SIDEBAR_TOGGLE" | "ACCESSIBILITY_DESATURATE_ROLES_TOGGLE" | "ACCESSIBILITY_FORCED_COLORS_MODAL_SEEN" | "ACCESSIBILITY_KEYBOARD_MODE_DISABLE" | "ACCESSIBILITY_KEYBOARD_MODE_ENABLE" | "ACCESSIBILITY_LOW_CONTRAST_TOGGLE" | "ACCESSIBILITY_RESET_TO_DEFAULT" | "ACCESSIBILITY_SET_ALWAYS_SHOW_LINK_DECORATIONS" | "ACCESSIBILITY_SET_CONTRAST" | "ACCESSIBILITY_SET_FONT_SIZE" | "ACCESSIBILITY_SET_HIDE_TAGS" | "ACCESSIBILITY_SET_MESSAGE_GROUP_SPACING" | "ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION" | "ACCESSIBILITY_SET_ROLE_STYLE" | "ACCESSIBILITY_SET_SATURATION" | "ACCESSIBILITY_SET_SYNC_FORCED_COLORS" | "ACCESSIBILITY_SET_ZOOM" | "ACCESSIBILITY_SUBMIT_BUTTON_TOGGLE" | "ACCESSIBILITY_SYNC_PROFILE_THEME_WITH_USER_THEME_TOGGLE" | "ACCESSIBILITY_SYSTEM_COLOR_PREFERENCES_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_CONTRAST_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_CROSSFADES_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED" | "ACKNOWLEDGE_CHANNEL_SAFETY_WARNING_TOOLTIP" | "ACK_APPROVED_GUILD_JOIN_REQUEST" | "ACTIVE_BOGO_PROMOTION_FETCH" | "ACTIVE_BOGO_PROMOTION_FETCH_FAIL" | "ACTIVE_BOGO_PROMOTION_FETCH_SUCCESS" | "ACTIVE_CHANNELS_FETCH_FAILURE" | "ACTIVE_CHANNELS_FETCH_START" | "ACTIVE_CHANNELS_FETCH_SUCCESS" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH_FAIL" | "ACTIVE_OUTBOUND_PROMOTIONS_FETCH_SUCCESS" | "ACTIVITIES_WHATS_NEW_ACKNOWLEDGE_SECTION" | "ACTIVITIES_WHATS_NEW_MARK_OPENED_SECTION" | "ACTIVITY_INVITE_EDUCATION_DISMISS" | "ACTIVITY_INVITE_MODAL_CLOSE" | "ACTIVITY_INVITE_MODAL_OPEN" | "ACTIVITY_JOIN" | "ACTIVITY_JOIN_FAILED" | "ACTIVITY_JOIN_LOADING" | "ACTIVITY_LAUNCH_FAIL" | "ACTIVITY_LAYOUT_MODE_UPDATE" | "ACTIVITY_METADATA_UPDATE" | "ACTIVITY_PLAY" | "ACTIVITY_SCREEN_ORIENTATION_UPDATE" | "ACTIVITY_START" | "ACTIVITY_SYNC" | "ACTIVITY_SYNC_STOP" | "ACTIVITY_UPDATE_FAIL" | "ACTIVITY_UPDATE_START" | "ACTIVITY_UPDATE_SUCCESS" | "ADD_STICKER_PREVIEW" | "ADMIN_ONBOARDING_GUIDE_HIDE" | "ADYEN_CASH_APP_PAY_SUBMIT_SUCCESS" | "ADYEN_CREATE_CASH_APP_PAY_COMPONENT_SUCCESS" | "ADYEN_CREATE_CLIENT_SUCCESS" | "ADYEN_TEARDOWN_CLIENT" | "AFK" | "AGE_GATE_FAILURE_MODAL_OPEN" | "AGE_GATE_LOGOUT_UNDERAGE_NEW_USER" | "AGE_GATE_MODAL_CLOSE" | "AGE_GATE_MODAL_OPEN" | "AGE_GATE_SUCCESS_MODAL_OPEN" | "APPLICATIONS_FETCH" | "APPLICATIONS_FETCH_FAIL" | "APPLICATIONS_FETCH_SUCCESS" | "APPLICATIONS_SHELF_FETCH_FAIL" | "APPLICATIONS_SHELF_FETCH_START" | "APPLICATIONS_SHELF_FETCH_SUCCESS" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_FAIL" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_START" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "APPLICATION_ASSETS_FETCH" | "APPLICATION_ASSETS_FETCH_SUCCESS" | "APPLICATION_ASSETS_UPDATE" | "APPLICATION_BRANCHES_FETCH_FAIL" | "APPLICATION_BRANCHES_FETCH_SUCCESS" | "APPLICATION_BUILD_FETCH_START" | "APPLICATION_BUILD_FETCH_SUCCESS" | "APPLICATION_BUILD_NOT_FOUND" | "APPLICATION_BUILD_SIZE_FETCH_FAIL" | "APPLICATION_BUILD_SIZE_FETCH_START" | "APPLICATION_BUILD_SIZE_FETCH_SUCCESS" | "APPLICATION_COMMAND_AUTOCOMPLETE_REQUEST" | "APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE" | "APPLICATION_COMMAND_EXECUTE_BAD_VERSION" | "APPLICATION_COMMAND_INDEX_FETCH_FAILURE" | "APPLICATION_COMMAND_INDEX_FETCH_REQUEST" | "APPLICATION_COMMAND_INDEX_FETCH_SUCCESS" | "APPLICATION_COMMAND_SET_ACTIVE_COMMAND" | "APPLICATION_COMMAND_SET_PREFERRED_COMMAND" | "APPLICATION_COMMAND_UPDATE_CHANNEL_STATE" | "APPLICATION_COMMAND_UPDATE_OPTIONS" | "APPLICATION_COMMAND_USED" | "APPLICATION_DIRECTORY_FETCH_APPLICATION" | "APPLICATION_DIRECTORY_FETCH_APPLICATION_FAILURE" | "APPLICATION_DIRECTORY_FETCH_APPLICATION_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_CATEGORIES_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS_FAILURE" | "APPLICATION_DIRECTORY_FETCH_COLLECTIONS_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_SEARCH" | "APPLICATION_DIRECTORY_FETCH_SEARCH_FAILURE" | "APPLICATION_DIRECTORY_FETCH_SEARCH_SUCCESS" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS_FAILURE" | "APPLICATION_DIRECTORY_FETCH_SIMILAR_APPLICATIONS_SUCCESS" | "APPLICATION_FETCH" | "APPLICATION_FETCH_FAIL" | "APPLICATION_FETCH_SUCCESS" | "APPLICATION_STORE_ACCEPT_EULA" | "APPLICATION_STORE_ACCEPT_STORE_TERMS" | "APPLICATION_STORE_CLEAR_DATA" | "APPLICATION_STORE_DIRECTORY_LAYOUT_FETCHING" | "APPLICATION_STORE_DIRECTORY_LAYOUT_FETCH_FAILED" | "APPLICATION_STORE_DIRECTORY_LAYOUT_FETCH_SUCCESS" | "APPLICATION_STORE_LOCATION_CHANGE" | "APPLICATION_STORE_MATURE_AGREE" | "APPLICATION_STORE_RESET_NAVIGATION" | "APPLICATION_SUBSCRIPTIONS_CHANNEL_NOTICE_DISMISSED" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS_FAILURE" | "APPLICATION_SUBSCRIPTIONS_FETCH_ENTITLEMENTS_SUCCESS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS_FAILURE" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTINGS_SUCCESS" | "APPLICATION_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN_SUCCESS" | "APPLIED_BOOSTS_COOLDOWN_FETCH_SUCCESS" | "APPLIED_GUILD_BOOST_COUNT_UPDATE" | "APP_ICON_EDITOR_CLOSE" | "APP_ICON_EDITOR_OPEN" | "APP_ICON_EDITOR_RESET" | "APP_ICON_TRACK_IMPRESSION" | "APP_ICON_UPDATED" | "APP_LAUNCHER_DISMISS" | "APP_LAUNCHER_SET_ACTIVE_COMMAND" | "APP_LAUNCHER_SHOW" | "APP_RECOMMENDATIONS_FETCH_RECOMMENDATIONS" | "APP_RECOMMENDATIONS_FETCH_RECOMMENDATIONS_FAILURE" | "APP_RECOMMENDATIONS_FETCH_RECOMMENDATIONS_SUCCESS" | "APP_STATE_UPDATE" | "APP_VIEW_SET_HOME_LINK" | "AUDIO_INPUT_DETECTED" | "AUDIO_RESET" | "AUDIO_SET_ATTENUATION" | "AUDIO_SET_AUTOMATIC_GAIN_CONTROL" | "AUDIO_SET_DEBUG_LOGGING" | "AUDIO_SET_DISPLAY_SILENCE_WARNING" | "AUDIO_SET_ECHO_CANCELLATION" | "AUDIO_SET_INPUT_DEVICE" | "AUDIO_SET_INPUT_VOLUME" | "AUDIO_SET_LOCAL_PAN" | "AUDIO_SET_LOCAL_VIDEO_DISABLED" | "AUDIO_SET_LOCAL_VOLUME" | "AUDIO_SET_LOOPBACK" | "AUDIO_SET_MODE" | "AUDIO_SET_NOISE_CANCELLATION" | "AUDIO_SET_NOISE_SUPPRESSION" | "AUDIO_SET_OUTPUT_DEVICE" | "AUDIO_SET_OUTPUT_VOLUME" | "AUDIO_SET_QOS" | "AUDIO_SET_SELF_MUTE" | "AUDIO_SET_SUBSYSTEM" | "AUDIO_SET_TEMPORARY_SELF_MUTE" | "AUDIO_TOGGLE_LOCAL_MUTE" | "AUDIO_TOGGLE_LOCAL_SOUNDBOARD_MUTE" | "AUDIO_TOGGLE_SELF_DEAF" | "AUDIO_TOGGLE_SELF_MUTE" | "AUDIO_VOLUME_CHANGE" | "AUDIT_LOG_FETCH_FAIL" | "AUDIT_LOG_FETCH_NEXT_PAGE_FAIL" | "AUDIT_LOG_FETCH_NEXT_PAGE_START" | "AUDIT_LOG_FETCH_NEXT_PAGE_SUCCESS" | "AUDIT_LOG_FETCH_START" | "AUDIT_LOG_FETCH_SUCCESS" | "AUDIT_LOG_FILTER_BY_ACTION" | "AUDIT_LOG_FILTER_BY_TARGET" | "AUDIT_LOG_FILTER_BY_USER" | "AUTHENTICATOR_CREATE" | "AUTHENTICATOR_DELETE" | "AUTHENTICATOR_UPDATE" | "AUTH_INVITE_UPDATE" | "AUTH_SESSION_CHANGE" | "AUTO_MODERATION_MENTION_RAID_DETECTION" | "AUTO_MODERATION_MENTION_RAID_NOTICE_DISMISS" | "BACKGROUND_SYNC" | "BACKGROUND_SYNC_CHANNEL_MESSAGES" | "BASIC_GUILD_FETCH" | "BASIC_GUILD_FETCH_FAILURE" | "BASIC_GUILD_FETCH_SUCCESS" | "BILLING_CREATE_REFERRAL_PREVIEW_FAIL" | "BILLING_CREATE_REFERRAL_PREVIEW_START" | "BILLING_CREATE_REFERRAL_PREVIEW_SUCCESS" | "BILLING_CREATE_REFERRAL_SUCCESS" | "BILLING_IP_COUNTRY_CODE_FAILURE" | "BILLING_IP_COUNTRY_CODE_FETCH_START" | "BILLING_LOCALIZED_PRICING_PROMO_FAILURE" | "BILLING_MOST_RECENT_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_NITRO_AFFINITY_FETCHED" | "BILLING_NITRO_AFFINITY_FETCH_SUCCEEDED" | "BILLING_PAYMENTS_FETCH_SUCCESS" | "BILLING_PAYMENT_FETCH_SUCCESS" | "BILLING_PAYMENT_SOURCES_FETCH_FAIL" | "BILLING_PAYMENT_SOURCES_FETCH_START" | "BILLING_PAYMENT_SOURCES_FETCH_SUCCESS" | "BILLING_PAYMENT_SOURCE_CREATE_FAIL" | "BILLING_PAYMENT_SOURCE_CREATE_START" | "BILLING_PAYMENT_SOURCE_CREATE_SUCCESS" | "BILLING_PAYMENT_SOURCE_FETCH_SUCCESS" | "BILLING_PAYMENT_SOURCE_REMOVE_CLEAR_ERROR" | "BILLING_PAYMENT_SOURCE_REMOVE_FAIL" | "BILLING_PAYMENT_SOURCE_REMOVE_START" | "BILLING_PAYMENT_SOURCE_REMOVE_SUCCESS" | "BILLING_PAYMENT_SOURCE_UPDATE_CLEAR_ERROR" | "BILLING_PAYMENT_SOURCE_UPDATE_FAIL" | "BILLING_PAYMENT_SOURCE_UPDATE_START" | "BILLING_PAYMENT_SOURCE_UPDATE_SUCCESS" | "BILLING_PERKS_RELEVANCE_FETCH_FAIL" | "BILLING_PERKS_RELEVANCE_FETCH_START" | "BILLING_PERKS_RELEVANCE_FETCH_SUCCESS" | "BILLING_POPUP_BRIDGE_CALLBACK" | "BILLING_POPUP_BRIDGE_STATE_UPDATE" | "BILLING_PREVIOUS_PREMIUM_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_PURCHASE_TOKEN_AUTH_CLEAR_STATE" | "BILLING_REFERRALS_REMAINING_FETCH_FAIL" | "BILLING_REFERRALS_REMAINING_FETCH_START" | "BILLING_REFERRALS_REMAINING_FETCH_SUCCESS" | "BILLING_REFERRAL_RESOLVE_FAIL" | "BILLING_REFERRAL_RESOLVE_SUCCESS" | "BILLING_REFERRAL_TRIAL_OFFER_UPDATE" | "BILLING_SET_IP_COUNTRY_CODE" | "BILLING_SET_LOCALIZED_PRICING_PROMO" | "BILLING_SUBSCRIPTION_CANCEL_FAIL" | "BILLING_SUBSCRIPTION_CANCEL_START" | "BILLING_SUBSCRIPTION_CANCEL_SUCCESS" | "BILLING_SUBSCRIPTION_FETCH_FAIL" | "BILLING_SUBSCRIPTION_FETCH_START" | "BILLING_SUBSCRIPTION_FETCH_SUCCESS" | "BILLING_SUBSCRIPTION_RESET" | "BILLING_SUBSCRIPTION_UPDATE_FAIL" | "BILLING_SUBSCRIPTION_UPDATE_START" | "BILLING_SUBSCRIPTION_UPDATE_SUCCESS" | "BILLING_USER_OFFER_ACKNOWLEDGED_SUCCESS" | "BILLING_USER_OFFER_FETCH_FAIL" | "BILLING_USER_OFFER_FETCH_START" | "BILLING_USER_OFFER_FETCH_SUCCESS" | "BILLING_USER_TRIAL_OFFER_ACKNOWLEDGED_SUCCESS" | "BILLING_USER_TRIAL_OFFER_FETCH_SUCCESS" | "BLOCKED_DOMAIN_LIST_FETCHED" | "BOOSTED_GUILD_GRACE_PERIOD_NOTICE_DISMISS" | "BRAINTREE_CREATE_CLIENT_SUCCESS" | "BRAINTREE_CREATE_PAYPAL_CLIENT_SUCCESS" | "BRAINTREE_CREATE_VENMO_CLIENT_SUCCESS" | "BRAINTREE_TEARDOWN_PAYPAL_CLIENT" | "BRAINTREE_TEARDOWN_VENMO_CLIENT" | "BRAINTREE_TOKENIZE_PAYPAL_FAIL" | "BRAINTREE_TOKENIZE_PAYPAL_START" | "BRAINTREE_TOKENIZE_PAYPAL_SUCCESS" | "BRAINTREE_TOKENIZE_VENMO_FAIL" | "BRAINTREE_TOKENIZE_VENMO_START" | "BRAINTREE_TOKENIZE_VENMO_SUCCESS" | "BROADCASTER_BUCKETS_RECEIVED" | "BROADCAST_START" | "BROADCAST_STOP" | "BROADCAST_VIEWERS_UPDATE" | "BROWSER_HANDOFF_BEGIN" | "BROWSER_HANDOFF_FROM_APP" | "BROWSER_HANDOFF_SET_USER" | "BROWSER_HANDOFF_UNAVAILABLE" | "BUILD_OVERRIDE_RESOLVED" | "BULK_ACK" | "BULK_CLEAR_RECENTS" | "BURST_REACTION_ANIMATION_ADD" | "BURST_REACTION_EFFECT_CLEAR" | "BURST_REACTION_EFFECT_PLAY" | "BURST_REACTION_PICKER_ANIMATION_ADD" | "BURST_REACTION_PICKER_ANIMATION_CLEAR" | "CACHED_EMOJIS_LOADED" | "CACHED_STICKERS_LOADED" | "CACHE_LOADED" | "CACHE_LOADED_LAZY" | "CACHE_LOADED_LAZY_NO_CACHE" | "CALL_CHAT_TOASTS_SET_ENABLED" | "CALL_CONNECT" | "CALL_CONNECT_MULTIPLE" | "CALL_CREATE" | "CALL_DELETE" | "CALL_ENQUEUE_RING" | "CALL_UPDATE" | "CANDIDATE_GAMES_CHANGE" | "CATEGORY_COLLAPSE" | "CATEGORY_COLLAPSE_ALL" | "CATEGORY_EXPAND" | "CATEGORY_EXPAND_ALL" | "CERTIFIED_DEVICES_SET" | "CHANGE_LOG_FETCH_FAILED" | "CHANGE_LOG_FETCH_SUCCESS" | "CHANGE_LOG_LOCK" | "CHANGE_LOG_MARK_SEEN" | "CHANGE_LOG_SET_CONFIG" | "CHANGE_LOG_SET_OVERRIDE" | "CHANGE_LOG_UNLOCK" | "CHANNEL_ACK" | "CHANNEL_CALL_POPOUT_WINDOW_OPEN" | "CHANNEL_COLLAPSE" | "CHANNEL_CREATE" | "CHANNEL_DELETE" | "CHANNEL_FOLLOWER_CREATED" | "CHANNEL_FOLLOWER_STATS_FETCH_FAILURE" | "CHANNEL_FOLLOWER_STATS_FETCH_SUCCESS" | "CHANNEL_FOLLOWING_PUBLISH_BUMP_DISMISSED" | "CHANNEL_FOLLOWING_PUBLISH_BUMP_HIDE_PERMANENTLY" | "CHANNEL_LOCAL_ACK" | "CHANNEL_MUTE_EXPIRED" | "CHANNEL_PINS_ACK" | "CHANNEL_PINS_UPDATE" | "CHANNEL_PRELOAD" | "CHANNEL_RECIPIENT_ADD" | "CHANNEL_RECIPIENT_REMOVE" | "CHANNEL_RTC_ACTIVE_CHANNELS" | "CHANNEL_RTC_SELECT_PARTICIPANT" | "CHANNEL_RTC_UPDATE_CHAT_OPEN" | "CHANNEL_RTC_UPDATE_LAYOUT" | "CHANNEL_RTC_UPDATE_PARTICIPANTS_OPEN" | "CHANNEL_RTC_UPDATE_STAGE_STREAM_SIZE" | "CHANNEL_RTC_UPDATE_STAGE_VIDEO_LIMIT_BOOST_UPSELL_DISMISSED" | "CHANNEL_RTC_UPDATE_VOICE_PARTICIPANTS_HIDDEN" | "CHANNEL_SAFETY_WARNING_FEEDBACK" | "CHANNEL_SELECT" | "CHANNEL_SETTINGS_CLOSE" | "CHANNEL_SETTINGS_INIT" | "CHANNEL_SETTINGS_LOADED_INVITES" | "CHANNEL_SETTINGS_OPEN" | "CHANNEL_SETTINGS_OVERWRITE_SELECT" | "CHANNEL_SETTINGS_PERMISSIONS_INIT" | "CHANNEL_SETTINGS_PERMISSIONS_SAVE_SUCCESS" | "CHANNEL_SETTINGS_PERMISSIONS_SELECT_PERMISSION" | "CHANNEL_SETTINGS_PERMISSIONS_SET_ADVANCED_MODE" | "CHANNEL_SETTINGS_PERMISSIONS_SUBMITTING" | "CHANNEL_SETTINGS_PERMISSIONS_UPDATE_PERMISSION" | "CHANNEL_SETTINGS_SET_SECTION" | "CHANNEL_SETTINGS_SUBMIT" | "CHANNEL_SETTINGS_SUBMIT_FAILURE" | "CHANNEL_SETTINGS_SUBMIT_SUCCESS" | "CHANNEL_SETTINGS_UPDATE" | "CHANNEL_STATUSES" | "CHANNEL_TOGGLE_MEMBERS_SECTION" | "CHANNEL_TOGGLE_SUMMARIES_SECTION" | "CHANNEL_UPDATES" | "CHECKING_FOR_UPDATES" | "CHECKOUT_RECOVERY_STATUS_FETCH" | "CHECKOUT_RECOVERY_STATUS_FETCH_FAILURE" | "CHECKOUT_RECOVERY_STATUS_FETCH_SUCCESS" | "CHECK_LAUNCHABLE_GAME" | "CLAN_SETTINGS_FETCH_START" | "CLAN_SETTINGS_FETCH_SUCCESS" | "CLAN_SETTINGS_SUBMIT" | "CLAN_SETTINGS_SUBMIT_ERROR" | "CLAN_SETTINGS_SUBMIT_SUCCESS" | "CLAN_SETTINGS_UPDATE" | "CLAN_SETUP_ERROR" | "CLAN_SETUP_RESET" | "CLAN_SETUP_SUBMIT" | "CLAN_SETUP_SUCCESS" | "CLAN_SETUP_UPDATE" | "CLEAR_AUTHENTICATION_ERRORS" | "CLEAR_CACHES" | "CLEAR_CHANNEL_SAFETY_WARNINGS" | "CLEAR_CONVERSATION_SUMMARIES" | "CLEAR_HANG_STATUS" | "CLEAR_INTERACTION_MODAL_STATE" | "CLEAR_LAST_SESSION_VOICE_CHANNEL_ID" | "CLEAR_MENTIONS" | "CLEAR_MESSAGES" | "CLEAR_OLDEST_UNREAD_MESSAGE" | "CLEAR_PENDING_CHANNEL_AND_ROLE_UPDATES" | "CLEAR_REMOTE_DISCONNECT_VOICE_CHANNEL_ID" | "CLEAR_STICKER_PREVIEW" | "CLIENT_THEMES_EDITOR_CLOSE" | "CLIENT_THEMES_EDITOR_OPEN" | "CLIPS_CLASSIFY_HARDWARE" | "CLIPS_CLEAR_CLIPS_SESSION" | "CLIPS_CLEAR_NEW_CLIP_IDS" | "CLIPS_DELETE_CLIP" | "CLIPS_DISMISS_EDUCATION" | "CLIPS_INIT" | "CLIPS_INIT_FAILURE" | "CLIPS_LOAD_DIRECTORY_SUCCESS" | "CLIPS_RESTART" | "CLIPS_SAVE_ANIMATION_END" | "CLIPS_SAVE_CLIP" | "CLIPS_SAVE_CLIP_ERROR" | "CLIPS_SAVE_CLIP_PLACEHOLDER" | "CLIPS_SAVE_CLIP_PLACEHOLDER_ERROR" | "CLIPS_SAVE_CLIP_START" | "CLIPS_SETTINGS_UPDATE" | "CLIPS_SHOW_CALL_WARNING" | "CLIPS_UPDATE_METADATA" | "CLOSE_SUSPENDED_USER" | "COLLECTIBLES_CATEGORIES_FETCH" | "COLLECTIBLES_CATEGORIES_FETCH_FAILURE" | "COLLECTIBLES_CATEGORIES_FETCH_SUCCESS" | "COLLECTIBLES_CATEGORY_ITEMS_VIEWED" | "COLLECTIBLES_CLAIM" | "COLLECTIBLES_CLAIM_FAILURE" | "COLLECTIBLES_CLAIM_SUCCESS" | "COLLECTIBLES_MARKETING_FETCH" | "COLLECTIBLES_MARKETING_FETCH_SUCCESS" | "COLLECTIBLES_PRODUCT_DETAILS_OPEN" | "COLLECTIBLES_PRODUCT_FETCH" | "COLLECTIBLES_PRODUCT_FETCH_FAILURE" | "COLLECTIBLES_PRODUCT_FETCH_SUCCESS" | "COLLECTIBLES_PURCHASES_FETCH" | "COLLECTIBLES_PURCHASES_FETCH_FAILURE" | "COLLECTIBLES_PURCHASES_FETCH_SUCCESS" | "COLLECTIBLES_SHOP_CLOSE" | "COLLECTIBLES_SHOP_OPEN" | "COMMANDS_MIGRATION_NOTICE_DISMISSED" | "COMMANDS_MIGRATION_OVERVIEW_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_TOGGLE_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_UPDATE_SUCCESS" | "COMPLETE_NEW_MEMBER_ACTION" | "COMPLETE_SIGN_UP" | "CONNECTED_DEVICE_IGNORE" | "CONNECTED_DEVICE_NEVER_SHOW_MODAL" | "CONNECTED_DEVICE_SET" | "CONNECTIONS_GRID_MODAL_HIDE" | "CONNECTIONS_GRID_MODAL_SHOW" | "CONNECTION_CLOSED" | "CONNECTION_INTERRUPTED" | "CONNECTION_OPEN" | "CONNECTION_OPEN_SUPPLEMENTAL" | "CONNECTION_RESUMED" | "CONSOLE_COMMAND_UPDATE" | "CONTENT_INVENTORY_CLEAR_FEED" | "CONTENT_INVENTORY_DEBUG_CLEAR_IMPRESSIONS" | "CONTENT_INVENTORY_DEBUG_LOG_IMPRESSIONS" | "CONTENT_INVENTORY_DEBUG_TOGGLE_FAST_IMPRESSION_CAPPING" | "CONTENT_INVENTORY_DEBUG_TOGGLE_IMPRESSION_CAPPING" | "CONTENT_INVENTORY_DELETE_OUTBOX_ENTRY" | "CONTENT_INVENTORY_INBOX_STALE" | "CONTENT_INVENTORY_MANUAL_REFRESH" | "CONTENT_INVENTORY_SET_FEED" | "CONTENT_INVENTORY_SET_FEED_STATE" | "CONTENT_INVENTORY_SET_FILTERS" | "CONTENT_INVENTORY_SET_USER_OUTBOX" | "CONTENT_INVENTORY_TOGGLE_FEED_HIDDEN" | "CONTENT_INVENTORY_TRACK_ITEM_IMPRESSIONS" | "CONTEXT_MENU_CLOSE" | "CONTEXT_MENU_OPEN" | "CONVERSATION_SUMMARY_UPDATE" | "CREATE_PENDING_REPLY" | "CREATE_REFERRALS_SUCCESS" | "CREATE_SHALLOW_PENDING_REPLY" | "CREATOR_MONETIZATION_NAG_ACTIVATE_ELIGIBLITY_FETCH_SUCCESS" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH_FAILURE" | "CREATOR_MONETIZATION_PRICE_TIERS_FETCH_SUCCESS" | "CURRENT_BUILD_OVERRIDE_RESOLVED" | "CURRENT_USER_UPDATE" | "DCF_DAILY_CAP_OVERRIDE" | "DCF_HANDLE_DC_DISMISSED" | "DCF_HANDLE_DC_SHOWN" | "DCF_RESET" | "DECAY_READ_STATES" | "DELETED_ENTITY_IDS" | "DELETE_INVALID_HANG_STATUSES" | "DELETE_PENDING_REPLY" | "DELETE_SUMMARY" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH_FAILURE" | "DETECTABLE_GAME_SUPPLEMENTAL_FETCH_SUCCESS" | "DETECTED_OFF_PLATFORM_PREMIUM_PERKS_DISMISS" | "DEVELOPER_ACTIVITY_SHELF_FETCH_FAIL" | "DEVELOPER_ACTIVITY_SHELF_FETCH_START" | "DEVELOPER_ACTIVITY_SHELF_FETCH_SUCCESS" | "DEVELOPER_ACTIVITY_SHELF_MARK_ACTIVITY_USED" | "DEVELOPER_ACTIVITY_SHELF_SET_ACTIVITY_URL_OVERRIDE" | "DEVELOPER_ACTIVITY_SHELF_TOGGLE_USE_ACTIVITY_URL_OVERRIDE" | "DEVELOPER_ACTIVITY_SHELF_UPDATE_FILTER" | "DEVELOPER_OPTIONS_UPDATE_SETTINGS" | "DEVELOPER_TEST_MODE_AUTHORIZATION_FAIL" | "DEVELOPER_TEST_MODE_AUTHORIZATION_START" | "DEVELOPER_TEST_MODE_AUTHORIZATION_SUCCESS" | "DEVELOPER_TEST_MODE_RESET" | "DEVELOPER_TEST_MODE_RESET_ERROR" | "DEV_TOOLS_DESIGN_TOGGLE_SET" | "DEV_TOOLS_DESIGN_TOGGLE_WEB_SET" | "DEV_TOOLS_DEV_SETTING_SET" | "DEV_TOOLS_SETTINGS_UPDATE" | "DISABLE_AUTOMATIC_ACK" | "DISCOVER_CHECKLIST_FETCH_FAILURE" | "DISCOVER_CHECKLIST_FETCH_START" | "DISCOVER_CHECKLIST_FETCH_SUCCESS" | "DISCOVER_GUILDS_FETCH_FAILURE" | "DISCOVER_GUILDS_FETCH_START" | "DISCOVER_GUILDS_FETCH_SUCCESS" | "DISMISS_CHANNEL_SAFETY_WARNINGS" | "DISMISS_FAVORITE_SUGGESTION" | "DISMISS_MEDIA_POST_SHARE_PROMPT" | "DISMISS_SIGN_UP" | "DISPATCH_APPLICATION_ADD_TO_INSTALLATIONS" | "DISPATCH_APPLICATION_CANCEL" | "DISPATCH_APPLICATION_ERROR" | "DISPATCH_APPLICATION_INSTALL" | "DISPATCH_APPLICATION_INSTALL_SCRIPTS_PROGRESS_UPDATE" | "DISPATCH_APPLICATION_LAUNCH_SETUP_COMPLETE" | "DISPATCH_APPLICATION_LAUNCH_SETUP_START" | "DISPATCH_APPLICATION_MOVE_UP" | "DISPATCH_APPLICATION_REMOVE_FINISHED" | "DISPATCH_APPLICATION_REPAIR" | "DISPATCH_APPLICATION_STATE_UPDATE" | "DISPATCH_APPLICATION_UNINSTALL" | "DISPATCH_APPLICATION_UPDATE" | "DISPLAYED_INVITE_SHOW" | "DOMAIN_MIGRATION_FAILURE" | "DOMAIN_MIGRATION_SKIP" | "DOMAIN_MIGRATION_START" | "DRAFT_CHANGE" | "DRAFT_CLEAR" | "DRAFT_SAVE" | "DRAWER_CLOSE" | "DRAWER_OPEN" | "DRAWER_SELECT_TAB" | "DROPS_ELIGIBILITY_FETCH_SUCCESS" | "DROPS_ENROLLED_USER_FETCH_SUCCESS" | "DROPS_FETCH_PROGRESS_FAILURE" | "DROPS_FETCH_PROGRESS_SUCCESS" | "DROPS_HEARTBEAT_FAILURE" | "DROPS_HEARTBEAT_SUCCESS" | "DROPS_PLATFORM_AVAILABILITY_SUCCESS" | "DROPS_UNENROLL_USER" | "DROPS_USER_STATUS_FETCH_FAILURE" | "DROPS_USER_STATUS_FETCH_SUCCESS" | "EMAIL_SETTINGS_FETCH_SUCCESS" | "EMAIL_SETTINGS_UPDATE" | "EMAIL_SETTINGS_UPDATE_SUCCESS" | "EMBEDDED_ACTIVITY_CLOSE" | "EMBEDDED_ACTIVITY_DEFERRED_OPEN" | "EMBEDDED_ACTIVITY_DISCONNECT" | "EMBEDDED_ACTIVITY_DISMISS_NEW_INDICATOR" | "EMBEDDED_ACTIVITY_FETCH_SHELF" | "EMBEDDED_ACTIVITY_FETCH_SHELF_FAIL" | "EMBEDDED_ACTIVITY_FETCH_SHELF_SUCCESS" | "EMBEDDED_ACTIVITY_LAUNCH_FAIL" | "EMBEDDED_ACTIVITY_LAUNCH_START" | "EMBEDDED_ACTIVITY_LAUNCH_SUCCESS" | "EMBEDDED_ACTIVITY_OPEN" | "EMBEDDED_ACTIVITY_SET_CONFIG" | "EMBEDDED_ACTIVITY_SET_FOCUSED_LAYOUT" | "EMBEDDED_ACTIVITY_SET_ORIENTATION_LOCK_STATE" | "EMBEDDED_ACTIVITY_SET_PANEL_MODE" | "EMBEDDED_ACTIVITY_UPDATE" | "EMBEDDED_ACTIVITY_UPDATE_V2" | "EMOJI_AUTOSUGGESTION_UPDATE" | "EMOJI_CAPTIONS_FETCH" | "EMOJI_CAPTIONS_FETCH_ERROR" | "EMOJI_CAPTIONS_FETCH_SUCCESS" | "EMOJI_DELETE" | "EMOJI_FETCH_FAILURE" | "EMOJI_FETCH_SUCCESS" | "EMOJI_INTERACTION_INITIATED" | "EMOJI_TRACK_USAGE" | "EMOJI_UPLOAD_START" | "EMOJI_UPLOAD_STOP" | "ENABLE_AUTOMATIC_ACK" | "ENABLE_GUILD_SIGN_UP" | "ENABLE_USER_SIGN_UP" | "ENTITLEMENTS_FETCH_FOR_USER_FAIL" | "ENTITLEMENTS_FETCH_FOR_USER_START" | "ENTITLEMENTS_FETCH_FOR_USER_SUCCESS" | "ENTITLEMENTS_GIFTABLE_FETCH_SUCCESS" | "ENTITLEMENT_CREATE" | "ENTITLEMENT_DELETE" | "ENTITLEMENT_FETCH_APPLICATION_FAIL" | "ENTITLEMENT_FETCH_APPLICATION_START" | "ENTITLEMENT_FETCH_APPLICATION_SUCCESS" | "ENTITLEMENT_UPDATE" | "EVENT_DIRECTORY_FETCH_FAILURE" | "EVENT_DIRECTORY_FETCH_START" | "EVENT_DIRECTORY_FETCH_SUCCESS" | "EXPERIMENTS_FETCH" | "EXPERIMENTS_FETCH_FAILURE" | "EXPERIMENTS_FETCH_SUCCESS" | "EXPERIMENT_OVERRIDE_BUCKET" | "FAMILY_CENTER_FETCH_START" | "FAMILY_CENTER_HANDLE_TAB_SELECT" | "FAMILY_CENTER_INITIAL_LOAD" | "FAMILY_CENTER_LINKED_USERS_FETCH_SUCCESS" | "FAMILY_CENTER_LINK_CODE_FETCH_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_REMOVE_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_SUCCESS" | "FAMILY_CENTER_REQUEST_LINK_UPDATE_SUCCESS" | "FAMILY_CENTER_TEEN_ACTIVITY_FETCH_SUCCESS" | "FAMILY_CENTER_TEEN_ACTIVITY_MORE_FETCH_SUCCESS" | "FETCH_AUTH_SESSIONS_SUCCESS" | "FETCH_CLAN_DISCOVERY_SEARCH_RESULT_SUCCESS" | "FETCH_GUILD_EVENT" | "FETCH_GUILD_EVENTS_FOR_GUILD" | "FETCH_GUILD_MEMBER_SUPPLEMENTAL_SUCCESS" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS_FAILURE" | "FETCH_INTEGRATION_APPLICATION_IDS_FOR_MY_GUILDS_SUCCESS" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_FAIL" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_START" | "FETCH_PRIVATE_CHANNEL_INTEGRATIONS_SUCCESS" | "FETCH_STATIC_CLAN_LIST_FAILURE" | "FETCH_STATIC_CLAN_LIST_START" | "FETCH_STATIC_CLAN_LIST_SUCCESS" | "FINGERPRINT" | "FORCE_INVISIBLE" | "FORGOT_PASSWORD_REQUEST" | "FORGOT_PASSWORD_SENT" | "FORUM_SEARCH_CLEAR" | "FORUM_SEARCH_FAILURE" | "FORUM_SEARCH_QUERY_UPDATED" | "FORUM_SEARCH_START" | "FORUM_SEARCH_SUCCESS" | "FORUM_UNREADS" | "FRIENDS_SET_INITIAL_SECTION" | "FRIENDS_SET_SECTION" | "FRIEND_INVITES_FETCH_REQUEST" | "FRIEND_INVITES_FETCH_RESPONSE" | "FRIEND_INVITE_CREATE_FAILURE" | "FRIEND_INVITE_CREATE_REQUEST" | "FRIEND_INVITE_CREATE_SUCCESS" | "FRIEND_INVITE_REVOKE_REQUEST" | "FRIEND_INVITE_REVOKE_SUCCESS" | "FRIEND_SUGGESTION_CREATE" | "FRIEND_SUGGESTION_DELETE" | "GAMES_DATABASE_FETCH" | "GAMES_DATABASE_FETCH_FAIL" | "GAMES_DATABASE_UPDATE" | "GAME_CLOUD_SYNC_COMPLETE" | "GAME_CLOUD_SYNC_CONFLICT" | "GAME_CLOUD_SYNC_ERROR" | "GAME_CLOUD_SYNC_START" | "GAME_CLOUD_SYNC_UPDATE" | "GAME_CONSOLE_FETCH_DEVICES_FAIL" | "GAME_CONSOLE_FETCH_DEVICES_START" | "GAME_CONSOLE_FETCH_DEVICES_SUCCESS" | "GAME_CONSOLE_SELECT_DEVICE" | "GAME_DETECTION_DEBUGGING_START" | "GAME_DETECTION_DEBUGGING_STOP" | "GAME_DETECTION_DEBUGGING_TICK" | "GAME_DETECTION_WATCH_CANDIDATE_GAMES_START" | "GAME_ICON_UPDATE" | "GAME_INVITE_CLEAR_UNSEEN" | "GAME_INVITE_CREATE" | "GAME_INVITE_DELETE" | "GAME_INVITE_DELETE_MANY" | "GAME_INVITE_UPDATE_STATUS" | "GAME_LAUNCHABLE_UPDATE" | "GAME_LAUNCH_FAIL" | "GAME_LAUNCH_START" | "GAME_LAUNCH_SUCCESS" | "GAME_PROFILE_OPEN" | "GENERIC_PUSH_NOTIFICATION_SENT" | "GIFT_CODES_FETCH" | "GIFT_CODES_FETCH_FAILURE" | "GIFT_CODES_FETCH_SUCCESS" | "GIFT_CODE_CREATE" | "GIFT_CODE_CREATE_SUCCESS" | "GIFT_CODE_REDEEM" | "GIFT_CODE_REDEEM_FAILURE" | "GIFT_CODE_REDEEM_SUCCESS" | "GIFT_CODE_RESOLVE" | "GIFT_CODE_RESOLVE_FAILURE" | "GIFT_CODE_RESOLVE_SUCCESS" | "GIFT_CODE_REVOKE_SUCCESS" | "GIFT_CODE_UPDATE" | "GIF_PICKER_INITIALIZE" | "GIF_PICKER_QUERY" | "GIF_PICKER_QUERY_FAILURE" | "GIF_PICKER_QUERY_SUCCESS" | "GIF_PICKER_SUGGESTIONS_SUCCESS" | "GIF_PICKER_TRENDING_FETCH_SUCCESS" | "GIF_PICKER_TRENDING_SEARCH_TERMS_SUCCESS" | "GRAVITY_ACK_ITEMS" | "GRAVITY_CUSTOM_SCORES_UPDATED" | "GRAVITY_FEEDBACK_GIVEN" | "GRAVITY_TAB_OPENED" | "GUILD_ACK" | "GUILD_ANALYTICS_ENGAGEMENT_OVERVIEW_FETCH_FAILURE" | "GUILD_ANALYTICS_ENGAGEMENT_OVERVIEW_FETCH_SUCCESS" | "GUILD_ANALYTICS_GROWTH_ACTIVATION_OVERVIEW_FETCH_FAILURE" | "GUILD_ANALYTICS_GROWTH_ACTIVATION_OVERVIEW_FETCH_SUCCESS" | "GUILD_ANALYTICS_GROWTH_ACTIVATION_RETENTION_FETCH_FAILURE" | "GUILD_ANALYTICS_GROWTH_ACTIVATION_RETENTION_FETCH_SUCCESS" | "GUILD_ANALYTICS_MEMBER_INSIGHTS_FETCH_SUCCESS" | "GUILD_APPLICATIONS_FETCH_SUCCESS" | "GUILD_APPLICATION_COMMAND_INDEX_UPDATE" | "GUILD_APPLIED_BOOSTS_FETCH_SUCCESS" | "GUILD_APPLY_BOOST_FAIL" | "GUILD_APPLY_BOOST_START" | "GUILD_APPLY_BOOST_SUCCESS" | "GUILD_BAN_ADD" | "GUILD_BAN_REMOVE" | "GUILD_BOOST_SLOTS_FETCH_SUCCESS" | "GUILD_BOOST_SLOT_CREATE" | "GUILD_BOOST_SLOT_UPDATE" | "GUILD_BOOST_SLOT_UPDATE_SUCCESS" | "GUILD_CREATE" | "GUILD_DELETE" | "GUILD_DIRECTORY_ADMIN_ENTRIES_FETCH_SUCCESS" | "GUILD_DIRECTORY_CACHED_SEARCH" | "GUILD_DIRECTORY_CATEGORY_SELECT" | "GUILD_DIRECTORY_COUNTS_FETCH_SUCCESS" | "GUILD_DIRECTORY_ENTRY_CREATE" | "GUILD_DIRECTORY_ENTRY_DELETE" | "GUILD_DIRECTORY_ENTRY_UPDATE" | "GUILD_DIRECTORY_FETCH_FAILURE" | "GUILD_DIRECTORY_FETCH_START" | "GUILD_DIRECTORY_FETCH_SUCCESS" | "GUILD_DIRECTORY_SEARCH_CLEAR" | "GUILD_DIRECTORY_SEARCH_FAILURE" | "GUILD_DIRECTORY_SEARCH_START" | "GUILD_DIRECTORY_SEARCH_SUCCESS" | "GUILD_DISCOVERY_CATEGORY_ADD" | "GUILD_DISCOVERY_CATEGORY_DELETE" | "GUILD_DISCOVERY_CATEGORY_FETCH_SUCCESS" | "GUILD_DISCOVERY_CATEGORY_UPDATE_FAIL" | "GUILD_DISCOVERY_CLEAR_SEARCH" | "GUILD_DISCOVERY_CLEAR_SEEN_GUILDS" | "GUILD_DISCOVERY_FETCH_FAILURE" | "GUILD_DISCOVERY_FETCH_START" | "GUILD_DISCOVERY_FETCH_SUCCESS" | "GUILD_DISCOVERY_GUILD_SEEN" | "GUILD_DISCOVERY_METADATA_FETCH_FAIL" | "GUILD_DISCOVERY_POPULAR_FETCH_FAILURE" | "GUILD_DISCOVERY_POPULAR_FETCH_START" | "GUILD_DISCOVERY_POPULAR_FETCH_SUCCESS" | "GUILD_DISCOVERY_SEARCH_COUNTS_FAIL" | "GUILD_DISCOVERY_SEARCH_FETCH_FAILURE" | "GUILD_DISCOVERY_SEARCH_FETCH_START" | "GUILD_DISCOVERY_SEARCH_FETCH_SUCCESS" | "GUILD_DISCOVERY_SEARCH_INIT" | "GUILD_DISCOVERY_SEARCH_UPDATE_COUNTS" | "GUILD_DISCOVERY_SELECT_CATEGORY" | "GUILD_DISCOVERY_SLUG_FETCH_FAIL" | "GUILD_DISCOVERY_SLUG_FETCH_SUCCESS" | "GUILD_EMOJIS_UPDATE" | "GUILD_FEATURE_ACK" | "GUILD_FOLDER_COLLAPSE" | "GUILD_FOLDER_CREATE_LOCAL" | "GUILD_FOLDER_DELETE_LOCAL" | "GUILD_FOLDER_EDIT_LOCAL" | "GUILD_GEO_RESTRICTED" | "GUILD_HOME_SETTINGS_FETCH_FAIL" | "GUILD_HOME_SETTINGS_FETCH_START" | "GUILD_HOME_SETTINGS_FETCH_SUCCESS" | "GUILD_HOME_SETTINGS_TOGGLE_ENABLED" | "GUILD_HOME_SETTINGS_UPDATE_FAIL" | "GUILD_HOME_SETTINGS_UPDATE_START" | "GUILD_HOME_SETTINGS_UPDATE_SUCCESS" | "GUILD_IDENTITY_SETTINGS_CLEAR_ERRORS" | "GUILD_IDENTITY_SETTINGS_INIT" | "GUILD_IDENTITY_SETTINGS_RESET_ALL_PENDING" | "GUILD_IDENTITY_SETTINGS_RESET_AND_CLOSE_FORM" | "GUILD_IDENTITY_SETTINGS_RESET_PENDING_MEMBER_CHANGES" | "GUILD_IDENTITY_SETTINGS_RESET_PENDING_PROFILE_CHANGES" | "GUILD_IDENTITY_SETTINGS_SET_GUILD" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_AVATAR" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_AVATAR_DECORATION" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BANNER" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BIO" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_NICKNAME" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_PROFILE_EFFECT_ID" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_PRONOUNS" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_THEME_COLORS" | "GUILD_IDENTITY_SETTINGS_SUBMIT" | "GUILD_IDENTITY_SETTINGS_SUBMIT_FAILURE" | "GUILD_IDENTITY_SETTINGS_SUBMIT_SUCCESS" | "GUILD_INTEGRATIONS_UPDATE" | "GUILD_JOIN" | "GUILD_JOIN_REQUESTS_BULK_ACTION" | "GUILD_JOIN_REQUESTS_FETCH_FAILURE" | "GUILD_JOIN_REQUESTS_FETCH_START" | "GUILD_JOIN_REQUESTS_FETCH_SUCCESS" | "GUILD_JOIN_REQUESTS_SET_APPLICATION_TAB" | "GUILD_JOIN_REQUESTS_SET_SELECTED" | "GUILD_JOIN_REQUESTS_SET_SORT_ORDER" | "GUILD_JOIN_REQUEST_BY_ID_FETCH_SUCCESS" | "GUILD_JOIN_REQUEST_CREATE" | "GUILD_JOIN_REQUEST_DELETE" | "GUILD_JOIN_REQUEST_UPDATE" | "GUILD_MEMBERS_CHUNK_BATCH" | "GUILD_MEMBERS_REQUEST" | "GUILD_MEMBER_ADD" | "GUILD_MEMBER_LIST_UPDATE" | "GUILD_MEMBER_PROFILE_UPDATE" | "GUILD_MEMBER_REMOVE" | "GUILD_MEMBER_UPDATE" | "GUILD_MEMBER_UPDATE_LOCAL" | "GUILD_MOVE_BY_ID" | "GUILD_MUTE_EXPIRED" | "GUILD_NEW_MEMBER_ACTIONS_DELETE_SUCCESS" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_FAIL" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_START" | "GUILD_NEW_MEMBER_ACTIONS_FETCH_SUCCESS" | "GUILD_NEW_MEMBER_ACTION_UPDATE_SUCCESS" | "GUILD_NSFW_AGREE" | "GUILD_ONBOARDING_COMPLETE" | "GUILD_ONBOARDING_PROMPTS_FETCH_FAILURE" | "GUILD_ONBOARDING_PROMPTS_FETCH_START" | "GUILD_ONBOARDING_PROMPTS_FETCH_SUCCESS" | "GUILD_ONBOARDING_PROMPTS_LOCAL_UPDATE" | "GUILD_ONBOARDING_SELECT_OPTION" | "GUILD_ONBOARDING_SET_STEP" | "GUILD_ONBOARDING_START" | "GUILD_ONBOARDING_UPDATE_RESPONSES_SUCCESS" | "GUILD_POPOUT_FETCH_FAILURE" | "GUILD_POPOUT_FETCH_START" | "GUILD_POPOUT_FETCH_SUCCESS" | "GUILD_PRODUCTS_FETCH" | "GUILD_PRODUCTS_FETCH_FAILURE" | "GUILD_PRODUCTS_FETCH_SUCCESS" | "GUILD_PRODUCT_CREATE" | "GUILD_PRODUCT_DELETE" | "GUILD_PRODUCT_FETCH" | "GUILD_PRODUCT_FETCH_FAILURE" | "GUILD_PRODUCT_FETCH_SUCCESS" | "GUILD_PRODUCT_UPDATE" | "GUILD_PROGRESS_COMPLETED_SEEN" | "GUILD_PROGRESS_DISMISS" | "GUILD_PROGRESS_INITIALIZE" | "GUILD_PROMPT_VIEWED" | "GUILD_RECOMMENDATION_FETCH" | "GUILD_RECOMMENDATION_FETCH_FAILURE" | "GUILD_RECOMMENDATION_FETCH_SUCCESS" | "GUILD_RESOURCE_CHANNEL_UPDATE_SUCCESS" | "GUILD_ROLE_CONNECTIONS_CONFIGURATIONS_FETCH_SUCCESS" | "GUILD_ROLE_CONNECTION_ELIGIBILITY_FETCH_SUCCESS" | "GUILD_ROLE_CREATE" | "GUILD_ROLE_DELETE" | "GUILD_ROLE_MEMBER_ADD" | "GUILD_ROLE_MEMBER_BULK_ADD" | "GUILD_ROLE_MEMBER_COUNT_FETCH_SUCCESS" | "GUILD_ROLE_MEMBER_COUNT_UPDATE" | "GUILD_ROLE_MEMBER_REMOVE" | "GUILD_ROLE_SUBSCRIPTIONS_CREATE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_DELETE_GROUP_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_DELETE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS_FAILURE" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTINGS_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_LISTING_FOR_PLAN_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_ABORTED" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_FAILURE" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_RESTRICTIONS_SUCCESS" | "GUILD_ROLE_SUBSCRIPTIONS_FETCH_TEMPLATES" | "GUILD_ROLE_SUBSCRIPTIONS_STASH_TEMPLATE_CHANNELS" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_GROUP_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_LISTING" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_SUBSCRIPTIONS_SETTINGS" | "GUILD_ROLE_SUBSCRIPTIONS_UPDATE_SUBSCRIPTION_TRIAL" | "GUILD_ROLE_UPDATE" | "GUILD_SCHEDULED_EVENT_CREATE" | "GUILD_SCHEDULED_EVENT_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTIONS_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_CREATE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_DELETE" | "GUILD_SCHEDULED_EVENT_EXCEPTION_UPDATE" | "GUILD_SCHEDULED_EVENT_RSVPS_FETCH_SUCESS" | "GUILD_SCHEDULED_EVENT_UPDATE" | "GUILD_SCHEDULED_EVENT_USERS_FETCH_SUCCESS" | "GUILD_SCHEDULED_EVENT_USER_ADD" | "GUILD_SCHEDULED_EVENT_USER_COUNTS_FETCH_SUCCESS" | "GUILD_SCHEDULED_EVENT_USER_REMOVE" | "GUILD_SEARCH_RECENT_MEMBERS" | "GUILD_SETTINGS_CANCEL_CHANGES" | "GUILD_SETTINGS_CLOSE" | "GUILD_SETTINGS_DEFAULT_CHANNELS_RESET" | "GUILD_SETTINGS_DEFAULT_CHANNELS_SAVE_FAILED" | "GUILD_SETTINGS_DEFAULT_CHANNELS_SAVE_SUCCESS" | "GUILD_SETTINGS_DEFAULT_CHANNELS_SUBMIT" | "GUILD_SETTINGS_DEFAULT_CHANNELS_TOGGLE" | "GUILD_SETTINGS_INIT" | "GUILD_SETTINGS_LOADED_BANS" | "GUILD_SETTINGS_LOADED_BANS_BATCH" | "GUILD_SETTINGS_LOADED_INTEGRATIONS" | "GUILD_SETTINGS_LOADED_INVITES" | "GUILD_SETTINGS_ONBOARDING_ADD_NEW_MEMBER_ACTION" | "GUILD_SETTINGS_ONBOARDING_ADD_RESOURCE_CHANNEL" | "GUILD_SETTINGS_ONBOARDING_DELETE_NEW_MEMBER_ACTION" | "GUILD_SETTINGS_ONBOARDING_DELETE_RESOURCE_CHANNEL" | "GUILD_SETTINGS_ONBOARDING_DISMISS_RESOURCE_CHANNEL_SUGGESTION" | "GUILD_SETTINGS_ONBOARDING_EDUCATION_UPSELL_DISMISSED" | "GUILD_SETTINGS_ONBOARDING_HOME_SETTINGS_RESET" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_EDIT" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_ERRORS" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_RESET" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_SAVE_FAILED" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_SAVE_SUCCESS" | "GUILD_SETTINGS_ONBOARDING_PROMPTS_SUBMIT" | "GUILD_SETTINGS_ONBOARDING_REORDER_NEW_MEMBER_ACTION" | "GUILD_SETTINGS_ONBOARDING_REORDER_RESOURCE_CHANNEL" | "GUILD_SETTINGS_ONBOARDING_SET_MODE" | "GUILD_SETTINGS_ONBOARDING_STEP" | "GUILD_SETTINGS_ONBOARDING_UPDATE_NEW_MEMBER_ACTION" | "GUILD_SETTINGS_ONBOARDING_UPDATE_RESOURCE_CHANNEL" | "GUILD_SETTINGS_ONBOARDING_UPDATE_WELCOME_MESSAGE" | "GUILD_SETTINGS_OPEN" | "GUILD_SETTINGS_ROLES_CLEAR_PERMISSIONS" | "GUILD_SETTINGS_ROLES_INIT" | "GUILD_SETTINGS_ROLES_SAVE_FAIL" | "GUILD_SETTINGS_ROLES_SAVE_SUCCESS" | "GUILD_SETTINGS_ROLES_SORT_UPDATE" | "GUILD_SETTINGS_ROLES_SUBMITTING" | "GUILD_SETTINGS_ROLES_UPDATE_COLOR" | "GUILD_SETTINGS_ROLES_UPDATE_DESCRIPTION" | "GUILD_SETTINGS_ROLES_UPDATE_NAME" | "GUILD_SETTINGS_ROLES_UPDATE_PERMISSIONS" | "GUILD_SETTINGS_ROLES_UPDATE_PERMISSION_SET" | "GUILD_SETTINGS_ROLES_UPDATE_ROLE_CONNECTION_CONFIGURATIONS" | "GUILD_SETTINGS_ROLES_UPDATE_ROLE_ICON" | "GUILD_SETTINGS_ROLES_UPDATE_SETTINGS" | "GUILD_SETTINGS_ROLE_SELECT" | "GUILD_SETTINGS_SAFETY_PAGE" | "GUILD_SETTINGS_SAFETY_SET_SUBSECTION" | "GUILD_SETTINGS_SAVE_ROUTE_STACK" | "GUILD_SETTINGS_SET_MFA_SUCCESS" | "GUILD_SETTINGS_SET_SEARCH_QUERY" | "GUILD_SETTINGS_SET_SECTION" | "GUILD_SETTINGS_SET_VANITY_URL" | "GUILD_SETTINGS_SET_WIDGET" | "GUILD_SETTINGS_SUBMIT" | "GUILD_SETTINGS_SUBMIT_FAILURE" | "GUILD_SETTINGS_SUBMIT_SUCCESS" | "GUILD_SETTINGS_UPDATE" | "GUILD_SETTINGS_VANITY_URL_CLOSE" | "GUILD_SETTINGS_VANITY_URL_ERROR" | "GUILD_SETTINGS_VANITY_URL_RESET" | "GUILD_SETTINGS_VANITY_URL_SET" | "GUILD_SOUNDBOARD_FETCH" | "GUILD_SOUNDBOARD_SOUNDS_UPDATE" | "GUILD_SOUNDBOARD_SOUND_CREATE" | "GUILD_SOUNDBOARD_SOUND_DELETE" | "GUILD_SOUNDBOARD_SOUND_PLAY_END" | "GUILD_SOUNDBOARD_SOUND_PLAY_LOCALLY" | "GUILD_SOUNDBOARD_SOUND_PLAY_START" | "GUILD_SOUNDBOARD_SOUND_UPDATE" | "GUILD_STICKERS_CREATE_SUCCESS" | "GUILD_STICKERS_FETCH_SUCCESS" | "GUILD_STICKERS_UPDATE" | "GUILD_STOP_LURKING" | "GUILD_STOP_LURKING_FAILURE" | "GUILD_SUBSCRIPTIONS" | "GUILD_SUBSCRIPTIONS_ADD_MEMBER_UPDATES" | "GUILD_SUBSCRIPTIONS_CHANNEL" | "GUILD_SUBSCRIPTIONS_FLUSH" | "GUILD_SUBSCRIPTIONS_MEMBERS_ADD" | "GUILD_SUBSCRIPTIONS_MEMBERS_REMOVE" | "GUILD_SUBSCRIPTIONS_REMOVE_MEMBER_UPDATES" | "GUILD_TEMPLATE_ACCEPT" | "GUILD_TEMPLATE_ACCEPT_FAILURE" | "GUILD_TEMPLATE_ACCEPT_SUCCESS" | "GUILD_TEMPLATE_CREATE_SUCCESS" | "GUILD_TEMPLATE_DELETE_SUCCESS" | "GUILD_TEMPLATE_DIRTY_TOOLTIP_HIDE" | "GUILD_TEMPLATE_DIRTY_TOOLTIP_REFRESH" | "GUILD_TEMPLATE_LOAD_FOR_GUILD_SUCCESS" | "GUILD_TEMPLATE_MODAL_HIDE" | "GUILD_TEMPLATE_MODAL_SHOW" | "GUILD_TEMPLATE_PROMOTION_TOOLTIP_HIDE" | "GUILD_TEMPLATE_RESOLVE" | "GUILD_TEMPLATE_RESOLVE_FAILURE" | "GUILD_TEMPLATE_RESOLVE_SUCCESS" | "GUILD_TEMPLATE_SYNC_SUCCESS" | "GUILD_TOGGLE_COLLAPSE_MUTED" | "GUILD_TOP_READ_CHANNELS_FETCH_SUCCESS" | "GUILD_UNAPPLY_BOOST_FAIL" | "GUILD_UNAPPLY_BOOST_START" | "GUILD_UNAPPLY_BOOST_SUCCESS" | "GUILD_UNAVAILABLE" | "GUILD_UPDATE" | "GUILD_UPDATE_DISCOVERY_METADATA" | "GUILD_UPDATE_DISCOVERY_METADATA_FAIL" | "GUILD_UPDATE_DISCOVERY_METADATA_FROM_SERVER" | "GUILD_VERIFICATION_CHECK" | "HABITUAL_DND_CLEAR" | "HIDE_ACTION_SHEET" | "HIDE_ACTION_SHEET_QUICK_SWITCHER" | "HIDE_KEYBOARD_SHORTCUTS" | "HIGH_FIVE_COMPLETE" | "HIGH_FIVE_COMPLETE_CLEAR" | "HIGH_FIVE_QUEUE" | "HIGH_FIVE_REMOVE" | "HIGH_FIVE_SET_ENABLED" | "HOTSPOT_HIDE" | "HOTSPOT_OVERRIDE_CLEAR" | "HOTSPOT_OVERRIDE_SET" | "HYPESQUAD_ONLINE_MEMBERSHIP_JOIN_SUCCESS" | "HYPESQUAD_ONLINE_MEMBERSHIP_LEAVE_SUCCESS" | "I18N_LOAD_ERROR" | "I18N_LOAD_START" | "I18N_LOAD_SUCCESS" | "IDLE" | "IMPERSONATE_STOP" | "IMPERSONATE_UPDATE" | "INBOX_OPEN" | "INCOMING_CALL_MOVE" | "INITIALIZE_MEMBER_SAFETY_STORE" | "INSTALLATION_LOCATION_ADD" | "INSTALLATION_LOCATION_FETCH_METADATA" | "INSTALLATION_LOCATION_REMOVE" | "INSTALLATION_LOCATION_UPDATE" | "INSTANT_INVITE_CLEAR" | "INSTANT_INVITE_CREATE" | "INSTANT_INVITE_CREATE_FAILURE" | "INSTANT_INVITE_CREATE_SUCCESS" | "INSTANT_INVITE_REVOKE_SUCCESS" | "INTEGRATION_CREATE" | "INTEGRATION_DELETE" | "INTEGRATION_PERMISSION_SETTINGS_APPLICATION_PERMISSIONS_FETCH_FAILURE" | "INTEGRATION_PERMISSION_SETTINGS_CLEAR" | "INTEGRATION_PERMISSION_SETTINGS_COMMANDS_FETCH_FAILURE" | "INTEGRATION_PERMISSION_SETTINGS_COMMANDS_FETCH_SUCCESS" | "INTEGRATION_PERMISSION_SETTINGS_COMMAND_UPDATE" | "INTEGRATION_PERMISSION_SETTINGS_EDIT" | "INTEGRATION_PERMISSION_SETTINGS_INIT" | "INTEGRATION_PERMISSION_SETTINGS_RESET" | "INTEGRATION_QUERY" | "INTEGRATION_QUERY_FAILURE" | "INTEGRATION_QUERY_SUCCESS" | "INTEGRATION_SETTINGS_INIT" | "INTEGRATION_SETTINGS_SAVE_FAILURE" | "INTEGRATION_SETTINGS_SAVE_SUCCESS" | "INTEGRATION_SETTINGS_SET_SECTION" | "INTEGRATION_SETTINGS_START_EDITING_COMMAND" | "INTEGRATION_SETTINGS_START_EDITING_INTEGRATION" | "INTEGRATION_SETTINGS_START_EDITING_WEBHOOK" | "INTEGRATION_SETTINGS_STOP_EDITING_COMMAND" | "INTEGRATION_SETTINGS_STOP_EDITING_INTEGRATION" | "INTEGRATION_SETTINGS_STOP_EDITING_WEBHOOK" | "INTEGRATION_SETTINGS_SUBMITTING" | "INTEGRATION_SETTINGS_UPDATE_INTEGRATION" | "INTEGRATION_SETTINGS_UPDATE_WEBHOOK" | "INTERACTION_CREATE" | "INTERACTION_FAILURE" | "INTERACTION_IFRAME_MODAL_CLOSE" | "INTERACTION_IFRAME_MODAL_CREATE" | "INTERACTION_IFRAME_MODAL_KEY_CREATE" | "INTERACTION_MODAL_CREATE" | "INTERACTION_QUEUE" | "INTERACTION_SUCCESS" | "INVITE_ACCEPT" | "INVITE_ACCEPT_FAILURE" | "INVITE_ACCEPT_SUCCESS" | "INVITE_APP_NOT_OPENED" | "INVITE_APP_OPENED" | "INVITE_APP_OPENING" | "INVITE_MODAL_CLOSE" | "INVITE_MODAL_ERROR" | "INVITE_MODAL_OPEN" | "INVITE_RESOLVE" | "INVITE_RESOLVE_FAILURE" | "INVITE_RESOLVE_SUCCESS" | "INVITE_SUGGESTIONS_SEARCH" | "IN_APP_REPORTS_SHOW_FEEDBACK" | "KEYBINDS_ADD_KEYBIND" | "KEYBINDS_DELETE_KEYBIND" | "KEYBINDS_ENABLE_ALL_KEYBINDS" | "KEYBINDS_REGISTER_GLOBAL_KEYBIND_ACTIONS" | "KEYBINDS_SET_KEYBIND" | "KEYBOARD_NAVIGATION_EXPLAINER_MODAL_SEEN" | "LAB_FEATURE_TOGGLE" | "LAYER_POP" | "LAYER_POP_ALL" | "LAYER_PUSH" | "LAYOUT_CREATE" | "LAYOUT_CREATE_WIDGETS" | "LAYOUT_DELETE_ALL_WIDGETS" | "LAYOUT_DELETE_WIDGET" | "LAYOUT_SET_PINNED" | "LAYOUT_SET_TOP_WIDGET" | "LAYOUT_UPDATE_WIDGET" | "LIBRARY_APPLICATIONS_TEST_MODE_ENABLED" | "LIBRARY_APPLICATION_ACTIVE_BRANCH_UPDATE" | "LIBRARY_APPLICATION_ACTIVE_LAUNCH_OPTION_UPDATE" | "LIBRARY_APPLICATION_FILTER_UPDATE" | "LIBRARY_APPLICATION_FLAGS_UPDATE_START" | "LIBRARY_APPLICATION_FLAGS_UPDATE_SUCCESS" | "LIBRARY_APPLICATION_UPDATE" | "LIBRARY_FETCH_SUCCESS" | "LIBRARY_TABLE_ACTIVE_ROW_ID_UPDATE" | "LIBRARY_TABLE_SORT_UPDATE" | "LIVE_CHANNEL_NOTICE_HIDE" | "LOAD_ARCHIVED_THREADS" | "LOAD_ARCHIVED_THREADS_FAIL" | "LOAD_ARCHIVED_THREADS_SUCCESS" | "LOAD_CHANNELS" | "LOAD_FORUM_POSTS" | "LOAD_FRIEND_SUGGESTIONS_FAILURE" | "LOAD_FRIEND_SUGGESTIONS_SUCCESS" | "LOAD_GRAVITY_CUSTOM_SCORES" | "LOAD_GRAVITY_DEHYDRATED" | "LOAD_GRAVITY_HYDRATED" | "LOAD_GUILD_AFFINITIES_SUCCESS" | "LOAD_INVITE_SUGGESTIONS" | "LOAD_MESSAGES" | "LOAD_MESSAGES_AROUND_SUCCESS" | "LOAD_MESSAGES_FAILURE" | "LOAD_MESSAGES_SUCCESS" | "LOAD_MESSAGES_SUCCESS_CACHED" | "LOAD_MESSAGE_INTERACTION_DATA_SUCCESS" | "LOAD_MESSAGE_REQUESTS_SUPPLEMENTAL_DATA_ERROR" | "LOAD_MESSAGE_REQUESTS_SUPPLEMENTAL_DATA_SUCCESS" | "LOAD_NOTIFICATION_CENTER_ITEMS" | "LOAD_NOTIFICATION_CENTER_ITEMS_FAILURE" | "LOAD_NOTIFICATION_CENTER_ITEMS_SUCCESS" | "LOAD_PINNED_MESSAGES" | "LOAD_PINNED_MESSAGES_FAILURE" | "LOAD_PINNED_MESSAGES_SUCCESS" | "LOAD_RECENT_MENTIONS" | "LOAD_RECENT_MENTIONS_FAILURE" | "LOAD_RECENT_MENTIONS_SUCCESS" | "LOAD_REGIONS" | "LOAD_RELATIONSHIPS_FAILURE" | "LOAD_RELATIONSHIPS_SUCCESS" | "LOAD_THREADS_SUCCESS" | "LOAD_USER_AFFINITIES" | "LOAD_USER_AFFINITIES_FAILURE" | "LOAD_USER_AFFINITIES_SUCCESS" | "LOAD_USER_AFFINITIES_V2" | "LOAD_USER_AFFINITIES_V2_FAILURE" | "LOAD_USER_AFFINITIES_V2_SUCCESS" | "LOCAL_ACTIVITY_UPDATE" | "LOCAL_MESSAGES_LOADED" | "LOCAL_MESSAGE_CREATE" | "LOGIN" | "LOGIN_ACCOUNT_DISABLED" | "LOGIN_ACCOUNT_SCHEDULED_FOR_DELETION" | "LOGIN_ATTEMPTED" | "LOGIN_FAILURE" | "LOGIN_MFA" | "LOGIN_MFA_FAILURE" | "LOGIN_MFA_SMS" | "LOGIN_MFA_SMS_FAILURE" | "LOGIN_MFA_SMS_REQUEST_SUCCESS" | "LOGIN_MFA_STEP" | "LOGIN_PASSWORD_RECOVERY_PHONE_VERIFICATION" | "LOGIN_PHONE_IP_AUTHORIZATION_REQUIRED" | "LOGIN_RESET" | "LOGIN_STATUS_RESET" | "LOGIN_SUCCESS" | "LOGIN_SUSPENDED_USER" | "LOGOUT" | "LOGOUT_AUTH_SESSIONS_SUCCESS" | "MASKED_LINK_ADD_TRUSTED_DOMAIN" | "MASKED_LINK_ADD_TRUSTED_PROTOCOL" | "MAX_MEMBER_COUNT_NOTICE_DISMISS" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS_ERROR" | "MEDIA_ENGINE_APPLY_MEDIA_FILTER_SETTINGS_START" | "MEDIA_ENGINE_DEVICES" | "MEDIA_ENGINE_INTERACTION_REQUIRED" | "MEDIA_ENGINE_NOISE_CANCELLATION_ERROR_RESET" | "MEDIA_ENGINE_PERMISSION" | "MEDIA_ENGINE_SET_AEC_DUMP" | "MEDIA_ENGINE_SET_AUDIO_ENABLED" | "MEDIA_ENGINE_SET_EXPERIMENTAL_ENCODERS" | "MEDIA_ENGINE_SET_EXPERIMENTAL_SOUNDSHARE" | "MEDIA_ENGINE_SET_GO_LIVE_SOURCE" | "MEDIA_ENGINE_SET_HARDWARE_ENCODING" | "MEDIA_ENGINE_SET_OPEN_H264" | "MEDIA_ENGINE_SET_VIDEO_DEVICE" | "MEDIA_ENGINE_SET_VIDEO_ENABLED" | "MEDIA_ENGINE_SET_VIDEO_HOOK" | "MEDIA_ENGINE_SOUNDSHARE_FAILED" | "MEDIA_ENGINE_SOUNDSHARE_TRANSMITTING" | "MEDIA_ENGINE_VIDEO_SOURCE_QUALITY_CHANGED" | "MEDIA_ENGINE_VIDEO_STATE_CHANGED" | "MEDIA_POST_EMBED_FETCH" | "MEDIA_POST_EMBED_FETCH_FAILURE" | "MEDIA_POST_EMBED_FETCH_SUCCESS" | "MEDIA_SESSION_JOINED" | "MEMBER_SAFETY_GUILD_MEMBER_SEARCH_SUCCESS" | "MEMBER_SAFETY_GUILD_MEMBER_UPDATE_BATCH" | "MEMBER_SAFETY_NEW_MEMBER_TIMESTAMP_REFRESH" | "MEMBER_SAFETY_PAGINATION_TOKEN_UPDATE" | "MEMBER_SAFETY_PAGINATION_UPDATE" | "MEMBER_SAFETY_SEARCH_STATE_UPDATE" | "MEMBER_VERIFICATION_FORM_FETCH_FAIL" | "MEMBER_VERIFICATION_FORM_UPDATE" | "MENTION_MODAL_CLOSE" | "MENTION_MODAL_OPEN" | "MESSAGE_ACK" | "MESSAGE_ACKED" | "MESSAGE_CREATE" | "MESSAGE_DELETE" | "MESSAGE_DELETE_BULK" | "MESSAGE_EDIT_FAILED_AUTOMOD" | "MESSAGE_END_EDIT" | "MESSAGE_EXPLICIT_CONTENT_FP_CREATE" | "MESSAGE_EXPLICIT_CONTENT_FP_SUBMIT" | "MESSAGE_EXPLICIT_CONTENT_SCAN_TIMEOUT" | "MESSAGE_LENGTH_UPSELL" | "MESSAGE_NOTIFICATION_SHOWN" | "MESSAGE_PREVIEWS_LOADED" | "MESSAGE_PREVIEWS_LOCALLY_LOADED" | "MESSAGE_REACTION_ADD" | "MESSAGE_REACTION_ADD_MANY" | "MESSAGE_REACTION_ADD_USERS" | "MESSAGE_REACTION_REMOVE" | "MESSAGE_REACTION_REMOVE_ALL" | "MESSAGE_REACTION_REMOVE_EMOJI" | "MESSAGE_REMINDER_NOTIFIED" | "MESSAGE_REMINDER_TOGGLE" | "MESSAGE_REQUEST_ACCEPT_OPTIMISTIC" | "MESSAGE_REQUEST_ACK" | "MESSAGE_REQUEST_CLEAR_ACK" | "MESSAGE_REVEAL" | "MESSAGE_SEND_FAILED" | "MESSAGE_SEND_FAILED_AUTOMOD" | "MESSAGE_START_EDIT" | "MESSAGE_UPDATE" | "MESSAGE_UPDATE_EDIT" | "MFA_CLEAR_BACKUP_CODES" | "MFA_DISABLE_SUCCESS" | "MFA_ENABLE_EMAIL_TOKEN" | "MFA_ENABLE_SUCCESS" | "MFA_SEEN_BACKUP_CODE_PROMPT" | "MFA_SEND_VERIFICATION_KEY" | "MFA_SMS_TOGGLE" | "MFA_SMS_TOGGLE_COMPLETE" | "MFA_VIEW_BACKUP_CODES" | "MFA_WEBAUTHN_CREDENTIALS_LOADED" | "MOBILE_NATIVE_UPDATE_CHECK_FINISHED" | "MOBILE_WEB_SIDEBAR_CLOSE" | "MOBILE_WEB_SIDEBAR_OPEN" | "MODAL_POP" | "MODAL_PUSH" | "MOD_VIEW_SEARCH_FINISH" | "MULTI_ACCOUNT_INVALIDATE_PUSH_SYNC_TOKENS" | "MULTI_ACCOUNT_MOBILE_EXPERIMENT_UPDATE" | "MULTI_ACCOUNT_MOVE_ACCOUNT" | "MULTI_ACCOUNT_REMOVE_ACCOUNT" | "MULTI_ACCOUNT_UPDATE_PUSH_SYNC_TOKEN" | "MULTI_ACCOUNT_VALIDATE_TOKEN_FAILURE" | "MULTI_ACCOUNT_VALIDATE_TOKEN_REQUEST" | "MULTI_ACCOUNT_VALIDATE_TOKEN_SUCCESS" | "MUTUAL_FRIENDS_FETCH_FAILURE" | "MUTUAL_FRIENDS_FETCH_START" | "MUTUAL_FRIENDS_FETCH_SUCCESS" | "NEWLY_ADDED_EMOJI_SEEN_ACKNOWLEDGED" | "NEWLY_ADDED_EMOJI_SEEN_PENDING" | "NEWLY_ADDED_EMOJI_SEEN_UPDATED" | "NEW_PAYMENT_SOURCE_ADDRESS_INFO_UPDATE" | "NEW_PAYMENT_SOURCE_CARD_INFO_UPDATE" | "NEW_PAYMENT_SOURCE_CLEAR_ERROR" | "NEW_PAYMENT_SOURCE_STRIPE_PAYMENT_REQUEST_UPDATE" | "NOTICE_DISABLE" | "NOTICE_DISMISS" | "NOTICE_SHOW" | "NOTIFICATIONS_SET_DESKTOP_TYPE" | "NOTIFICATIONS_SET_DISABLED_SOUNDS" | "NOTIFICATIONS_SET_DISABLE_UNREAD_BADGE" | "NOTIFICATIONS_SET_NOTIFY_MESSAGES_IN_SELECTED_CHANNEL" | "NOTIFICATIONS_SET_PERMISSION_STATE" | "NOTIFICATIONS_SET_TASKBAR_FLASH" | "NOTIFICATIONS_SET_TTS_TYPE" | "NOTIFICATIONS_TOGGLE_ALL_DISABLED" | "NOTIFICATION_CENTER_CLEAR_GUILD_MENTIONS" | "NOTIFICATION_CENTER_ITEMS_ACK" | "NOTIFICATION_CENTER_ITEMS_ACK_FAILURE" | "NOTIFICATION_CENTER_ITEMS_LOCAL_ACK" | "NOTIFICATION_CENTER_ITEM_COMPLETED" | "NOTIFICATION_CENTER_ITEM_CREATE" | "NOTIFICATION_CENTER_ITEM_DELETE" | "NOTIFICATION_CENTER_ITEM_DELETE_FAILURE" | "NOTIFICATION_CENTER_REFRESH" | "NOTIFICATION_CENTER_SET_ACTIVE" | "NOTIFICATION_CENTER_SET_TAB" | "NOTIFICATION_CENTER_TAB_FOCUSED" | "NOTIFICATION_CLICK" | "NOTIFICATION_CREATE" | "NOTIFICATION_SETTINGS_UPDATE" | "NOW_PLAYING_MOUNTED" | "NOW_PLAYING_UNMOUNTED" | "NUF_COMPLETE" | "NUF_NEW_USER" | "OAUTH2_TOKEN_REVOKE" | "ONLINE_GUILD_MEMBER_COUNT_UPDATE" | "OUTBOUND_PROMOTIONS_SEEN" | "OUTBOUND_PROMOTION_NOTICE_DISMISS" | "OVERLAY_ACTIVATE_REGION" | "OVERLAY_CALL_PRIVATE_CHANNEL" | "OVERLAY_CRASHED" | "OVERLAY_DEACTIVATE_ALL_REGIONS" | "OVERLAY_DISABLE_EXTERNAL_LINK_ALERT" | "OVERLAY_FOCUSED" | "OVERLAY_INCOMPATIBLE_APP" | "OVERLAY_INITIALIZE" | "OVERLAY_JOIN_GAME" | "OVERLAY_MESSAGE_EVENT_ACTION" | "OVERLAY_NOTIFICATION_EVENT" | "OVERLAY_NOTIFY_READY_TO_SHOW" | "OVERLAY_READY" | "OVERLAY_SELECT_CALL" | "OVERLAY_SELECT_CHANNEL" | "OVERLAY_SET_ASSOCIATED_GAME" | "OVERLAY_SET_AVATAR_SIZE_MODE" | "OVERLAY_SET_CLICK_ZONES" | "OVERLAY_SET_DISPLAY_NAME_MODE" | "OVERLAY_SET_DISPLAY_USER_MODE" | "OVERLAY_SET_ENABLED" | "OVERLAY_SET_INPUT_LOCKED" | "OVERLAY_SET_NOTIFICATION_POSITION_MODE" | "OVERLAY_SET_NOT_IDLE" | "OVERLAY_SET_PREVIEW_IN_GAME_MODE" | "OVERLAY_SET_SHOW_KEYBIND_INDICATORS" | "OVERLAY_SET_TEXT_CHAT_NOTIFICATION_MODE" | "OVERLAY_SET_TEXT_WIDGET_OPACITY" | "OVERLAY_SET_UI_LOCKED" | "OVERLAY_SOUNDBOARD_SOUNDS_FETCH_REQUEST" | "OVERLAY_START_SESSION" | "OVERLAY_SUCCESSFULLY_SHOWN" | "OVERLAY_WIDGET_CHANGED" | "PASSIVE_UPDATE_V2" | "PASSWORDLESS_FAILURE" | "PASSWORDLESS_START" | "PASSWORD_UPDATED" | "PAYMENT_AUTHENTICATION_CLEAR_ERROR" | "PAYMENT_AUTHENTICATION_ERROR" | "PAYMENT_UPDATE" | "PERMISSION_CLEAR_ELEVATED_PROCESS" | "PERMISSION_CLEAR_PTT_ADMIN_WARNING" | "PERMISSION_CLEAR_SUPPRESS_WARNING" | "PERMISSION_CLEAR_VAD_WARNING" | "PERMISSION_CONTINUE_NONELEVATED_PROCESS" | "PERMISSION_REQUEST_ELEVATED_PROCESS" | "PHONE_SET_COUNTRY_CODE" | "PICTURE_IN_PICTURE_CLOSE" | "PICTURE_IN_PICTURE_HIDE" | "PICTURE_IN_PICTURE_MOVE" | "PICTURE_IN_PICTURE_OPEN" | "PICTURE_IN_PICTURE_RESIZE" | "PICTURE_IN_PICTURE_SHOW" | "PICTURE_IN_PICTURE_UPDATE_RECT" | "PICTURE_IN_PICTURE_UPDATE_SELECTED_WINDOW" | "POGGERMODE_ACHIEVEMENT_UNLOCK" | "POGGERMODE_SETTINGS_UPDATE" | "POGGERMODE_TEMPORARILY_DISABLED" | "POGGERMODE_UPDATE_COMBO" | "POGGERMODE_UPDATE_MESSAGE_COMBO" | "POPOUT_WINDOW_ADD_STYLESHEET" | "POPOUT_WINDOW_CLOSE" | "POPOUT_WINDOW_OPEN" | "POPOUT_WINDOW_SET_ALWAYS_ON_TOP" | "POST_CONNECTION_OPEN" | "PREMIUM_MARKETING_DATA_READY" | "PREMIUM_MARKETING_PREVIEW" | "PREMIUM_PAYMENT_ERROR_CLEAR" | "PREMIUM_PAYMENT_MODAL_CLOSE" | "PREMIUM_PAYMENT_MODAL_OPEN" | "PREMIUM_PAYMENT_SUBSCRIBE_FAIL" | "PREMIUM_PAYMENT_SUBSCRIBE_START" | "PREMIUM_PAYMENT_SUBSCRIBE_SUCCESS" | "PREMIUM_PAYMENT_UPDATE_FAIL" | "PREMIUM_PAYMENT_UPDATE_SUCCESS" | "PREMIUM_PERKS_DEMOS_FETCH_FAILURE" | "PREMIUM_PERKS_DEMOS_FETCH_SUCCESS" | "PREMIUM_PERKS_DEMO_ACTIVATE_FAILURE" | "PREMIUM_PERKS_DEMO_ACTIVATE_SUCCESS" | "PREMIUM_PERKS_DEMO_COMPLETE" | "PREMIUM_PERKS_DEMO_OVERRIDE" | "PREMIUM_REQUIRED_MODAL_CLOSE" | "PREMIUM_REQUIRED_MODAL_OPEN" | "PRESENCES_REPLACE" | "PRESENCE_UPDATES" | "PRIVATE_CHANNEL_INTEGRATION_CREATE" | "PRIVATE_CHANNEL_INTEGRATION_DELETE" | "PRIVATE_CHANNEL_INTEGRATION_UPDATE" | "PRIVATE_CHANNEL_RECIPIENTS_ADD_USER" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_CLOSE" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_OPEN" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_QUERY" | "PRIVATE_CHANNEL_RECIPIENTS_INVITE_SELECT" | "PRIVATE_CHANNEL_RECIPIENTS_REMOVE_USER" | "PROFILE_CUSTOMIZATION_OPEN_PREVIEW_MODAL" | "PROFILE_EFFECTS_SET_TRY_IT_OUT" | "PROFILE_PANEL_TOGGLE_SECTION" | "PROXY_BLOCKED_REQUEST" | "PUBLIC_UPSELL_NOTICE_DISMISS" | "PURCHASED_ITEMS_FESTIVITY_FETCH_WOW_MOMENT_MEDIA_SUCCESS" | "PURCHASED_ITEMS_FESTIVITY_IS_FETCHING_WOW_MOMENT_MEDIA" | "PURCHASED_ITEMS_FESTIVITY_SET_CAN_PLAY_WOW_MOMENT" | "PUSH_NOTIFICATION_CLICK" | "QUESTS_CLAIM_REWARD_BEGIN" | "QUESTS_CLAIM_REWARD_FAILURE" | "QUESTS_CLAIM_REWARD_SUCCESS" | "QUESTS_DELIVERY_OVERRIDE" | "QUESTS_DISMISS_CONTENT_BEGIN" | "QUESTS_DISMISS_CONTENT_FAILURE" | "QUESTS_DISMISS_CONTENT_SUCCESS" | "QUESTS_DISMISS_PROGRESS_TRACKING_FAILURE_NOTICE" | "QUESTS_ENROLL_BEGIN" | "QUESTS_ENROLL_FAILURE" | "QUESTS_ENROLL_SUCCESS" | "QUESTS_FETCH_CURRENT_QUESTS_BEGIN" | "QUESTS_FETCH_CURRENT_QUESTS_FAILURE" | "QUESTS_FETCH_CURRENT_QUESTS_SUCCESS" | "QUESTS_FETCH_REWARD_CODE_BEGIN" | "QUESTS_FETCH_REWARD_CODE_FAILURE" | "QUESTS_FETCH_REWARD_CODE_SUCCESS" | "QUESTS_PREVIEW_UPDATE_SUCCESS" | "QUESTS_SEND_HEARTBEAT_FAILURE" | "QUESTS_SEND_HEARTBEAT_SUCCESS" | "QUESTS_USER_STATUS_UPDATE" | "QUEUE_INTERACTION_COMPONENT_STATE" | "QUICKSWITCHER_HIDE" | "QUICKSWITCHER_SEARCH" | "QUICKSWITCHER_SELECT" | "QUICKSWITCHER_SHOW" | "QUICKSWITCHER_SWITCH_TO" | "RECEIVE_CHANNEL_AFFINITIES" | "RECEIVE_CHANNEL_SUMMARIES" | "RECEIVE_CHANNEL_SUMMARIES_BULK" | "RECEIVE_CHANNEL_SUMMARY" | "RECENT_MENTION_DELETE" | "RECOMPUTE_READ_STATES" | "REFERRALS_FETCH_ELIGIBLE_USER_FAIL" | "REFERRALS_FETCH_ELIGIBLE_USER_START" | "REFERRALS_FETCH_ELIGIBLE_USER_SUCCESS" | "REGISTER" | "REGISTER_FAILURE" | "REGISTER_SAVE_FORM" | "REGISTER_SUCCESS" | "RELATIONSHIP_ADD" | "RELATIONSHIP_PENDING_INCOMING_REMOVED" | "RELATIONSHIP_REMOVE" | "RELATIONSHIP_UPDATE" | "RELOAD_GRAVITY" | "REMOTE_COMMAND" | "REMOTE_SESSION_CONNECT" | "REMOTE_SESSION_DISCONNECT" | "REMOVE_AUTOMOD_MESSAGE_NOTICE" | "REQUEST_CHANNEL_AFFINITIES" | "REQUEST_CHANNEL_SUMMARIES" | "REQUEST_CHANNEL_SUMMARIES_BULK" | "REQUEST_CHANNEL_SUMMARY" | "REQUEST_FORUM_UNREADS" | "REQUEST_SOUNDBOARD_SOUNDS" | "RESET_NOTIFICATION_CENTER" | "RESET_PAYMENT_ID" | "RESET_PREVIEW_CLIENT_THEME" | "RESET_SOCKET" | "RESORT_THREADS" | "RPC_APP_AUTHENTICATED" | "RPC_APP_CONNECTED" | "RPC_APP_DISCONNECTED" | "RPC_NOTIFICATION_CREATE" | "RPC_SERVER_READY" | "RTC_CONNECTION_CLIENT_CONNECT" | "RTC_CONNECTION_CLIENT_DISCONNECT" | "RTC_CONNECTION_FLAGS" | "RTC_CONNECTION_LOSS_RATE" | "RTC_CONNECTION_PING" | "RTC_CONNECTION_PLATFORM" | "RTC_CONNECTION_SECURE_FRAMES_UPDATE" | "RTC_CONNECTION_STATE" | "RTC_CONNECTION_UPDATE_ID" | "RTC_CONNECTION_USER_CREATE" | "RTC_CONNECTION_VIDEO" | "RTC_DEBUG_MODAL_CLOSE" | "RTC_DEBUG_MODAL_OPEN" | "RTC_DEBUG_MODAL_OPEN_REPLAY" | "RTC_DEBUG_MODAL_OPEN_REPLAY_AT_PATH" | "RTC_DEBUG_MODAL_SET_SECTION" | "RTC_DEBUG_MODAL_UPDATE" | "RTC_DEBUG_MODAL_UPDATE_VIDEO_OUTPUT" | "RTC_DEBUG_POPOUT_WINDOW_OPEN" | "RTC_DEBUG_SET_RECORDING_FLAG" | "RTC_DEBUG_SET_SIMULCAST_OVERRIDE" | "RTC_LATENCY_TEST_COMPLETE" | "RTC_SPEED_TEST_START_TEST" | "RTC_SPEED_TEST_STOP_TEST" | "RUNNING_GAMES_CHANGE" | "RUNNING_GAME_ADD_OVERRIDE" | "RUNNING_GAME_DELETE_ENTRY" | "RUNNING_GAME_EDIT_NAME" | "RUNNING_GAME_TOGGLE_DETECTION" | "RUNNING_GAME_TOGGLE_OVERLAY" | "RUNNING_STREAMER_TOOLS_CHANGE" | "SAFETY_HUB_APPEAL_CLOSE" | "SAFETY_HUB_APPEAL_OPEN" | "SAFETY_HUB_APPEAL_SIGNAL_CUSTOM_INPUT_CHANGE" | "SAFETY_HUB_APPEAL_SIGNAL_SELECT" | "SAFETY_HUB_FETCH_CLASSIFICATION_FAILURE" | "SAFETY_HUB_FETCH_CLASSIFICATION_START" | "SAFETY_HUB_FETCH_CLASSIFICATION_SUCCESS" | "SAFETY_HUB_FETCH_FAILURE" | "SAFETY_HUB_FETCH_START" | "SAFETY_HUB_FETCH_SUCCESS" | "SAFETY_HUB_REQUEST_REVIEW_FAILURE" | "SAFETY_HUB_REQUEST_REVIEW_START" | "SAFETY_HUB_REQUEST_REVIEW_SUCCESS" | "SAVED_MESSAGES_UPDATE" | "SAVED_MESSAGE_CREATE" | "SAVED_MESSAGE_DELETE" | "SAVE_LAST_NON_VOICE_ROUTE" | "SAVE_LAST_ROUTE" | "SEARCH_ADD_HISTORY" | "SEARCH_AUTOCOMPLETE_QUERY_UPDATE" | "SEARCH_CLEAR_HISTORY" | "SEARCH_EDITOR_STATE_CHANGE" | "SEARCH_EDITOR_STATE_CLEAR" | "SEARCH_ENSURE_SEARCH_STATE" | "SEARCH_FINISH" | "SEARCH_INDEXING" | "SEARCH_MODAL_CLOSE" | "SEARCH_MODAL_OPEN" | "SEARCH_REMOVE_HISTORY" | "SEARCH_SCREEN_OPEN" | "SEARCH_SET_SHOW_BLOCKED_RESULTS" | "SEARCH_START" | "SECURE_FRAMES_ALL_VERIFIED_KEYS_DELETE" | "SECURE_FRAMES_SETTINGS_UPDATE" | "SECURE_FRAMES_TRANSIENT_KEY_CREATE" | "SECURE_FRAMES_TRANSIENT_KEY_DELETE" | "SECURE_FRAMES_UPLOAD_PUBLIC_KEY_SUCCESS" | "SECURE_FRAMES_USER_VERIFIED_KEYS_DELETE" | "SECURE_FRAMES_VERIFIED_KEY_CREATE" | "SECURE_FRAMES_VERIFIED_KEY_DELETE" | "SELECTIVELY_SYNCED_USER_SETTINGS_UPDATE" | "SELECT_HOME_RESOURCE_CHANNEL" | "SELECT_NEW_MEMBER_ACTION_CHANNEL" | "SELF_PRESENCE_STORE_UPDATE" | "SESSIONS_REPLACE" | "SET_CHANNEL_BITRATE" | "SET_CHANNEL_VIDEO_QUALITY_MODE" | "SET_CONSENT_REQUIRED" | "SET_CREATED_AT_OVERRIDE" | "SET_GRAVITY_FILTERS" | "SET_GRAVITY_SELECTED_CHANNEL" | "SET_GRAVITY_SELECTED_SUMMARY" | "SET_GUILD_FOLDER_EXPANDED" | "SET_HIGHLIGHTED_SUMMARY" | "SET_INTERACTION_COMPONENT_STATE" | "SET_LOCATION_METADATA" | "SET_LOGIN_CREDENTIALS" | "SET_NATIVE_PERMISSION" | "SET_PENDING_REPLY_SHOULD_MENTION" | "SET_PREMIUM_TYPE_OVERRIDE" | "SET_RECENTLY_ACTIVE_COLLAPSED" | "SET_RECENT_MENTIONS_FILTER" | "SET_RECENT_MENTIONS_STALE" | "SET_SELECTED_SUMMARY" | "SET_SOUNDPACK" | "SET_STREAM_APP_INTENT" | "SET_SUMMARY_FEEDBACK" | "SET_TTS_SPEECH_RATE" | "SET_VAD_PERMISSION" | "SHARED_CANVAS_CLEAR_DRAWABLES" | "SHARED_CANVAS_DRAW_LINE_POINT" | "SHARED_CANVAS_SET_DRAW_MODE" | "SHARED_CANVAS_UPDATE_EMOJI_HOSE" | "SHARED_CANVAS_UPDATE_LINE_POINTS" | "SHOW_ACTION_SHEET" | "SHOW_ACTION_SHEET_QUICK_SWITCHER" | "SHOW_KEYBOARD_SHORTCUTS" | "SIDEBAR_CLOSE" | "SIDEBAR_CLOSE_GUILD" | "SIDEBAR_CREATE_THREAD" | "SIDEBAR_VIEW_CHANNEL" | "SIDEBAR_VIEW_GUILD" | "SKUS_FETCH_SUCCESS" | "SKU_FETCH_FAIL" | "SKU_FETCH_START" | "SKU_FETCH_SUCCESS" | "SKU_PURCHASE_AWAIT_CONFIRMATION" | "SKU_PURCHASE_CLEAR_ERROR" | "SKU_PURCHASE_FAIL" | "SKU_PURCHASE_MODAL_CLOSE" | "SKU_PURCHASE_MODAL_OPEN" | "SKU_PURCHASE_PREVIEW_FETCH" | "SKU_PURCHASE_PREVIEW_FETCH_FAILURE" | "SKU_PURCHASE_PREVIEW_FETCH_SUCCESS" | "SKU_PURCHASE_SHOW_CONFIRMATION_STEP" | "SKU_PURCHASE_START" | "SKU_PURCHASE_SUCCESS" | "SKU_PURCHASE_UPDATE_IS_GIFT" | "SLOWMODE_RESET_COOLDOWN" | "SLOWMODE_SET_COOLDOWN" | "SOUNDBOARD_FETCH_DEFAULT_SOUNDS" | "SOUNDBOARD_FETCH_DEFAULT_SOUNDS_SUCCESS" | "SOUNDBOARD_MUTE_JOIN_SOUND" | "SOUNDBOARD_SET_OVERLAY_ENABLED" | "SOUNDBOARD_SOUNDS_RECEIVED" | "SPEAKING" | "SPEAKING_MESSAGE" | "SPEAK_MESSAGE" | "SPEAK_TEXT" | "SPEED_TEST_CREATE" | "SPEED_TEST_DELETE" | "SPEED_TEST_SERVER_UPDATE" | "SPELLCHECK_LEARN_WORD" | "SPELLCHECK_TOGGLE" | "SPELLCHECK_UNLEARN_WORD" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN_REVOKE" | "SPOTIFY_NEW_TRACK" | "SPOTIFY_PLAYER_PAUSE" | "SPOTIFY_PLAYER_PLAY" | "SPOTIFY_PLAYER_STATE" | "SPOTIFY_PROFILE_UPDATE" | "SPOTIFY_SET_ACTIVE_DEVICE" | "SPOTIFY_SET_DEVICES" | "SPOTIFY_SET_PROTOCOL_REGISTERED" | "STAGE_INSTANCE_CREATE" | "STAGE_INSTANCE_DELETE" | "STAGE_INSTANCE_UPDATE" | "STAGE_MUSIC_MUTE" | "STAGE_MUSIC_PLAY" | "START_BROADCAST_STREAM" | "START_SESSION" | "STATUS_PAGE_INCIDENT" | "STATUS_PAGE_SCHEDULED_MAINTENANCE" | "STATUS_PAGE_SCHEDULED_MAINTENANCE_ACK" | "STICKER_FETCH_SUCCESS" | "STICKER_PACKS_FETCH_START" | "STICKER_PACKS_FETCH_SUCCESS" | "STICKER_PACK_FETCH_SUCCESS" | "STICKER_TRACK_USAGE" | "STOP_SPEAKING" | "STORE_LISTINGS_FETCH_SUCCESS" | "STORE_LISTING_FETCH_SUCCESS" | "STREAMER_MODE_UPDATE" | "STREAMING_UPDATE" | "STREAM_CLOSE" | "STREAM_CREATE" | "STREAM_DELETE" | "STREAM_LAYOUT_UPDATE" | "STREAM_PREVIEW_FETCH_FAIL" | "STREAM_PREVIEW_FETCH_START" | "STREAM_PREVIEW_FETCH_SUCCESS" | "STREAM_SERVER_UPDATE" | "STREAM_SET_PAUSED" | "STREAM_START" | "STREAM_STATS_UPDATE" | "STREAM_STOP" | "STREAM_TIMED_OUT" | "STREAM_UPDATE" | "STREAM_UPDATE_SELF_HIDDEN" | "STREAM_UPDATE_SETTINGS" | "STREAM_WATCH" | "STRIPE_TOKEN_FAILURE" | "SUBSCRIPTION_PLANS_FETCH" | "SUBSCRIPTION_PLANS_FETCH_FAILURE" | "SUBSCRIPTION_PLANS_FETCH_SUCCESS" | "SUBSCRIPTION_PLANS_RESET" | "SURVEY_FETCHED" | "SURVEY_HIDE" | "SURVEY_OVERRIDE" | "SURVEY_SEEN" | "SYSTEM_THEME_CHANGE" | "THERMAL_STATE_CHANGE" | "THREAD_CREATE" | "THREAD_CREATE_LOCAL" | "THREAD_DELETE" | "THREAD_LIST_SYNC" | "THREAD_MEMBERS_UPDATE" | "THREAD_MEMBER_LIST_UPDATE" | "THREAD_MEMBER_LOCAL_UPDATE" | "THREAD_MEMBER_UPDATE" | "THREAD_SETTINGS_DRAFT_CHANGE" | "THREAD_UPDATE" | "TOGGLE_GUILD_FOLDER_EXPAND" | "TOGGLE_OVERLAY_CANVAS" | "TOGGLE_TOPICS_BAR" | "TOP_EMOJIS_FETCH" | "TOP_EMOJIS_FETCH_SUCCESS" | "TRUNCATE_MENTIONS" | "TRUNCATE_MESSAGES" | "TRY_ACK" | "TUTORIAL_INDICATOR_DISMISS" | "TUTORIAL_INDICATOR_HIDE" | "TUTORIAL_INDICATOR_SHOW" | "TUTORIAL_INDICATOR_SUPPRESS_ALL" | "TYPING_START" | "TYPING_START_LOCAL" | "TYPING_STOP" | "TYPING_STOP_LOCAL" | "UNREAD_SETTING_NOTICE_CHANNEL_VISIT" | "UNREAD_SETTING_NOTICE_RENDERED" | "UNSYNCED_USER_SETTINGS_UPDATE" | "UNVERIFIED_GAME_UPDATE" | "UPCOMING_GUILD_EVENT_NOTICE_HIDE" | "UPCOMING_GUILD_EVENT_NOTICE_SEEN" | "UPDATE_AVAILABLE" | "UPDATE_BACKGROUND_GRADIENT_PRESET" | "UPDATE_CHANNEL_DIMENSIONS" | "UPDATE_CHANNEL_LIST_DIMENSIONS" | "UPDATE_CHANNEL_LIST_SUBTITLES" | "UPDATE_CLIENT_PREMIUM_TYPE" | "UPDATE_CONSENTS" | "UPDATE_DOWNLOADED" | "UPDATE_ERROR" | "UPDATE_GUILD_LIST_DIMENSIONS" | "UPDATE_HANG_STATUS" | "UPDATE_HANG_STATUS_CUSTOM" | "UPDATE_MANUALLY" | "UPDATE_MOBILE_PENDING_THEME_INDEX" | "UPDATE_NOT_AVAILABLE" | "UPDATE_TOKEN" | "UPDATE_VISIBLE_MESSAGES" | "UPLOAD_ATTACHMENT_ADD_FILES" | "UPLOAD_ATTACHMENT_CLEAR_ALL_FILES" | "UPLOAD_ATTACHMENT_POP_FILE" | "UPLOAD_ATTACHMENT_REMOVE_FILE" | "UPLOAD_ATTACHMENT_REMOVE_FILES" | "UPLOAD_ATTACHMENT_SET_FILE" | "UPLOAD_ATTACHMENT_SET_UPLOADS" | "UPLOAD_ATTACHMENT_UPDATE_FILE" | "UPLOAD_CANCEL_REQUEST" | "UPLOAD_COMPLETE" | "UPLOAD_COMPRESSION_PROGRESS" | "UPLOAD_FAIL" | "UPLOAD_FILE_UPDATE" | "UPLOAD_ITEM_CANCEL_REQUEST" | "UPLOAD_PROGRESS" | "UPLOAD_RESTORE_FAILED_UPLOAD" | "UPLOAD_START" | "USER_ACHIEVEMENT_UPDATE" | "USER_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "USER_APPLICATION_REMOVE" | "USER_APPLICATION_UPDATE" | "USER_APPLIED_BOOSTS_FETCH_START" | "USER_APPLIED_BOOSTS_FETCH_SUCCESS" | "USER_AUTHORIZED_APPS_UPDATE" | "USER_CONNECTIONS_INTEGRATION_JOINING" | "USER_CONNECTIONS_INTEGRATION_JOINING_ERROR" | "USER_CONNECTIONS_UPDATE" | "USER_CONNECTION_UPDATE" | "USER_GUILD_JOIN_REQUEST_COACHMARK_CLEAR" | "USER_GUILD_JOIN_REQUEST_COACHMARK_SHOW" | "USER_GUILD_JOIN_REQUEST_UPDATE" | "USER_GUILD_SETTINGS_CHANNEL_UPDATE" | "USER_GUILD_SETTINGS_CHANNEL_UPDATE_BULK" | "USER_GUILD_SETTINGS_FULL_UPDATE" | "USER_GUILD_SETTINGS_GUILD_AND_CHANNELS_UPDATE" | "USER_GUILD_SETTINGS_GUILD_UPDATE" | "USER_GUILD_SETTINGS_REMOVE_PENDING_CHANNEL_UPDATES" | "USER_JOIN_REQUEST_GUILDS_FETCH" | "USER_NON_CHANNEL_ACK" | "USER_NOTE_LOADED" | "USER_NOTE_LOAD_START" | "USER_NOTE_UPDATE" | "USER_PAYMENT_BROWSER_CHECKOUT_DONE" | "USER_PAYMENT_BROWSER_CHECKOUT_STARTED" | "USER_PAYMENT_CLIENT_ADD" | "USER_PROFILE_EFFECTS_FETCH" | "USER_PROFILE_EFFECTS_FETCH_FAILURE" | "USER_PROFILE_EFFECTS_FETCH_SUCCESS" | "USER_PROFILE_FETCH_FAILURE" | "USER_PROFILE_FETCH_START" | "USER_PROFILE_FETCH_SUCCESS" | "USER_PROFILE_MODAL_CLOSE" | "USER_PROFILE_MODAL_OPEN" | "USER_PROFILE_UPDATE_FAILURE" | "USER_PROFILE_UPDATE_START" | "USER_PROFILE_UPDATE_SUCCESS" | "USER_REQUIRED_ACTION_UPDATE" | "USER_SETTINGS_ACCOUNT_CLOSE" | "USER_SETTINGS_ACCOUNT_INIT" | "USER_SETTINGS_ACCOUNT_RESET_AND_CLOSE_FORM" | "USER_SETTINGS_ACCOUNT_SET_PENDING_ACCENT_COLOR" | "USER_SETTINGS_ACCOUNT_SET_PENDING_AVATAR" | "USER_SETTINGS_ACCOUNT_SET_PENDING_AVATAR_DECORATION" | "USER_SETTINGS_ACCOUNT_SET_PENDING_BANNER" | "USER_SETTINGS_ACCOUNT_SET_PENDING_BIO" | "USER_SETTINGS_ACCOUNT_SET_PENDING_GLOBAL_NAME" | "USER_SETTINGS_ACCOUNT_SET_PENDING_PROFILE_EFFECT_ID" | "USER_SETTINGS_ACCOUNT_SET_PENDING_PRONOUNS" | "USER_SETTINGS_ACCOUNT_SET_PENDING_THEME_COLORS" | "USER_SETTINGS_ACCOUNT_SET_SINGLE_TRY_IT_OUT_COLLECTIBLES_ITEM" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_AVATAR" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_AVATAR_DECORATION" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_BANNER" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_PROFILE_EFFECT_ID" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_THEME_COLORS" | "USER_SETTINGS_ACCOUNT_SUBMIT" | "USER_SETTINGS_ACCOUNT_SUBMIT_FAILURE" | "USER_SETTINGS_ACCOUNT_SUBMIT_SUCCESS" | "USER_SETTINGS_CLEAR_ERRORS" | "USER_SETTINGS_LOCALE_OVERRIDE" | "USER_SETTINGS_MODAL_CLEAR_SCROLL_POSITION" | "USER_SETTINGS_MODAL_CLEAR_SUBSECTION" | "USER_SETTINGS_MODAL_CLOSE" | "USER_SETTINGS_MODAL_INIT" | "USER_SETTINGS_MODAL_OPEN" | "USER_SETTINGS_MODAL_RESET" | "USER_SETTINGS_MODAL_SET_SECTION" | "USER_SETTINGS_MODAL_SUBMIT" | "USER_SETTINGS_MODAL_SUBMIT_COMPLETE" | "USER_SETTINGS_MODAL_SUBMIT_FAILURE" | "USER_SETTINGS_MODAL_UPDATE_ACCOUNT" | "USER_SETTINGS_OVERRIDE_APPLY" | "USER_SETTINGS_OVERRIDE_CLEAR" | "USER_SETTINGS_PROTO_ENQUEUE_UPDATE" | "USER_SETTINGS_PROTO_LOAD_IF_NECESSARY" | "USER_SETTINGS_PROTO_UPDATE" | "USER_SETTINGS_PROTO_UPDATE_EDIT_INFO" | "USER_SETTINGS_RESET_ALL_PENDING" | "USER_SETTINGS_RESET_ALL_TRY_IT_OUT" | "USER_SETTINGS_RESET_PENDING_ACCOUNT_CHANGES" | "USER_SETTINGS_RESET_PENDING_AVATAR_DECORATION" | "USER_SETTINGS_RESET_PENDING_PROFILE_CHANGES" | "USER_SOUNDBOARD_SET_VOLUME" | "USER_TENURE_REWARD_STATUS_DELETE" | "USER_TENURE_REWARD_STATUS_RESET" | "USER_TENURE_REWARD_SYNC_START" | "USER_TENURE_REWARD_SYNC_SUCCESS" | "USER_UPDATE" | "VERIFY_FAILURE" | "VERIFY_SUCCESS" | "VIDEO_BACKGROUND_SHOW_FEEDBACK" | "VIDEO_FILTER_ASSETS_FETCH_SUCCESS" | "VIDEO_FILTER_ASSET_DELETE_SUCCESS" | "VIDEO_FILTER_ASSET_UPLOAD_SUCCESS" | "VIDEO_SAVE_LAST_USED_BACKGROUND_OPTION" | "VIEW_HISTORY_MARK_VIEW" | "VOICE_CATEGORY_COLLAPSE" | "VOICE_CATEGORY_EXPAND" | "VOICE_CHANNEL_EFFECT_CLEAR" | "VOICE_CHANNEL_EFFECT_RECENT_EMOJI" | "VOICE_CHANNEL_EFFECT_SEND" | "VOICE_CHANNEL_EFFECT_SENT_LOCAL" | "VOICE_CHANNEL_EFFECT_TOGGLE_ANIMATION_TYPE" | "VOICE_CHANNEL_EFFECT_UPDATE_TIME_STAMP" | "VOICE_CHANNEL_SELECT" | "VOICE_CHANNEL_SHOW_FEEDBACK" | "VOICE_CHANNEL_STATUS_UPDATE" | "VOICE_SERVER_UPDATE" | "VOICE_STATE_UPDATES" | "WAIT_FOR_REMOTE_SESSION" | "WEBHOOKS_FETCHING" | "WEBHOOKS_UPDATE" | "WEBHOOK_CREATE" | "WEBHOOK_DELETE" | "WEBHOOK_UPDATE" | "WELCOME_SCREEN_FETCH_FAIL" | "WELCOME_SCREEN_FETCH_START" | "WELCOME_SCREEN_FETCH_SUCCESS" | "WELCOME_SCREEN_SETTINGS_CLEAR" | "WELCOME_SCREEN_SETTINGS_RESET" | "WELCOME_SCREEN_SETTINGS_UPDATE" | "WELCOME_SCREEN_SUBMIT" | "WELCOME_SCREEN_SUBMIT_FAILURE" | "WELCOME_SCREEN_SUBMIT_SUCCESS" | "WELCOME_SCREEN_UPDATE" | "WELCOME_SCREEN_VIEW" | "WINDOW_FOCUS" | "WINDOW_FULLSCREEN_CHANGE" | "WINDOW_HIDDEN" | "WINDOW_INIT" | "WINDOW_RESIZED" | "WINDOW_UNLOAD" | "WINDOW_VISIBILITY_CHANGE" | "WRITE_CACHES"; diff --git a/src/webpack/common/types/i18nMessages.ts b/src/webpack/common/types/i18nMessages.ts index fdf677fc0..d18d801fd 100644 --- a/src/webpack/common/types/i18nMessages.ts +++ b/src/webpack/common/types/i18nMessages.ts @@ -19,6 +19,6 @@ import { LiteralUnion } from "type-fest"; export type i18nMessages = LiteralUnion< - "TITLE_BAR_CLOSE_WINDOW" | "TITLE_BAR_MAXIMIZE_WINDOW" | "TITLE_BAR_MINIMIZE_WINDOW" | "en-US" | "en-GB" | "zh-CN" | "zh-TW" | "cs" | "da" | "nl" | "fr" | "de" | "el" | "hu" | "it" | "ja" | "ko" | "pl" | "pt-BR" | "ru" | "es-ES" | "sv-SE" | "tr" | "bg" | "uk" | "fi" | "no" | "hr" | "ro" | "lt" | "th" | "vi" | "hi" | "KEYBIND_DESCRIPTION_MODAL_NAVIGATE_SERVERS" | "KEYBIND_DESCRIPTION_MODAL_NAVIGATE_CHANNELS" | "KEYBIND_DESCRIPTION_MODAL_NAVIGATE_BACK_FORWARD" | "KEYBIND_DESCRIPTION_MODAL_UNREAD_CHANNELS" | "KEYBIND_DESCRIPTION_MODAL_UNREAD_MENTION_CHANNELS" | "KEYBIND_DESCRIPTION_MODAL_NAVIGATE_TO_CALL" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_PREVIOUS_GUILD" | "KEYBIND_DESCRIPTION_MODAL_QUICKSWITCHER" | "KEYBIND_DESCRIPTION_MODAL_CREATE_GUILD" | "DND_OPERATION_LABEL_START" | "DND_OPERATION_LABEL_MOVE" | "DND_OPERATION_LABEL_DROP" | "DND_OPERATION_LABEL_CANCEL" | "KEYBIND_DESCRIPTION_MODAL_MARK_SERVER_READ" | "KEYBIND_DESCRIPTION_MODAL_MARK_CHANNEL_READ" | "KEYBIND_DESCRIPTION_MODAL_CREATE_DM_GROUP" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_PINS" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_INBOX" | "KEYBIND_DESCRIPTION_MODAL_MARK_TOP_INBOX_CHANNEL_READ" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_USERS" | "KEYBIND_DESCRIPTION_MODAL_SEARCH_EMOJIS" | "KEYBIND_DESCRIPTION_MODAL_SEARCH_GIFS" | "KEYBIND_DESCRIPTION_MODAL_SEARCH_STICKERS" | "KEYBIND_DESCRIPTION_MODAL_SCROLL_CHAT" | "KEYBIND_DESCRIPTION_MODAL_JUMP_TO_FIRST_UNREAD" | "KEYBIND_DESCRIPTION_MODAL_FOCUS_TEXT_AREA" | "KEYBIND_DESCRIPTION_MODAL_UPLOAD_FILE" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_MUTE" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_DEAFEN" | "KEYBIND_DESCRIPTION_MODAL_CALL_ACCEPT" | "KEYBIND_DESCRIPTION_MODAL_CALL_DECLINE" | "KEYBIND_DESCRIPTION_MODAL_CALL_START" | "KEYBIND_DESCRIPTION_MODAL_TOGGLE_HELP" | "KEYBIND_DESCRIPTION_MODAL_SEARCH" | "KEYBIND_DESCRIPTION_MODAL_EASTER_EGG" | "EDIT_MESSAGE" | "DELETE_MESSAGE" | "PIN_MESSAGE" | "ADD_REACTION" | "MESSAGE_ACTION_REPLY" | "COPY_TEXT" | "MARK_UNREAD" | "GUILDS_BAR_A11Y_LABEL" | "SERVERS" | "NEW" | "ADD_A_SERVER" | "GUILD_DISCOVERY_TOOLTIP" | "ACCOUNT_A11Y_LABEL" | "GUILD_SIDEBAR_A11Y_LABEL" | "UNKNOWN_USER" | "LOADING" | "LOADING_DID_YOU_KNOW" | "CONNECTING_PROBLEMS_CTA" | "TWEET_US" | "SERVER_STATUS" | "NOTIFICATION_CENTER_INCOMING_FRIEND_REQUEST" | "COMMAND_SHRUG_MESSAGE_DESCRIPTION" | "DIRECT_MESSAGES" | "GUILD_TOOLTIP_A11Y_LABEL" | "FAVORITES_GUILD_NAME" | "MEMBER_VERIFICATION_FOLDER_NAME" | "DND_DROP_ABOVE" | "DND_DROP_COMBINE" | "GUILD_FOLDER_TOOLTIP_A11Y_LABEL" | "DND_END_OF_LIST" | "GUILD_SIDEBAR_ACTIONS_BUTTON" | "PREMIUM_GUILD_TIER_3" | "PREMIUM_GUILD_SUBSCRIPTION_SUBSCRIBER_COUNT_TOOLTIP" | "CHANNELS" | "NEW_UNREADS" | "NEW_MENTIONS" | "UNMUTE" | "MUTE" | "DEAFEN" | "SET_STATUS" | "LABEL_WITH_ONLINE_STATUS" | "STATUS_ONLINE" | "USER_SETTINGS" | "CHANNEL_HEADER_BAR_A11Y_LABEL" | "TEXT_CHANNEL" | "UPLOAD_TO" | "UPLOAD_AREA_HELP" | "THREADS" | "NOTIFICATION_SETTINGS" | "PINNED_MESSAGES" | "MEMBER_LIST_SHOWN" | "SEARCH" | "SEARCH_CLEAR" | "INBOX" | "HELP" | "TEXTAREA_PLACEHOLDER" | "TEXTAREA_TEXT_CHANNEL_A11Y_LABEL" | "CHANNEL_A11Y_LABEL" | "CHANNEL_CHAT_HEADING" | "CHANNEL_MESSAGES_A11Y_LABEL" | "CHANNEL_MESSAGES_A11Y_DESCRIPTION" | "CHAT_ATTACH_UPLOAD_A_FILE" | "CHAT_ATTACH_UPLOAD_TEXT_AS_FILE" | "CREATE_THREAD" | "CHAT_ATTACH_USE_SLASH_COMMAND" | "CHAT_ATTACH_UPLOAD_OR_INVITE" | "PREMIUM_GIFT_BUTTON_TOOLTIP" | "PREMIUM_GIFT_BUTTON_LABEL" | "GIF_BUTTON_LABEL" | "STICKER_BUTTON_LABEL" | "SELECT_EMOJI" | "CHARACTER_COUNT_CLOSE_TO_LIMIT" | "MEMBERS_LIST_LANDMARK_LABEL" | "MEMBERS" | "PREMIUM_GUILD_SUBSCRIPTIONS_GOAL" | "PREMIUM_GUILD_TIER_3_SHORT" | "PREMIUM_GUILD_SUBSCRIPTIONS_PROGRESS_BAR_BLURB" | "PREMIUM_GUILD_SUBSCRIPTIONS_PROGRESS_BAR_COMPLETED_BLURB" | "PREMIUM_GUILD_SUBSCRIPTIONS_NUDGE_TOOLTIP_COMPLETE" | "PREMIUM_GUILD_SUBSCRIPTIONS_PROGRESS_BAR_TADA_ICON_ALT_TEXT" | "GUILD_EVENTS" | "GUILD_SIDEBAR_DEFAULT_CHANNEL_A11Y_LABEL" | "CHANNEL_TOOLTIP_TEXT_LIMITED" | "CREATE_INSTANT_INVITE" | "EDIT_CHANNEL" | "CHANNEL_TOOLTIP_TEXT" | "CATEGORY_A11Y_LABEL" | "CREATE_CHANNEL" | "CHANNEL_TOOLTIP_RULES" | "GUILD_SIDEBAR_ANNOUNCEMENT_CHANNEL_A11Y_LABEL" | "CHANNEL_TOOLTIP_ANNOUNCEMENTS" | "GUILD_SIDEBAR_STAGE_CHANNEL_A11Y_LABEL" | "OPEN_CHAT" | "CHANNEL_TOOLTIP_STAGE_LIMITED" | "CHANNEL_TOOLTIP_TEXT_ACTIVE_THREADS" | "THREAD_GROUP_A11Y_LABEL" | "GUILD_SIDEBAR_THREAD_A11Y_LABEL" | "GUILD_SIDEBAR_VOICE_CHANNEL_A11Y_LABEL" | "GUILD_SIDEBAR_CHANNEL_A11Y_LABEL_UNREAD" | "GUILD_SIDEBAR_VOICE_CHANNEL_A11Y_LABEL_USERS" | "CHANNEL_TOOLTIP_VOICE" | "STATUS_UNKNOWN" | "MESSAGE_A11Y_ROLE_DESCRIPTION" | "ROLE_ICON_ALT_TEXT" | "CHANNEL_MESSAGE_REPLY_A11Y_LABEL" | "IMAGE" | "MESSAGE_EDITED" | "MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL" | "EMOJI_TOOLTIP_CLICK_CTA" | "REMOVE_MESSAGE_ATTACHMENT" | "VERIFIED_BOT_TOOLTIP" | "SUPPRESS_ALL_EMBEDS" | "CHANNEL_MEMBERS_A11Y_LABEL" | "GUILD_OWNER" | "PREMIUM_GUILD_SUBSCRIPTION_TOOLTIP" | "STATUS_ONLINE_MOBILE" | "STATUS_IDLE" | "STREAMING" | "LISTENING_TO" | "WATCHING" | "COMPETING" | "PLAYING_GAME" | "STATUS_DND" | "CHAT_ATTACH_INVITE_TO_LISTEN" | "ACTIVITY_PANEL_GO_LIVE_STREAM_GAME" | "SEARCH_ANSWER_HAS_LINK" | "SEARCH_ANSWER_HAS_EMBED" | "SEARCH_ANSWER_HAS_ATTACHMENT" | "SEARCH_ANSWER_HAS_VIDEO" | "SEARCH_ANSWER_HAS_IMAGE" | "SEARCH_ANSWER_HAS_SOUND" | "SEARCH_ANSWER_HAS_STICKER" | "SEARCH_FILTER_FROM" | "SEARCH_FILTER_MENTIONS" | "SEARCH_FILTER_HAS" | "SEARCH_FILTER_FILE_TYPE" | "SEARCH_FILTER_FILE_NAME" | "SEARCH_FILTER_BEFORE" | "SEARCH_FILTER_ON" | "SEARCH_FILTER_DURING" | "SEARCH_FILTER_AFTER" | "SEARCH_FILTER_IN" | "SEARCH_FILTER_PINNED" | "MESSAGE_UTILITIES_A11Y_LABEL" | "EDIT" | "MORE" | "SEARCH_SHORTCUT_TODAY" | "SEARCH_SHORTCUT_YESTERDAY" | "SEARCH_SHORTCUT_WEEK" | "SEARCH_SHORTCUT_MONTH" | "SEARCH_SHORTCUT_YEAR" | "SEARCH_GROUP_HEADER_SEARCH_OPTIONS" | "SEARCH_GROUP_HEADER_HISTORY" | "LEARN_MORE" | "SEARCH_ANSWER_FROM" | "SEARCH_ANSWER_MENTIONS" | "SEARCH_ANSWER_HAS" | "SEARCH_ANSWER_DATE" | "SEARCH_ANSWER_IN" | "SEARCH_ANSWER_BOOLEAN" | "SEARCH_CLEAR_HISTORY" | "SEARCH_FROM_SUGGESTIONS" | "MEMBER_LIST_HIDDEN" | "SEARCH_RESULTS_SECTION_LABEL" | "SEARCH_NEWEST_SHORT" | "SEARCH_OLDEST_SHORT" | "SEARCH_MOST_RELEVANT_SHORT" | "SEARCHING" | "TOTAL_RESULTS" | "JUMP" | "REPLY_QUOTE_MESSAGE_NOT_LOADED" | "COPY_MESSAGE_LINK" | "COPY_ID" | "MESSAGE_ACTIONS_MENU_LABEL" | "ACTIVE_THREADS_POPOUT_HEADER" | "ACTIVE_THREADS_POPOUT_LINK" | "THREAD_BROWSER_TIMESTAMP_MINUTES" | "THREAD_BROWSER_TIMESTAMP_HOURS" | "THREAD_BROWSER_TIMESTAMP_DAYS" | "THREAD_BROWSER_TIMESTAMP_MORE_THAN_MONTH" | "OPEN_CHANNEL_TOPIC" | "VIDEO" | "REPLY_QUOTE_NO_TEXT_CONTENT" | "PLAY" | "NEW_MEMBER_BADGE_TOOLTIP_TEXT" | "USER_SETTINGS_MY_ACCOUNT" | "USER_SETTINGS_PROFILES" | "PRIVACY_AND_SAFETY" | "AUTHORIZED_APPS" | "AUTH_SESSIONS" | "CONNECTIONS" | "CLIPS" | "FRIEND_REQUESTS" | "BILLING_SETTINGS" | "PREMIUM" | "PREMIUM_GUILD_SUBSCRIPTION_TITLE" | "SUBSCRIPTIONS_TITLE" | "GIFT_INVENTORY" | "BILLING" | "APP_SETTINGS" | "APPEARANCE" | "ACCESSIBILITY" | "VOICE_AND_VIDEO" | "POGGERMODE" | "TEXT_AND_IMAGES" | "NOTIFICATIONS" | "KEYBINDS" | "LANGUAGE" | "USER_SETTINGS_WINDOWS_SETTINGS" | "USER_SETTINGS_LINUX_SETTINGS" | "STREAMER_MODE" | "SETTINGS_ADVANCED" | "ACTIVITY_SETTINGS" | "ACTIVITY_PRIVACY" | "REGISTERED_GAMES" | "OVERLAY" | "WHATS_NEW" | "USER_SETTINGS_HYPESQUAD" | "LOGOUT" | "BETA" | "USER_SETTINGS_ACCOUNT_PASSWORD_AND_AUTHENTICATION" | "CHANGE_PASSWORD" | "USER_SETTINGS_EDIT_USER_PROFILE" | "ACTIONS" | "HYPESQUAD_ONLINE_BADGE_TOOLTIP" | "HYPESQUAD_HOUSE_1" | "ACTIVE_DEVELOPER_BADGE_TOOLTIP" | "PROFILE_USER_BADGES" | "USER_SETTINGS_LABEL_USERNAME" | "USER_SETTINGS_ACCOUNT_EDIT_USERNAME_A11Y_LABEL" | "USER_SETTINGS_ACCOUNT_EDIT_EMAIL_A11Y_LABEL" | "USER_SETTINGS_ACCOUNT_REVEAL_EMAIL_A11Y_LABEL" | "USER_SETTINGS_ACCOUNT_HIDE_EMAIL_A11Y_LABEL" | "USER_SETTINGS_LABEL_EMAIL" | "REVEAL" | "USER_SETTINGS_ACCOUNT_REVEAL_PHONE_A11Y_LABEL" | "USER_SETTINGS_ACCOUNT_HIDE_PHONE_A11Y_LABEL" | "USER_SETTINGS_LABEL_PHONE_NUMBER" | "USER_SETTINGS_ACCOUNT_REMOVE_PHONE_A11Y_LABEL" | "REMOVE" | "USER_SETTINGS_ACCOUNT_EDIT_PHONE_A11Y_LABEL" | "TWO_FA_VIEW_BACKUP_CODES" | "TWO_FA_ENABLED" | "TWO_FA_DESCRIPTION" | "TWO_FA_DISABLED_FOR_SERVER_SUBSCRIPTION_MOD" | "MFA_SMS_AUTH_CURRENT_PHONE" | "MFA_SMS_PHONE_NUMBER_REVEAL" | "MFA_SMS_ENABLE" | "CHANGE_PHONE_NUMBER" | "MFA_SMS_AUTH" | "MFA_SMS_AUTH_SALES_PITCH" | "TWO_FA_REMOVE" | "USER_SETTINGS_ACCOUNT_REMOVAL_SECTION" | "USER_SETTINGS_ACCOUNT_REMOVAL_DESCRIPTION" | "DISABLE_ACCOUNT" | "DELETE_ACCOUNT" | "CLOSE" | "PREMIUM_BADGE_TOOLTIP" | "SELECT" | "REPLYING_TO" | "SUPER_REACTION_NITRO_TOOLTIP" | "YOURE_VIEWING_OLDER_MESSAGES" | "JUMP_TO_PRESENT" | "REACT_WITH_COUNT_A11Y_LABEL" | "SEARCH_NO_RESULTS" | "GUILD_SIDEBAR_CHANNEL_A11Y_LABEL_LIMIT" | "COPY_LINK" | "DELETE" | "GUILD_SIDEBAR_DEFAULT_CHANNEL_A11Y_LABEL_WITH_MENTIONS" | "NEW_MESSAGES" | "NEW_MESSAGES_PILL" | "JUMP_TO_LAST_UNREAD_MESSAGE" | "MARK_AS_READ", + "bg" | "cs" | "da" | "de" | "el" | "en" | "en-GB" | "en-US" | "es-419" | "es-ES" | "fi" | "fr" | "hi" | "hr" | "hu" | "it" | "ja" | "ko" | "lt" | "nl" | "no" | "pl" | "pt-BR" | "ro" | "ru" | "sv-SE" | "th" | "tr" | "uk" | "vi" | "zh-CN" | "zh-TW", string >; diff --git a/src/webpack/common/types/menu.d.ts b/src/webpack/common/types/menu.d.ts index 5ae9062c3..8a12f6c1b 100644 --- a/src/webpack/common/types/menu.d.ts +++ b/src/webpack/common/types/menu.d.ts @@ -16,12 +16,10 @@ * along with this program. If not, see . */ -import type { ComponentType, CSSProperties, MouseEvent, PropsWithChildren, ReactNode, UIEvent } from "react"; - -type RC = ComponentType>>; +import type { CSSProperties, MouseEvent, ReactNode, UIEvent } from "react"; export interface Menu { - Menu: RC<{ + Menu: AnyComponentTypeWithChildren<{ navId: string; onClose(): void; className?: string; @@ -29,31 +27,31 @@ export interface Menu { hideScroller?: boolean; onSelect?(): void; }>; - MenuSeparator: ComponentType; - MenuGroup: RC<{ + MenuSeparator: AnyComponentType; + MenuGroup: AnyComponentTypeWithChildren<{ label?: string; }>; - MenuItem: RC<{ + MenuItem: AnyComponentTypeWithChildren<{ id: string; label: ReactNode; action?(e: MouseEvent): void; - icon?: ComponentType; + icon?: AnyComponentType; color?: string; - render?: ComponentType; + render?: AnyComponentType; onChildrenScroll?: Function; childRowHeight?: number; listClassName?: string; disabled?: boolean; }>; - MenuCheckboxItem: RC<{ + MenuCheckboxItem: AnyComponentTypeWithChildren<{ id: string; label: string; checked: boolean; action?(e: MouseEvent): void; disabled?: boolean; }>; - MenuRadioItem: RC<{ + MenuRadioItem: AnyComponentTypeWithChildren<{ id: string; group: string; label: string; @@ -61,19 +59,19 @@ export interface Menu { action?(e: MouseEvent): void; disabled?: boolean; }>; - MenuControlItem: RC<{ + MenuControlItem: AnyComponentTypeWithChildren<{ id: string; interactive?: boolean; }>; - MenuSliderControl: RC<{ + MenuSliderControl: AnyComponentTypeWithChildren<{ minValue: number, maxValue: number, value: number, onChange(value: number): void, renderValue?(value: number): string, }>; - MenuSearchControl: RC<{ - query: string + MenuSearchControl: AnyComponentTypeWithChildren<{ + query: string; onChange(query: string): void; placeholder?: string; }>; diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 9ca7dfc94..496d9e87f 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -18,6 +18,7 @@ import { DraftType } from "@webpack/common"; import { Channel, Guild, Role } from "discord-types/general"; +import type * as Stores from "discord-types/stores"; import { FluxDispatcher, FluxEvents } from "./utils"; @@ -38,7 +39,6 @@ export class FluxStore { registerActionHandlers: GenericFunction; syncWith: GenericFunction; waitFor: GenericFunction; - __getLocalVars(): Record; static getAll(): FluxStore[]; } @@ -220,6 +220,20 @@ export class GuildStore extends FluxStore { getAllGuildRoles(): Record>; } +export type MessageStore = FluxStore & Omit & { + getMessages(channelId: string): any; +}; + +export type UserStore = FluxStore & Stores.UserStore; +export type SelectedChannelStore = FluxStore & Stores.SelectedChannelStore; +export type ChannelStore = FluxStore & Stores.ChannelStore; +export type GuildMemberStore = FluxStore & Stores.GuildMemberStore; + +export type RelationshipStore = FluxStore & Stores.RelationshipStore & { + /** Get the date (as a string) that the relationship was created */ + getSince(userId: string): string; +}; + export class ThemeStore extends FluxStore { theme: "light" | "dark" | "darker" | "midnight"; darkSidebar: boolean; @@ -229,8 +243,8 @@ export class ThemeStore extends FluxStore { } export type useStateFromStores = ( - stores: t.FluxStore[], + stores: FluxStore[], mapper: () => T, - dependencies?: any, + dependencies?: any[] | null, isEqual?: (old: T, newer: T) => boolean ) => T; diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index dd76d1ade..e8b01aed8 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Guild, GuildMember, User } from "discord-types/general"; +import { Channel, Guild, GuildMember, User } from "discord-types/general"; import type { ReactNode } from "react"; import { LiteralUnion } from "type-fest"; @@ -84,53 +84,57 @@ interface RestRequestData { export type RestAPI = Record<"del" | "get" | "patch" | "post" | "put", (data: RestRequestData) => Promise>; -export type Permissions = "CREATE_INSTANT_INVITE" - | "KICK_MEMBERS" - | "BAN_MEMBERS" +export type Permissions = "ADD_REACTIONS" | "ADMINISTRATOR" - | "MANAGE_CHANNELS" - | "MANAGE_GUILD" + | "ATTACH_FILES" + | "BAN_MEMBERS" | "CHANGE_NICKNAME" + | "CONNECT" + | "CREATE_EVENTS" + | "CREATE_GUILD_EXPRESSIONS" + | "CREATE_INSTANT_INVITE" + | "CREATE_PRIVATE_THREADS" + | "CREATE_PUBLIC_THREADS" + | "DEAFEN_MEMBERS" + | "EMBED_LINKS" + | "KICK_MEMBERS" + | "MANAGE_CHANNELS" + | "MANAGE_EVENTS" + | "MANAGE_GUILD" + | "MANAGE_GUILD_EXPRESSIONS" + | "MANAGE_MESSAGES" | "MANAGE_NICKNAMES" | "MANAGE_ROLES" + | "MANAGE_THREADS" | "MANAGE_WEBHOOKS" - | "MANAGE_GUILD_EXPRESSIONS" - | "CREATE_GUILD_EXPRESSIONS" + | "MENTION_EVERYONE" + | "MODERATE_MEMBERS" + | "MOVE_MEMBERS" + | "MUTE_MEMBERS" + | "PRIORITY_SPEAKER" + | "READ_MESSAGE_HISTORY" + | "REQUEST_TO_SPEAK" + | "SEND_MESSAGES" + | "SEND_MESSAGES_IN_THREADS" + | "SEND_POLLS" + | "SEND_TTS_MESSAGES" + | "SEND_VOICE_MESSAGES" + | "SET_VOICE_CHANNEL_STATUS" + | "SPEAK" + | "STREAM" + | "USE_APPLICATION_COMMANDS" + | "USE_CLYDE_AI" + | "USE_EMBEDDED_ACTIVITIES" + | "USE_EXTERNAL_APPS" + | "USE_EXTERNAL_EMOJIS" + | "USE_EXTERNAL_SOUNDS" + | "USE_EXTERNAL_STICKERS" + | "USE_SOUNDBOARD" + | "USE_VAD" | "VIEW_AUDIT_LOG" | "VIEW_CHANNEL" - | "VIEW_GUILD_ANALYTICS" | "VIEW_CREATOR_MONETIZATION_ANALYTICS" - | "MODERATE_MEMBERS" - | "SEND_MESSAGES" - | "SEND_TTS_MESSAGES" - | "MANAGE_MESSAGES" - | "EMBED_LINKS" - | "ATTACH_FILES" - | "READ_MESSAGE_HISTORY" - | "MENTION_EVERYONE" - | "USE_EXTERNAL_EMOJIS" - | "ADD_REACTIONS" - | "USE_APPLICATION_COMMANDS" - | "MANAGE_THREADS" - | "CREATE_PUBLIC_THREADS" - | "CREATE_PRIVATE_THREADS" - | "USE_EXTERNAL_STICKERS" - | "SEND_MESSAGES_IN_THREADS" - | "SEND_VOICE_MESSAGES" - | "CONNECT" - | "SPEAK" - | "MUTE_MEMBERS" - | "DEAFEN_MEMBERS" - | "MOVE_MEMBERS" - | "USE_VAD" - | "PRIORITY_SPEAKER" - | "STREAM" - | "USE_EMBEDDED_ACTIVITIES" - | "USE_SOUNDBOARD" - | "USE_EXTERNAL_SOUNDS" - | "REQUEST_TO_SPEAK" - | "MANAGE_EVENTS" - | "CREATE_EVENTS"; + | "VIEW_GUILD_ANALYTICS"; export type PermissionsBits = Record; @@ -217,6 +221,18 @@ export interface IconUtils { getAnimatableSourceWithFallback: any; } +export interface UserUtils { + getUser: (id: string) => Promise; +} + +export interface UploadHandler { + promptToUpload: (files: File[], channel: Channel, draftType: Number) => void; +} + +export interface ApplicationAssetUtils { + fetchAssetIds: (applicationId: string, e: string[]) => Promise; +} + export interface Constants { Endpoints: Record; UserFlags: Record; @@ -225,7 +241,7 @@ export interface Constants { export type ActiveView = LiteralUnion<"emoji" | "gif" | "sticker" | "soundboard", string>; -export interface ExpressionPickerStoreState extends Record { +export interface ExpressionPickerStoreState extends AnyRecord { activeView: ActiveView | null; lastActiveView: ActiveView | null; activeViewType: any | null; diff --git a/src/webpack/common/userSettings.ts b/src/webpack/common/userSettings.ts index b00620b27..6eb266cc2 100644 --- a/src/webpack/common/userSettings.ts +++ b/src/webpack/common/userSettings.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findLazy } from "@webpack"; +import { find } from "@webpack"; export const UserSettingsActionCreators = { - FrecencyUserSettingsActionCreators: findLazy(m => m.ProtoClass?.typeName?.endsWith(".FrecencyUserSettings")), - PreloadedUserSettingsActionCreators: findLazy(m => m.ProtoClass?.typeName?.endsWith(".PreloadedUserSettings")), + FrecencyUserSettingsActionCreators: find(m => m.ProtoClass?.typeName?.endsWith(".FrecencyUserSettings")), + PreloadedUserSettingsActionCreators: find(m => m.ProtoClass?.typeName?.endsWith(".PreloadedUserSettings")), }; diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index b557f4da2..223e09489 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,55 +16,49 @@ * along with this program. If not, see . */ -import type { Channel } from "discord-types/general"; +import { _resolveDiscordLoaded, filters, find, findByCode, findByProps, mapMangledModule, waitFor } from "@webpack"; -// 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; +export const FluxDispatcher = findByProps("dispatch", "subscribe", (m: t.FluxDispatcher) => { // Non import call to avoid circular dependency Vencord.Plugins.subscribeAllPluginsFluxEvents(m); const cb = () => { m.unsubscribe("CONNECTION_OPEN", cb); - _resolveReady(); + _resolveDiscordLoaded(); }; m.subscribe("CONNECTION_OPEN", cb); + + return m; }); -export let ComponentDispatch; -waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m); +export const ComponentDispatch = findByProps("dispatchToLastSubscribed"); -export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', { +export const Constants: t.Constants = mapMangledModule('ME:"/users/@me"', { Endpoints: filters.byProps("USER", "ME"), UserFlags: filters.byProps("STAFF", "SPAMMER"), FriendsSections: m => m.PENDING === "PENDING" && m.ADD_FRIEND }); -export const RestAPI: t.RestAPI = findLazy(m => typeof m === "object" && m.del && m.put); -export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear"); +export const RestAPI = find(m => typeof m === "object" && m.del && m.put); +export const moment = findByProps("parseTwoDigitYear"); -export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage"); +export const hljs = findByProps("highlight", "registerLanguage"); -export const { match, P }: Pick = mapMangledModuleLazy("@ts-pattern/matcher", { +export const { match, P }: Pick = mapMangledModule("@ts-pattern/matcher", { match: filters.byCode("return new"), P: filters.byProps("when") }); -export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep"); +export const lodash = findByProps("debounce", "cloneDeep"); -export const i18n: t.i18n = findLazy(m => m.Messages?.["en-US"]); +export const i18n = find(m => m.Messages?.["en-US"]); -export let SnowflakeUtils: t.SnowflakeUtils; -waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m); +export const SnowflakeUtils = findByProps("fromTimestamp", "extractTimestamp"); -export let Parser: t.Parser; -waitFor("parseTopic", m => Parser = m); -export let Alerts: t.Alerts; -waitFor(["show", "close"], m => Alerts = m); +export const Parser = findByProps("parseTopic"); +export const Alerts = findByProps("show", "close"); const ToastType = { MESSAGE: 0, @@ -110,14 +104,12 @@ export const Toasts = { } }; -// This is the same module but this is easier -waitFor("showToast", m => { +waitFor(filters.byProps("showToast"), m => { Toasts.show = m.showToast; Toasts.pop = m.popToast; Toasts.create = m.createToast; }); - /** * Show a simple toast. If you need more options, use Toasts.show manually */ @@ -125,48 +117,44 @@ export function showToast(message: string, type = ToastType.MESSAGE, options?: T Toasts.show(Toasts.create(message, type, options)); } -export const UserUtils = { - getUser: findByCodeLazy(".USER(") +export const UserUtils: t.UserUtils = { + getUser: findByCode(".USER(") }; -export const UploadManager = findByPropsLazy("clearAll", "addFile"); -export const UploadHandler = { - promptToUpload: findByCodeLazy(".ATTACHMENT_TOO_MANY_ERROR_TITLE,") as (files: File[], channel: Channel, draftType: Number) => void +export const UploadManager = findByProps("clearAll", "addFile"); +export const UploadHandler: t.UploadHandler = { + promptToUpload: findByCode(".ATTACHMENT_TOO_MANY_ERROR_TITLE,") }; -export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetImage") as { - fetchAssetIds: (applicationId: string, e: string[]) => Promise; -}; +export const ApplicationAssetUtils = findByProps("fetchAssetIds", "getAssetImage"); -export const Clipboard: t.Clipboard = mapMangledModuleLazy('queryCommandEnabled("copy")', { +export const Clipboard: t.Clipboard = mapMangledModule('queryCommandEnabled("copy")', { copy: filters.byCode(".copy("), SUPPORTS_COPY: e => typeof e === "boolean" }); -export const NavigationRouter: t.NavigationRouter = mapMangledModuleLazy("Transitioning to ", { +export const NavigationRouter: t.NavigationRouter = mapMangledModule("Transitioning to ", { transitionTo: filters.byCode("transitionTo -"), transitionToGuild: filters.byCode("transitionToGuild -"), back: filters.byCode("goBack()"), forward: filters.byCode("goForward()"), }); -export let SettingsRouter: any; -waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); +export const SettingsRouter = findByProps("open", "saveAccountChanges"); -export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); +export const PermissionsBits = find(m => typeof m.ADMINISTRATOR === "bigint"); -export const zustandCreate = findByCodeLazy("will be removed in v4"); +export const zustandCreate = findByCode("will be removed in v4"); +export const zustandPersist = findByCode("[zustand persist middleware]"); -export const zustandPersist = findByCodeLazy("[zustand persist middleware]"); +export const MessageActions = findByProps("editMessage", "sendMessage"); +export const MessageCache = findByProps("clearCache", "_channelMessages"); +export const UserProfileActions = findByProps("openUserProfileModal", "closeUserProfileModal"); +export const InviteActions = findByProps("resolveInvite"); -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 IconUtils = findByProps("getGuildBannerURL", "getUserAvatarURL"); -export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); - -export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", { +export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModule("expression-picker-last-active-view", { openExpressionPicker: filters.byCode(/setState\({activeView:(?:(?!null)\i),activeViewType:/), closeExpressionPicker: filters.byCode("setState({activeView:null"), toggleMultiExpressionPicker: filters.byCode(".EMOJI,"), @@ -176,14 +164,14 @@ export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLa useExpressionPickerStore: filters.byCode("Object.is") }); -export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', { +export const PopoutActions: t.PopoutActions = mapMangledModule('type:"POPOUT_WINDOW_OPEN"', { open: filters.byCode('type:"POPOUT_WINDOW_OPEN"'), close: filters.byCode('type:"POPOUT_WINDOW_CLOSE"'), setAlwaysOnTop: filters.byCode('type:"POPOUT_WINDOW_SET_ALWAYS_ON_TOP"'), }); -export const UsernameUtils: t.UsernameUtils = findByPropsLazy("useName", "getGlobalName"); -export const DisplayProfileUtils: t.DisplayProfileUtils = mapMangledModuleLazy(/=\i\.getUserProfile\(\i\),\i=\i\.getGuildMemberProfile\(/, { +export const UsernameUtils = findByProps("useName", "getGlobalName"); +export const DisplayProfileUtils: t.DisplayProfileUtils = mapMangledModule(/=\i\.getUserProfile\(\i\),\i=\i\.getGuildMemberProfile\(/, { getDisplayProfile: filters.byCode(".getGuildMemberProfile("), useDisplayProfile: filters.byCode(/\[\i\.\i,\i\.\i],\(\)=>/) }); diff --git a/src/webpack/index.ts b/src/webpack/index.ts index 036c2a3fc..77c57da6f 100644 --- a/src/webpack/index.ts +++ b/src/webpack/index.ts @@ -16,5 +16,6 @@ * along with this program. If not, see . */ +export * from "./api"; export * as Common from "./common"; -export * from "./webpack"; +export * from "./wreq.d"; diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index fb640cea8..4156f0c13 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -1,370 +1,516 @@ /* - * 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 . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated, Nuckyz, and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ -import { WEBPACK_CHUNK } from "@utils/constants"; +import { Settings } from "@api/Settings"; import { Logger } from "@utils/Logger"; -import { canonicalizeReplacement } from "@utils/patches"; +import { interpolateIfDefined } from "@utils/misc"; import { PatchReplacement } from "@utils/types"; -import { WebpackInstance } from "discord-types/other"; -import { traceFunction } from "../debug/Tracer"; +import { traceFunctionWithResults } from "../debug/Tracer"; import { patches } from "../plugins"; -import { _initWebpack, beforeInitListeners, factoryListeners, moduleListeners, subscriptions, wreq } from "."; +import { _initWebpack, AnyModuleFactory, AnyWebpackRequire, factoryListeners, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from "."; const logger = new Logger("WebpackInterceptor", "#8caaee"); -let webpackChunk: any[]; +/** A set with all the Webpack instances */ +export const allWebpackInstances = new Set(); +/** Whether we tried to fallback to factory WebpackRequire, or disabled patches */ +let wreqFallbackApplied = false; -// Patch the window webpack chunk setter to monkey patch the push method before any chunks are pushed -// This way we can patch the factory of everything being pushed to the modules array -Object.defineProperty(window, WEBPACK_CHUNK, { - configurable: true, +export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: string | RegExp, totalTime: number]>; - get: () => webpackChunk, - set: v => { - if (v?.push) { - if (!v.push.$$vencordOriginal) { - logger.info(`Patching ${WEBPACK_CHUNK}.push`); - patchPush(v); - - // @ts-ignore - delete window[WEBPACK_CHUNK]; - window[WEBPACK_CHUNK] = v; - } - } - - webpackChunk = v; +type Define = typeof Reflect.defineProperty; +const define: Define = (target, p, attributes) => { + if (Object.hasOwn(attributes, "value")) { + attributes.writable = true; } -}); -// wreq.m is the webpack module factory. -// normally, this is populated via webpackGlobal.push, which we patch below. -// However, Discord has their .m prepopulated. -// Thus, we use this hack to immediately access their wreq.m and patch all already existing factories -Object.defineProperty(Function.prototype, "m", { - configurable: true, + return Reflect.defineProperty(target, p, { + configurable: true, + enumerable: true, + ...attributes + }); +}; - set(v: any) { - Object.defineProperty(this, "m", { - value: v, - configurable: true, - enumerable: true, - writable: true - }); +// wreq.m is the Webpack object containing module factories. It is pre-populated with module factories, and is also populated via webpackGlobal.push +// We use this setter to intercept when wreq.m is defined and apply the patching in its module factories. +// We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or definining getters for the patched versions. - // When using react devtools or other extensions, we may also catch their webpack here. - // This ensures we actually got the right one +// If this is the main Webpack, we also set up the internal references to WebpackRequire. +define(Function.prototype, "m", { + enumerable: false, + + set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) { + define(this, "m", { value: originalModules }); + + // Ensure this is one of Discord main Webpack instances. + // We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here. const { stack } = new Error(); - if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || Array.isArray(v)) { + if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) { return; } - const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; - logger.info("Found Webpack module factory", fileName); + const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1]; + logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`); - patchFactories(v); + allWebpackInstances.add(this); - // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property. + // Define a setter for the ensureChunk property of WebpackRequire. Only the main Webpack (which is the only that includes chunk loading) has this property. // So if the setter is called, this means we can initialize the internal references to WebpackRequire. - Object.defineProperty(this, "p", { - configurable: true, - - set(this: WebpackInstance, bundlePath: string) { - Object.defineProperty(this, "p", { - value: bundlePath, - configurable: true, - enumerable: true, - writable: true - }); + define(this, "e", { + enumerable: false, + set(this: WebpackRequire, ensureChunk: WebpackRequire["e"]) { + define(this, "e", { value: ensureChunk }); clearTimeout(setterTimeout); - if (bundlePath !== "/assets/") return; - logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); + logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire"); _initWebpack(this); - - for (const beforeInitListener of beforeInitListeners) { - beforeInitListener(this); - } } }); // setImmediate to clear this property setter if this is not the main Webpack. - // If this is the main Webpack, wreq.p will always be set before the timeout runs. - const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); + // If this is the main Webpack, wreq.e will always be set before the timeout runs. + const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0); + + // Patch the pre-populated factories + for (const id in originalModules) { + if (updateExistingFactory(originalModules, id, originalModules[id], true)) { + continue; + } + + notifyFactoryListeners(originalModules[id]); + defineModulesFactoryGetter(id, Settings.eagerPatches ? wrapAndPatchFactory(id, originalModules[id]) : originalModules[id]); + } + + define(originalModules, Symbol.toStringTag, { + value: "ModuleFactories", + enumerable: false + }); + + // The proxy responsible for patching the module factories when they are set, or definining getters for the patched versions + const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler); + /* + If Discord ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype + Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler)); + */ + + define(this, "m", { value: proxiedModuleFactories }); } }); -function patchPush(webpackGlobal: any) { - function handlePush(chunk: any) { - try { - patchFactories(chunk[1]); - } catch (err) { - logger.error("Error in handlePush", err); +const moduleFactoriesHandler: ProxyHandler = { + /* + If Discord ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype + and that requires defining additional traps for keeping the object working + + // Proxies on the prototype dont intercept "get" when the property is in the object itself. But in case it isn't we need to return undefined, + // to avoid Reflect.get having no effect and causing a stack overflow + get: (target, p, receiver) => { + return undefined; + }, + // Same thing as get + has: (target, p) => { + return false; + }, + */ + + // The set trap for patching or defining getters for the module factories when new module factories are loaded + set: (target, p, newValue, receiver) => { + // If the property is not a number, we are not dealing with a module factory + if (Number.isNaN(Number(p))) { + return define(target, p, { value: newValue }); } - return handlePush.$$vencordOriginal.call(webpackGlobal, chunk); + if (updateExistingFactory(target, p, newValue)) { + return true; + } + + notifyFactoryListeners(newValue); + defineModulesFactoryGetter(p, Settings.eagerPatches ? wrapAndPatchFactory(p, newValue) : newValue); + + return true; + } +}; + +/** + * Update a factory that exists in any Webpack instance with a new original factory. + * + * @target The module factories where this new original factory is being set + * @param id The id of the module + * @param newFactory The new original factory + * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget + * @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance + */ +function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) { + let existingFactory: TypedPropertyDescriptor | undefined; + let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined; + for (const wreq of allWebpackInstances) { + if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue; + + if (Reflect.getOwnPropertyDescriptor(wreq.m, id) != null) { + existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id); + moduleFactoriesWithFactory = wreq.m; + break; + } } - handlePush.$$vencordOriginal = webpackGlobal.push; - handlePush.toString = handlePush.$$vencordOriginal.toString.bind(handlePush.$$vencordOriginal); - // Webpack overwrites .push with its own push like so: `d.push = n.bind(null, d.push.bind(d));` - // it wraps the old push (`d.push.bind(d)`). this old push is in this case our handlePush. - // If we then repatched the new push, we would end up with recursive patching, which leads to our patches - // being applied multiple times. - // Thus, override bind to use the original push - handlePush.bind = (...args: unknown[]) => handlePush.$$vencordOriginal.bind(...args); + if (existingFactory != null) { + // If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required. + // So define the descriptor of it on this current Webpack instance (if it doesn't exist already), call Reflect.set with the new original, + // and let the correct logic apply (normal set, or defineModuleFactoryGetter setter) - Object.defineProperty(webpackGlobal, "push", { - configurable: true, - - get: () => handlePush, - set(v) { - handlePush.$$vencordOriginal = v; + if (moduleFactoriesWithFactory !== moduleFactoriesTarget) { + Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory); } - }); + + // Persist $$vencordPatchedSource in the new original factory, if the patched one has already been required + if (IS_DEV && existingFactory.value != null) { + newFactory.$$vencordPatchedSource = existingFactory.value.$$vencordPatchedSource; + } + + return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget); + } + + return false; } -let webpackNotInitializedLogged = false; +/** + * Notify all factory listeners. + * + * @param factory The original factory to notify for + */ +function notifyFactoryListeners(factory: AnyModuleFactory) { + for (const factoryListener of factoryListeners) { + try { + factoryListener(factory); + } catch (err) { + logger.error("Error in Webpack factory listener:\n", err, factoryListener); + } + } +} -function patchFactories(factories: Record void>) { - for (const id in factories) { - let mod = factories[id]; +/** + * Define the getter for returning the patched version of the module factory. + * + * If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original + * and only be patched when accessed for the first time. + * + * @param id The id of the module + * @param factory The original or patched module factory + */ +function defineModulesFactoryGetter(id: PropertyKey, factory: WrappedModuleFactory) { + const descriptor: PropertyDescriptor = { + get() { + // $$vencordOriginal means the factory is already patched + if (factory.$$vencordOriginal != null) { + return factory; + } - const originalMod = mod; - const patchedBy = new Set(); + return (factory = wrapAndPatchFactory(id, factory)); + }, + set(newFactory: AnyModuleFactory) { + if (IS_DEV && factory.$$vencordPatchedSource != null) { + newFactory.$$vencordPatchedSource = factory.$$vencordPatchedSource; + } - const factory = factories[id] = function (module: any, exports: any, require: WebpackInstance) { - if (wreq == null && IS_DEV) { - if (!webpackNotInitializedLogged) { - webpackNotInitializedLogged = true; + if (factory.$$vencordOriginal != null) { + factory.toString = newFactory.toString.bind(newFactory); + factory.$$vencordOriginal = newFactory; + } else { + factory = newFactory; + } + } + }; + + // Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object + // have the patched version + for (const wreq of allWebpackInstances) { + define(wreq.m, id, descriptor); + } +} + +/** + * Wraps and patches a module factory. + * + * @param id The id of the module + * @param factory The original or patched module factory + * @returns The wrapper for the patched module factory + */ +function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory) { + const patchedFactory = patchFactory(id, originalFactory); + + const wrappedFactory: WrappedModuleFactory = function (...args) { + // Restore the original factory in all the module factories objects. We want to make sure the original factory is restored properly, no matter what is the Webpack instance + for (const wreq of allWebpackInstances) { + define(wreq.m, id, { value: wrappedFactory.$$vencordOriginal }); + } + + // eslint-disable-next-line prefer-const + let [module, exports, require] = args; + + if (wreq == null) { + if (!wreqFallbackApplied) { + wreqFallbackApplied = true; + + // Make sure the require argument is actually the WebpackRequire function + if (typeof require === "function" && require.m != null) { + const { stack } = new Error(); + const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; + + logger.warn( + "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called patched module factory (" + + `id: ${String(id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + + ")" + ); + + _initWebpack(require as WebpackRequire); + } else if (IS_DEV) { logger.error("WebpackRequire was not initialized, running modules without patches instead."); + return wrappedFactory.$$vencordOriginal!.apply(this, args); } + } else if (IS_DEV) { + return wrappedFactory.$$vencordOriginal!.apply(this, args); + } + } - return void originalMod(module, exports, require); + let factoryReturn: unknown; + try { + // Call the patched factory + factoryReturn = patchedFactory.apply(this, args); + } catch (err) { + // Just re-throw Discord errors + if (patchedFactory === originalFactory) { + throw err; } - try { - mod(module, exports, require); - } catch (err) { - // Just rethrow discord errors - if (mod === originalMod) throw err; + logger.error("Error in patched module factory:\n", err); + return wrappedFactory.$$vencordOriginal!.apply(this, args); + } - logger.error("Error in patched module", err); - return void originalMod(module, exports, require); - } + exports = module.exports; + if (exports == null) return factoryReturn; - exports = module.exports; + // There are (at the time of writing) 11 modules exporting the window + // Make these non enumerable to improve webpack search performance + if (typeof require === "function") { + let foundWindow = false; - if (!exports) return; - - // There are (at the time of writing) 11 modules exporting the window - // Make these non enumerable to improve webpack search performance - if (require.c) { - let foundWindow = false; - - if (exports === window) { + if (exports === window) { + foundWindow = true; + } else if (typeof exports === "object") { + if (exports.default === window) { foundWindow = true; - } else if (typeof exports === "object") { - if (exports?.default === window) { - foundWindow = true; - } else { - for (const nested in exports) if (nested.length <= 3) { - if (exports[nested] === window) { - foundWindow = true; - } + } else { + for (const exportKey in exports) if (exportKey.length <= 3) { + if (exports[exportKey] === window) { + foundWindow = true; } } } + } - if (foundWindow) { + if (foundWindow) { + if (require.c != null) { Object.defineProperty(require.c, id, { value: require.c[id], enumerable: false, configurable: true, writable: true }); - - return; } - } - for (const callback of moduleListeners) { - try { - callback(exports, id); - } catch (err) { - logger.error("Error in Webpack module listener:\n", err, callback); - } - } - - for (const [filter, callback] of subscriptions) { - try { - if (exports && filter(exports)) { - subscriptions.delete(filter); - callback(exports, id); - } else if (typeof exports === "object") { - if (exports.default && filter(exports.default)) { - subscriptions.delete(filter); - callback(exports.default, id); - } else { - for (const nested in exports) if (nested.length <= 3) { - if (exports[nested] && filter(exports[nested])) { - subscriptions.delete(filter); - callback(exports[nested], id); - } - } - } - } - } catch (err) { - logger.error("Error while firing callback for Webpack subscription:\n", err, filter, callback); - } - } - } as any as { toString: () => string, original: any, (...args: any[]): void; $$vencordPatchedSource?: string; }; - - factory.toString = originalMod.toString.bind(originalMod); - factory.original = originalMod; - - for (const factoryListener of factoryListeners) { - try { - factoryListener(originalMod); - } catch (err) { - logger.error("Error in Webpack factory listener:\n", err, factoryListener); + return factoryReturn; } } - // Discords Webpack chunks for some ungodly reason contain random - // newlines. Cyn recommended this workaround and it seems to work fine, - // however this could potentially break code, so if anything goes weird, - // this is probably why. - // Additionally, `[actual newline]` is one less char than "\n", so if Discord - // ever targets newer browsers, the minifier could potentially use this trick and - // cause issues. - // - // 0, prefix is to turn it into an expression: 0,function(){} would be invalid syntax without the 0, - let code: string = "0," + mod.toString().replaceAll("\n", ""); + for (const callback of moduleListeners) { + try { + callback(exports, { id, factory: wrappedFactory.$$vencordOriginal! }); + } catch (err) { + logger.error("Error in Webpack module listener:\n", err, callback); + } + } - for (let i = 0; i < patches.length; i++) { - const patch = patches[i]; - - const moduleMatches = typeof patch.find === "string" - ? code.includes(patch.find) - : patch.find.test(code); - - if (!moduleMatches) continue; - - patchedBy.add(patch.plugin); - - const executePatch = traceFunction(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => code.replace(match, replace)); - const previousMod = mod; - const previousCode = code; - - // We change all patch.replacement to array in plugins/index - for (const replacement of patch.replacement as PatchReplacement[]) { - const lastMod = mod; - const lastCode = code; - - canonicalizeReplacement(replacement, patch.plugin); - - try { - const newCode = executePatch(replacement.match, replacement.replace as string); - if (newCode === code) { - if (!patch.noWarn) { - logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`); - if (IS_DEV) { - logger.debug("Function Source:\n", code); - } - } - - if (patch.group) { - logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`); - mod = previousMod; - code = previousCode; - patchedBy.delete(patch.plugin); - break; - } - - continue; + for (const [filter, callback] of waitForSubscriptions) { + try { + if (filter.$$vencordIsFactoryFilter) { + if (filter(wrappedFactory.$$vencordOriginal!)) { + waitForSubscriptions.delete(filter); + callback(exports, { id, exportKey: null, factory: wrappedFactory.$$vencordOriginal! }); } - code = newCode; - mod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`); - } catch (err) { - logger.error(`Patch by ${patch.plugin} errored (Module id is ${id}): ${replacement.match}\n`, err); + continue; + } - if (IS_DEV) { - const changeSize = code.length - lastCode.length; - const match = lastCode.match(replacement.match)!; + if (filter(exports)) { + waitForSubscriptions.delete(filter); + callback(exports, { id, exportKey: null, factory: wrappedFactory.$$vencordOriginal! }); + continue; + } - // Use 200 surrounding characters of context - const start = Math.max(0, match.index! - 200); - const end = Math.min(lastCode.length, match.index! + match[0].length + 200); - // (changeSize may be negative) - const endPatched = end + changeSize; + if (typeof exports !== "object") { + continue; + } - const context = lastCode.slice(start, end); - const patchedContext = code.slice(start, endPatched); + if (exports.default != null && filter(exports.default)) { + waitForSubscriptions.delete(filter); + callback(exports.default, { id, exportKey: "default", factory: wrappedFactory.$$vencordOriginal! }); + continue; + } - // inline require to avoid including it in !IS_DEV builds - const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext); - let fmt = "%c %s "; - const elements = [] as string[]; - for (const d of diff) { - const color = d.removed - ? "red" - : d.added - ? "lime" - : "grey"; - fmt += "%c%s"; - elements.push("color:" + color, d.value); - } + for (const exportKey in exports) if (exportKey.length <= 3) { + const exportValue = exports[exportKey]; - logger.errorCustomFmt(...Logger.makeTitle("white", "Before"), context); - logger.errorCustomFmt(...Logger.makeTitle("white", "After"), patchedContext); - const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff"); - logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements); + if (exportValue != null && filter(exportValue)) { + waitForSubscriptions.delete(filter); + callback(exportValue, { id, exportKey, factory: wrappedFactory.$$vencordOriginal! }); + break; } + } + } catch (err) { + logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); + } + } - patchedBy.delete(patch.plugin); + return factoryReturn; + }; + + wrappedFactory.toString = originalFactory.toString.bind(originalFactory); + wrappedFactory.$$vencordOriginal = originalFactory; + + if (IS_DEV && patchedFactory !== originalFactory) { + const patchedSource = String(patchedFactory); + + wrappedFactory.$$vencordPatchedSource = patchedSource; + originalFactory.$$vencordPatchedSource = patchedSource; + } + + return wrappedFactory; +} + +/** + * Patches a module factory. + * + * @param id The id of the module + * @param factory The original module factory + * @returns The patched module factory + */ +function patchFactory(id: PropertyKey, factory: AnyModuleFactory) { + // 0, prefix to turn it into an expression: 0,function(){} would be invalid syntax without the 0, + let code: string = "0," + String(factory); + let patchedFactory = factory; + + const patchedBy = new Set(); + + for (let i = 0; i < patches.length; i++) { + const patch = patches[i]; + + const moduleMatches = typeof patch.find === "string" + ? code.includes(patch.find) + : (patch.find.global && (patch.find.lastIndex = 0), patch.find.test(code)); + + if (!moduleMatches) continue; + + patchedBy.add(patch.plugin); + + const executePatch = traceFunctionWithResults(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => { + if (match instanceof RegExp && match.global) { + match.lastIndex = 0; + } + + return code.replace(match, replace); + }); + const previousCode = code; + const previousFactory = factory; + + // We change all patch.replacement to array in plugins/index + for (const replacement of patch.replacement as PatchReplacement[]) { + const lastCode = code; + const lastFactory = factory; + + try { + const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string); + + if (IS_REPORTER) { + patchTimings.push([patch.plugin, id, replacement.match, totalTime]); + } + + if (newCode === code) { + if (!patch.noWarn) { + logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(id)}): ${replacement.match}`); + if (IS_DEV) { + logger.debug("Function Source:\n", code); + } + } if (patch.group) { - logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`); - mod = previousMod; + logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`); code = previousCode; + patchedFactory = previousFactory; + patchedBy.delete(patch.plugin); break; } - mod = lastMod; - code = lastCode; + continue; } - } - if (!patch.all) patches.splice(i--, 1); - } + code = newCode; + patchedFactory = (0, eval)(`// Webpack Module ${String(id)} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(id)}`); + } catch (err) { + logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(id)}): ${replacement.match}\n`, err); - if (IS_DEV) { - if (mod !== originalMod) { - factory.$$vencordPatchedSource = String(mod); - } else if (wreq != null) { - const existingFactory = wreq.m[id]; + if (IS_DEV) { + const changeSize = code.length - lastCode.length; + const match = lastCode.match(replacement.match)!; - if (existingFactory != null) { - factory.$$vencordPatchedSource = existingFactory.$$vencordPatchedSource; + // Use 200 surrounding characters of context + const start = Math.max(0, match.index! - 200); + const end = Math.min(lastCode.length, match.index! + match[0].length + 200); + // (changeSize may be negative) + const endPatched = end + changeSize; + + const context = lastCode.slice(start, end); + const patchedContext = code.slice(start, endPatched); + + // inline require to avoid including it in !IS_DEV builds + const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext); + let fmt = "%c %s "; + const elements: string[] = []; + for (const d of diff) { + const color = d.removed + ? "red" + : d.added + ? "lime" + : "grey"; + fmt += "%c%s"; + elements.push("color:" + color, d.value); + } + + logger.errorCustomFmt(...Logger.makeTitle("white", "Before"), context); + logger.errorCustomFmt(...Logger.makeTitle("white", "After"), patchedContext); + const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff"); + logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements); } + + patchedBy.delete(patch.plugin); + + if (patch.group) { + logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`); + code = previousCode; + patchedFactory = previousFactory; + break; + } + + code = lastCode; + patchedFactory = lastFactory; } } + + if (!patch.all) patches.splice(i--, 1); } + + return patchedFactory; } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts deleted file mode 100644 index 19519d647..000000000 --- a/src/webpack/webpack.ts +++ /dev/null @@ -1,645 +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 . -*/ - -import { makeLazy, proxyLazy } from "@utils/lazy"; -import { LazyComponent } from "@utils/lazyReact"; -import { Logger } from "@utils/Logger"; -import { canonicalizeMatch } from "@utils/patches"; -import type { WebpackInstance } from "discord-types/other"; - -import { traceFunction } from "../debug/Tracer"; - -const logger = new Logger("Webpack"); - -export let _resolveReady: () => void; -/** - * Fired once a gateway connection to Discord has been established. - * This indicates that the core webpack modules have been initialised - */ -export const onceReady = new Promise(r => _resolveReady = r); - -export let wreq: WebpackInstance; -export let cache: WebpackInstance["c"]; - -export type FilterFn = (mod: any) => boolean; - -export type PropsFilter = Array; -export type CodeFilter = Array; -export type StoreNameFilter = string; - -export const stringMatches = (s: string, filter: CodeFilter) => - filter.every(f => - typeof f === "string" - ? s.includes(f) - : (f.global && (f.lastIndex = 0), f.test(s)) - ); - -export const filters = { - byProps: (...props: PropsFilter): FilterFn => - props.length === 1 - ? m => m[props[0]] !== void 0 - : m => props.every(p => m[p] !== void 0), - - byCode: (...code: CodeFilter): FilterFn => { - code = code.map(canonicalizeMatch); - return m => { - if (typeof m !== "function") return false; - return stringMatches(Function.prototype.toString.call(m), code); - }; - }, - byStoreName: (name: StoreNameFilter): FilterFn => m => - m.constructor?.displayName === name, - - componentByCode: (...code: CodeFilter): FilterFn => { - const filter = filters.byCode(...code); - return m => { - if (filter(m)) return true; - if (!m.$$typeof) return false; - if (m.type) - return m.type.render - ? filter(m.type.render) // memo + forwardRef - : filter(m.type); // memo - if (m.render) return filter(m.render); // forwardRef - return false; - }; - } -}; - -export type CallbackFn = (mod: any, id: string) => void; - -export const subscriptions = new Map(); -export const moduleListeners = new Set(); -export const factoryListeners = new Set<(factory: (module: any, exports: any, require: WebpackInstance) => void) => void>(); -export const beforeInitListeners = new Set<(wreq: WebpackInstance) => void>(); - -export function _initWebpack(webpackRequire: WebpackInstance) { - wreq = webpackRequire; - cache = webpackRequire.c; -} - -let devToolsOpen = false; -if (IS_DEV && IS_DISCORD_DESKTOP) { - // At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed - setTimeout(() => { - DiscordNative/* just to make sure */?.window.setDevtoolsCallbacks(() => devToolsOpen = true, () => devToolsOpen = false); - }, 0); -} - -export function handleModuleNotFound(method: string, ...filter: unknown[]) { - const err = new Error(`webpack.${method} found no module`); - logger.error(err, "Filter:", filter); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; -} - -/** - * Find the first module that matches the filter - */ -export const find = traceFunction("find", function find(filter: FilterFn, { isIndirect = false, isWaitFor = false }: { isIndirect?: boolean; isWaitFor?: boolean; } = {}) { - if (typeof filter !== "function") - throw new Error("Invalid filter. Expected a function got " + typeof filter); - - for (const key in cache) { - const mod = cache[key]; - if (!mod.loaded || !mod?.exports) continue; - - if (filter(mod.exports)) { - return isWaitFor ? [mod.exports, key] : mod.exports; - } - - if (typeof mod.exports !== "object") continue; - - if (mod.exports.default && filter(mod.exports.default)) { - const found = mod.exports.default; - return isWaitFor ? [found, key] : found; - } - - // the length check makes search about 20% faster - for (const nestedMod in mod.exports) if (nestedMod.length <= 3) { - const nested = mod.exports[nestedMod]; - if (nested && filter(nested)) { - return isWaitFor ? [nested, key] : nested; - } - } - } - - if (!isIndirect) { - handleModuleNotFound("find", filter); - } - - return isWaitFor ? [null, null] : null; -}); - -export function findAll(filter: FilterFn) { - if (typeof filter !== "function") - throw new Error("Invalid filter. Expected a function got " + typeof filter); - - const ret = [] as any[]; - for (const key in cache) { - const mod = cache[key]; - if (!mod.loaded || !mod?.exports) continue; - - if (filter(mod.exports)) - ret.push(mod.exports); - else if (typeof mod.exports !== "object") - continue; - - if (mod.exports.default && filter(mod.exports.default)) - ret.push(mod.exports.default); - else for (const nestedMod in mod.exports) if (nestedMod.length <= 3) { - const nested = mod.exports[nestedMod]; - if (nested && filter(nested)) ret.push(nested); - } - } - - return ret; -} - -/** - * Same as {@link find} but in bulk - * @param filterFns Array of filters. Please note that this array will be modified in place, so if you still - * need it afterwards, pass a copy. - * @returns Array of results in the same order as the passed filters - */ -export const findBulk = traceFunction("findBulk", function findBulk(...filterFns: FilterFn[]) { - if (!Array.isArray(filterFns)) - throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); - - const { length } = filterFns; - - if (length === 0) - throw new Error("Expected at least two filters."); - - if (length === 1) { - if (IS_DEV) { - throw new Error("bulk called with only one filter. Use find"); - } - return find(filterFns[0]); - } - - const filters = filterFns as Array; - - let found = 0; - const results = Array(length); - - outer: - for (const key in cache) { - const mod = cache[key]; - if (!mod.loaded || !mod?.exports) continue; - - for (let j = 0; j < length; j++) { - const filter = filters[j]; - // Already done - if (filter === undefined) continue; - - if (filter(mod.exports)) { - results[j] = mod.exports; - filters[j] = undefined; - if (++found === length) break outer; - break; - } - - if (typeof mod.exports !== "object") - continue; - - if (mod.exports.default && filter(mod.exports.default)) { - results[j] = mod.exports.default; - filters[j] = undefined; - if (++found === length) break outer; - break; - } - - for (const nestedMod in mod.exports) - if (nestedMod.length <= 3) { - const nested = mod.exports[nestedMod]; - if (nested && filter(nested)) { - results[j] = nested; - filters[j] = undefined; - if (++found === length) break outer; - continue outer; - } - } - } - } - - if (found !== length) { - const err = new Error(`Got ${length} filters, but only found ${found} modules!`); - if (IS_DEV) { - if (!devToolsOpen) - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - throw err; - } else { - logger.warn(err); - } - } - - return results; -}); - -/** - * Find the id of the first module factory that includes all the given code - * @returns string or null - */ -export const findModuleId = traceFunction("findModuleId", function findModuleId(...code: CodeFilter) { - code = code.map(canonicalizeMatch); - - for (const id in wreq.m) { - if (stringMatches(wreq.m[id].toString(), code)) return id; - } - - const err = new Error("Didn't find module with code(s):\n" + code.join("\n")); - if (IS_DEV) { - if (!devToolsOpen) - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - throw err; - } else { - logger.warn(err); - } - - return null; -}); - -/** - * Find the first module factory that includes all the given code - * @returns The module factory or null - */ -export function findModuleFactory(...code: CodeFilter) { - const id = findModuleId(...code); - if (!id) return null; - - return wreq.m[id]; -} - -export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks" | "mapMangledModule", any[]]>; - -/** - * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. - * - * Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy. - * On first property access, the lazy is evaluated - * @param factory lazy factory - * @param attempts how many times to try to evaluate the lazy before giving up - * @returns Proxy - * - * Note that the example below exists already as an api, see {@link findByPropsLazy} - * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); - */ -export function proxyLazyWebpack(factory: () => T, attempts?: number) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]); - - return proxyLazy(factory, attempts); -} - -/** - * This is just a wrapper around {@link LazyComponent} to make our reporter test for your webpack finds. - * - * A lazy component. The factory method is called on first render. - * @param factory Function returning a Component - * @param attempts How many times to try to get the component before giving up - * @returns Result of factory function - */ -export function LazyComponentWebpack(factory: () => any, attempts?: number) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]); - - return LazyComponent(factory, attempts); -} - -/** - * Find the first module that matches the filter, lazily - */ -export function findLazy(filter: FilterFn) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["find", [filter]]); - - return proxyLazy(() => find(filter)); -} - -/** - * Find the first module that has the specified properties - */ -export function findByProps(...props: PropsFilter) { - const res = find(filters.byProps(...props), { isIndirect: true }); - if (!res) - handleModuleNotFound("findByProps", ...props); - return res; -} - -/** - * Find the first module that has the specified properties, lazily - */ -export function findByPropsLazy(...props: PropsFilter) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByProps", props]); - - return proxyLazy(() => findByProps(...props)); -} - -/** - * Find the first function that includes all the given code - */ -export function findByCode(...code: CodeFilter) { - const res = find(filters.byCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findByCode", ...code); - return res; -} - -/** - * Find the first function that includes all the given code, lazily - */ -export function findByCodeLazy(...code: CodeFilter) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByCode", code]); - - return proxyLazy(() => findByCode(...code)); -} - -/** - * Find a store by its displayName - */ -export function findStore(name: StoreNameFilter) { - const res = find(filters.byStoreName(name), { isIndirect: true }); - if (!res) - handleModuleNotFound("findStore", name); - return res; -} - -/** - * Find a store by its displayName, lazily - */ -export function findStoreLazy(name: StoreNameFilter) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findStore", [name]]); - - return proxyLazy(() => findStore(name)); -} - -/** - * Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs - */ -export function findComponentByCode(...code: CodeFilter) { - const res = find(filters.componentByCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponentByCode", ...code); - return res; -} - -/** - * Finds the first component that matches the filter, lazily. - */ -export function findComponentLazy(filter: FilterFn) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponent", [filter]]); - - - return LazyComponent(() => { - const res = find(filter, { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponent", filter); - return res; - }); -} - -/** - * Finds the first component that includes all the given code, lazily - */ -export function findComponentByCodeLazy(...code: CodeFilter) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponentByCode", code]); - - return LazyComponent(() => { - const res = find(filters.componentByCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponentByCode", ...code); - return res; - }); -} - -/** - * Finds the first component that is exported by the first prop name, lazily - */ -export function findExportedComponentLazy(...props: PropsFilter) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["findExportedComponent", props]); - - return LazyComponent(() => { - const res = find(filters.byProps(...props), { isIndirect: true }); - if (!res) - handleModuleNotFound("findExportedComponent", ...props); - return res[props[0]]; - }); -} - -/** - * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) - * then maps it into an easily usable module via the specified mappers. - * - * @param code The code to look for - * @param mappers Mappers to create the non mangled exports - * @returns Unmangled exports as specified in mappers - * - * @example mapMangledModule("headerIdIsManaged:", { - * openModal: filters.byCode("headerIdIsManaged:"), - * closeModal: filters.byCode("key==") - * }) - */ -export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record): Record { - const exports = {} as Record; - - const id = findModuleId(...Array.isArray(code) ? code : [code]); - if (id === null) - return exports; - - const mod = wreq(id as any); - outer: - for (const key in mod) { - const member = mod[key]; - for (const newName in mappers) { - // if the current mapper matches this module - if (mappers[newName](member)) { - exports[newName] = member; - continue outer; - } - } - } - return exports; -}); - -/** - * {@link mapMangledModule}, lazy. - - * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) - * then maps it into an easily usable module via the specified mappers. - * - * @param code The code to look for - * @param mappers Mappers to create the non mangled exports - * @returns Unmangled exports as specified in mappers - * - * @example mapMangledModule("headerIdIsManaged:", { - * openModal: filters.byCode("headerIdIsManaged:"), - * closeModal: filters.byCode("key==") - * }) - */ -export function mapMangledModuleLazy(code: string | RegExp | CodeFilter, mappers: Record): Record { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]); - - return proxyLazy(() => mapMangledModule(code, mappers)); -} - -export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/; -export const ChunkIdsRegex = /\("([^"]+?)"\)/g; - -/** - * Extract and load chunks using their entry point - * @param code An array of all the code the module factory containing the lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory - * @returns A promise that resolves with a boolean whether the chunks were loaded - */ -export async function extractAndLoadChunks(code: CodeFilter, matcher: RegExp = DefaultExtractAndLoadChunksRegex) { - const module = findModuleFactory(...code); - if (!module) { - const err = new Error("extractAndLoadChunks: Couldn't find module factory"); - logger.warn(err, "Code:", code, "Matcher:", matcher); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; - - return false; - } - - const match = module.toString().match(canonicalizeMatch(matcher)); - if (!match) { - const err = new Error("extractAndLoadChunks: Couldn't find chunk loading in module factory code"); - logger.warn(err, "Code:", code, "Matcher:", matcher); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; - - return false; - } - - const [, rawChunkIds, entryPointId] = match; - if (Number.isNaN(Number(entryPointId))) { - const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); - logger.warn(err, "Code:", code, "Matcher:", matcher); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; - - return false; - } - - if (rawChunkIds) { - const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => Number(m[1])); - await Promise.all(chunkIds.map(id => wreq.e(id))); - } - - if (wreq.m[entryPointId] == null) { - const err = new Error("extractAndLoadChunks: Entry point is not loaded in the module factories, perhaps one of the chunks failed to load"); - logger.warn(err, "Code:", code, "Matcher:", matcher); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; - - return false; - } - - wreq(Number(entryPointId)); - return true; -} - -/** - * This is just a wrapper around {@link extractAndLoadChunks} to make our reporter test for your webpack finds. - * - * Extract and load chunks using their entry point - * @param code An array of all the code the module factory containing the lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory - * @returns A function that returns a promise that resolves with a boolean whether the chunks were loaded, on first call - */ -export function extractAndLoadChunksLazy(code: CodeFilter, matcher = DefaultExtractAndLoadChunksRegex) { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); - - return makeLazy(() => extractAndLoadChunks(code, matcher)); -} - -/** - * Wait for a module that matches the provided filter to be registered, - * then call the callback with the module as the first argument - */ -export function waitFor(filter: string | PropsFilter | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) { - if (IS_REPORTER && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]); - - if (typeof filter === "string") - filter = filters.byProps(filter); - else if (Array.isArray(filter)) - filter = filters.byProps(...filter); - else if (typeof filter !== "function") - throw new Error("filter must be a string, string[] or function, got " + typeof filter); - - if (cache != null) { - const [existing, id] = find(filter, { isIndirect: true, isWaitFor: true }); - if (existing) return void callback(existing, id); - } - - subscriptions.set(filter, callback); -} - -/** - * Search modules by keyword. This searches the factory methods, - * meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc - * @param code One or more strings or regexes - * @returns Mapping of found modules - */ -export function search(...code: CodeFilter) { - code = code.map(canonicalizeMatch); - - const results = {} as Record; - const factories = wreq.m; - - for (const id in factories) { - const factory = factories[id].original ?? factories[id]; - - if (stringMatches(factory.toString(), code)) - results[id] = factory; - } - - return results; -} - -/** - * Extract a specific module by id into its own Source File. This has no effect on - * the code, it is only useful to be able to look at a specific module without having - * to view a massive file. extract then returns the extracted module so you can jump to it. - * As mentioned above, note that this extracted module is not actually used, - * so putting breakpoints or similar will have no effect. - * @param id The id of the module to extract - */ -export function extract(id: string | number) { - const mod = wreq.m[id] as Function; - if (!mod) return null; - - const code = ` -// [EXTRACTED] WebpackModule${id} -// WARNING: This module was extracted to be more easily readable. -// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! - -0,${mod.toString()} -//# sourceURL=ExtractedWebpackModule${id} -`; - const extracted = (0, eval)(code); - return extracted as Function; -} diff --git a/src/webpack/wreq.d.ts b/src/webpack/wreq.d.ts new file mode 100644 index 000000000..63bd27942 --- /dev/null +++ b/src/webpack/wreq.d.ts @@ -0,0 +1,205 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated, Nuckyz and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export type ModuleExports = any; + +export type Module = { + id: PropertyKey; + loaded: boolean; + exports: ModuleExports; +}; + +/** exports can be anything, however initially it is always an empty object */ +export type ModuleFactory = (this: ModuleExports, module: Module, exports: ModuleExports, require: WebpackRequire) => void; + +export type WebpackQueues = unique symbol; +export type WebpackExports = unique symbol; +export type WebpackError = unique symbol; + +export type AsyncModulePromise = Promise & { + [WebpackQueues]: (fnQueue: ((queue: any[]) => any)) => any; + [WebpackExports]: ModuleExports; + [WebpackError]?: any; +}; + +export type AsyncModuleBody = ( + handleAsyncDependencies: (deps: AsyncModulePromise[]) => + Promise<() => ModuleExports[]> | (() => ModuleExports[]), + asyncResult: (error?: any) => void +) => Promise; + +export type ChunkHandlers = { + /** + * Ensures the js file for this chunk is loaded, or starts to load if it's not. + * @param chunkId The chunk id + * @param promises The promises array to add the loading promise to + */ + j: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void, + /** + * Ensures the css file for this chunk is loaded, or starts to load if it's not. + * @param chunkId The chunk id + * @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too + */ + css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void, +}; + +export type ScriptLoadDone = (event: Event) => void; + +// export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & { +// /** Check if a chunk has been loaded */ +// j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean; +// }; + +export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { + /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */ + m: Record; + /** The module cache, where all modules which have been WebpackRequire'd are stored */ + c: Record; + // /** + // * Export star. Sets properties of "fromObject" to "toObject" as getters that return the value from "fromObject", like this: + // * @example + // * const fromObject = { a: 1 }; + // * Object.keys(fromObject).forEach(key => { + // * if (key !== "default" && !Object.hasOwn(toObject, key)) { + // * Object.defineProperty(toObject, key, { + // * get: () => fromObject[key], + // * enumerable: true + // * }); + // * } + // * }); + // * @returns fromObject + // */ + // es: (this: WebpackRequire, fromObject: AnyRecord, toObject: AnyRecord) => AnyRecord; + /** + * Creates an async module. A module that exports something that is a Promise, or requires an export from an async module. + * + * The body function must be an async function. "module.exports" will become an {@link AsyncModulePromise}. + * + * The body function will be called with a function to handle requires that import from an async module, and a function to resolve this async module. An example on how to handle async dependencies: + * @example + * const factory = (module, exports, wreq) => { + * wreq.a(module, async (handleAsyncDependencies, asyncResult) => { + * try { + * const asyncRequireA = wreq(...); + * + * const asyncDependencies = handleAsyncDependencies([asyncRequire]); + * const [requireAResult] = asyncDependencies.then != null ? (await asyncDependencies)() : asyncDependencies; + * + * // Use the required module + * console.log(requireAResult); + * + * // Mark this async module as resolved + * asyncResult(); + * } catch(error) { + * // Mark this async module as rejected with an error + * asyncResult(error); + * } + * }, false); // false because our module does not have an await after dealing with the async requires + * } + */ + a: (this: WebpackRequire, module: Module, body: AsyncModuleBody, hasAwaitAfterDependencies?: boolean) => void; + /** getDefaultExport function for compatibility with non-harmony modules */ + n: (this: WebpackRequire, exports: any) => () => ModuleExports; + /** + * Create a fake namespace object, useful for faking an __esModule with a default export. + * + * mode & 1: Value is a module id, require it + * + * mode & 2: Merge all properties of value into the namespace + * + * mode & 4: Return value when already namespace object + * + * mode & 16: Return value when it's Promise-like + * + * mode & (8|1): Behave like require + */ + t: (this: WebpackRequire, value: any, mode: number) => any; + /** + * Define getter functions for harmony exports. For every prop in "definiton" (the module exports), set a getter in "exports" for the getter function in the "definition", like this: + * @example + * const exports = {}; + * const definition = { exportName: () => someExportedValue }; + * for (const key in definition) { + * if (Object.hasOwn(definition, key) && !Object.hasOwn(exports, key)) { + * Object.defineProperty(exports, key, { + * get: definition[key], + * enumerable: true + * }); + * } + * } + * // exports is now { exportName: someExportedValue } (but each value is actually a getter) + */ + d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void; + /** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */ + f: ChunkHandlers; + /** + * The ensure chunk function, it ensures a chunk is loaded, or loads if needed. + * Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded. + */ + e: (this: WebpackRequire, chunkId: PropertyKey) => Promise; + /** Get the filename for the css part of a chunk */ + k: (this: WebpackRequire, chunkId: PropertyKey) => string; + /** Get the filename for the js part of a chunk */ + u: (this: WebpackRequire, chunkId: PropertyKey) => string; + /** The global object, will likely always be the window */ + g: typeof globalThis; + /** Harmony module decorator. Decorates a module as an ES Module, and prevents Node.js "module.exports" from being set */ + hmd: (this: WebpackRequire, module: Module) => any; + /** Shorthand for Object.prototype.hasOwnProperty */ + o: typeof Object.prototype.hasOwnProperty; + /** + * Function to load a script tag. "done" is called when the loading has finished or a timeout has occurred. + * "done" will be attached to existing scripts loading if src === url or data-webpack === `${uniqueName}:${key}`, + * so it will be called when that existing script finishes loading. + */ + l: (this: WebpackRequire, url: string, done: ScriptLoadDone, key?: string | number, chunkId?: PropertyKey) => void; + /** Defines __esModule on the exports, marking ES Modules compatibility as true */ + r: (this: WebpackRequire, exports: ModuleExports) => void; + /** Node.js module decorator. Decorates a module as a Node.js module */ + nmd: (this: WebpackRequire, module: Module) => any; + // /** + // * Register deferred code which will be executed when the passed chunks are loaded. + // * + // * If chunkIds is defined, it defers the execution of the callback and returns undefined. + // * + // * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument. + // * + // * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code. + // * + // * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed. + // */ + // O: OnChunksLoaded; + /** + * Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports". + * @returns The exports argument, but now assigned with the exports of the wasm instance + */ + v: (this: WebpackRequire, exports: ModuleExports, wasmModuleId: any, wasmModuleHash: string, importsObj?: WebAssembly.Imports) => Promise; + /** Bundle public path, where chunk files are stored. Used by other methods which load chunks to obtain the full asset url */ + p: string; + /** The runtime id of the current runtime */ + j: string; + /** Document baseURI or WebWorker location.href */ + b: string; +}; + +// Utility section for Vencord + +export type AnyWebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & Partial> & { + /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */ + m: Record; +}; + +/** exports can be anything, however initially it is always an empty object */ +export type AnyModuleFactory = ((this: ModuleExports, module: Module, exports: ModuleExports, require: AnyWebpackRequire) => void) & { + $$vencordPatchedSource?: string; +}; + +export type WrappedModuleFactory = AnyModuleFactory & { + $$vencordOriginal?: AnyModuleFactory; + $$vencordPatchedSource?: string; +}; + +export type WrappedModuleFactories = Record; diff --git a/tsconfig.json b/tsconfig.json index db6d0918d..4e695dcab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,7 @@ "@shared/*": ["./shared/*"], "@webpack/types": ["./webpack/common/types"], "@webpack/common": ["./webpack/common"], - "@webpack": ["./webpack/webpack"] + "@webpack": ["./webpack/api"] }, "plugins": [