From a8e18f17e278f538f0d8dcab429220f0ac6bb60c Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 26 May 2024 01:11:36 -0300 Subject: [PATCH 01/77] ConsoleShortcuts: Start at Init --- src/plugins/consoleShortcuts/index.ts | 58 ++++++++++++++++++--------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index e25e7cb30..b0efe8a08 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -19,10 +19,10 @@ import { Devs } from "@utils/constants"; import { relaunch } from "@utils/native"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; -import definePlugin from "@utils/types"; +import definePlugin, { StartAt } from "@utils/types"; import * as Webpack from "@webpack"; -import { extract, filters, findAll, search } from "@webpack"; -import { React, ReactDOM } from "@webpack/common"; +import { extract, filters, findAll, findModuleId, search } from "@webpack"; +import * as Common from "@webpack/common"; import type { ComponentType } from "react"; const WEB_ONLY = (f: string) => () => { @@ -34,7 +34,7 @@ export default definePlugin({ description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", authors: [Devs.Ven], - getShortcuts() { + getShortcuts(): Record { function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { const cache = new Map(); @@ -64,16 +64,17 @@ export default definePlugin({ let fakeRenderWin: WeakRef | undefined; const find = newFindWrapper(f => f); const findByProps = newFindWrapper(filters.byProps); + return { - ...Vencord.Webpack.Common, - wp: Vencord.Webpack, - wpc: Webpack.wreq.c, - wreq: Webpack.wreq, + ...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(Webpack.findModuleId(code)!), + wpexs: (code: string) => extract(findModuleId(code)!), find, - findAll, + findAll: findAll, findByProps, findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), findByCode: newFindWrapper(filters.byCode), @@ -82,10 +83,10 @@ export default definePlugin({ findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], findStore: newFindWrapper(filters.byStoreName), - PluginsApi: Vencord.Plugins, - plugins: Vencord.Plugins.plugins, - Settings: Vencord.Settings, - Api: Vencord.Api, + PluginsApi: { getter: () => Vencord.Plugins }, + plugins: { getter: () => Vencord.Plugins.plugins }, + Settings: { getter: () => Vencord.Settings }, + Api: { getter: () => Vencord.Api }, reload: () => location.reload(), restart: IS_WEB ? WEB_ONLY("restart") : relaunch, canonicalizeMatch, @@ -115,21 +116,40 @@ export default definePlugin({ }); } - ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); + Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); } }; }, + startAt: StartAt.Init, start() { const shortcuts = this.getShortcuts(); - window.shortcutList = shortcuts; - for (const [key, val] of Object.entries(shortcuts)) - window[key] = val; + window.shortcutList = {}; + + for (const [key, val] of Object.entries(shortcuts)) { + if (val.getter != null) { + Object.defineProperty(window.shortcutList, key, { + get: val.getter, + configurable: true, + enumerable: true + }); + + Object.defineProperty(window, key, { + get: () => window.shortcutList[key], + configurable: true, + enumerable: true + }); + } else { + window.shortcutList[key] = val; + window[key] = val; + } + } }, stop() { delete window.shortcutList; - for (const key in this.getShortcuts()) + for (const key in this.getShortcuts()) { delete window[key]; + } } }); From 4f2c2b8e4ab507e91fca5ce6b80edbe01ab61510 Mon Sep 17 00:00:00 2001 From: Manti <67705577+mantikafasi@users.noreply.github.com> Date: Sun, 26 May 2024 19:14:29 +0300 Subject: [PATCH 02/77] new plugin Summaries: show Discords AI-generated convo summaries (#2481) Co-authored-by: Vendicated --- src/plugins/seeSummaries/README.md | 7 ++ src/plugins/seeSummaries/index.tsx | 114 +++++++++++++++++++++++ src/webpack/common/types/fluxEvents.d.ts | 2 +- 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/plugins/seeSummaries/README.md create mode 100644 src/plugins/seeSummaries/index.tsx diff --git a/src/plugins/seeSummaries/README.md b/src/plugins/seeSummaries/README.md new file mode 100644 index 000000000..dfb90bab3 --- /dev/null +++ b/src/plugins/seeSummaries/README.md @@ -0,0 +1,7 @@ +# Summaries + +Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations. + +Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled. + +![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745) diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx new file mode 100644 index 000000000..04a25198c --- /dev/null +++ b/src/plugins/seeSummaries/index.tsx @@ -0,0 +1,114 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "@api/index"; +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { ChannelStore, GuildStore } from "@webpack/common"; + +const SummaryStore = findByPropsLazy("allSummaries", "findSummary"); +const { createSummaryFromServer } = findByPropsLazy("createSummaryFromServer"); + +const settings = definePluginSettings({ + summaryExpiryThresholdDays: { + type: OptionType.SLIDER, + description: "The time in days before a summary is removed. Note that only up to 50 summaries are kept per channel", + markers: [1, 3, 5, 7, 10, 15, 20, 25, 30], + stickToMarkers: false, + default: 3, + } +}); + +interface Summary { + count: number; + end_id: string; + id: string; + message_ids: string[]; + people: string[]; + source: number; + start_id: string; + summ_short: string; + topic: string; + type: number; + unsafe: boolean; +} + +interface ChannelSummaries { + type: string; + channel_id: string; + guild_id: string; + summaries: Summary[]; + + // custom property + time?: number; +} + +export default definePlugin({ + name: "Summaries", + description: "Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations", + authors: [Devs.mantikafasi], + settings, + patches: [ + { + find: "ChannelTypesSets.SUMMARIZEABLE.has", + replacement: { + match: /\i\.hasFeature\(\i\.GuildFeatures\.SUMMARIES_ENABLED\w+?\)/g, + replace: "true" + } + }, + { + find: "RECEIVE_CHANNEL_SUMMARY(", + replacement: { + match: /shouldFetch\((\i),\i\){/, + replace: "$& if(!$self.shouldFetch($1)) return false;" + } + } + ], + flux: { + CONVERSATION_SUMMARY_UPDATE(data) { + const incomingSummaries: ChannelSummaries[] = data.summaries.map((summary: any) => ({ ...createSummaryFromServer(summary), time: Date.now() })); + + // idk if this is good for performance but it doesnt seem to be a problem in my experience + DataStore.update("summaries-data", summaries => { + summaries ??= {}; + summaries[data.channel_id] ? summaries[data.channel_id].unshift(...incomingSummaries) : (summaries[data.channel_id] = incomingSummaries); + if (summaries[data.channel_id].length > 50) + summaries[data.channel_id] = summaries[data.channel_id].slice(0, 50); + + return summaries; + }); + } + }, + + async start() { + await DataStore.update("summaries-data", summaries => { + for (const key of Object.keys(summaries)) { + for (let i = summaries[key].length - 1; i >= 0; i--) { + if (summaries[key][i].time < Date.now() - 1000 * 60 * 60 * 24 * settings.store.summaryExpiryThresholdDays) { + summaries[key].splice(i, 1); + } + } + + if (summaries[key].length === 0) { + delete summaries[key]; + } + } + + Object.assign(SummaryStore.allSummaries(), summaries); + return summaries; + }); + }, + + shouldFetch(channelId: string) { + const channel = ChannelStore.getChannel(channelId); + // SUMMARIES_ENABLED feature is not in discord-types + const guild = GuildStore.getGuild(channel.guild_id); + // @ts-ignore + return guild.hasFeature("SUMMARIES_ENABLED_GA"); + } +}); diff --git a/src/webpack/common/types/fluxEvents.d.ts b/src/webpack/common/types/fluxEvents.d.ts index 36bcb2cde..1bd50f461 100644 --- a/src/webpack/common/types/fluxEvents.d.ts +++ b/src/webpack/common/types/fluxEvents.d.ts @@ -37,4 +37,4 @@ function makeFluxEventList() { */ // 46kb worth of events ??????? -export type FluxEvents = "ACCESSIBILITY_COLORBLIND_TOGGLE" | "ACCESSIBILITY_DARK_SIDEBAR_TOGGLE" | "ACCESSIBILITY_DESATURATE_ROLES_TOGGLE" | "ACCESSIBILITY_DETECTION_MODAL_SEEN" | "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_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_SUPPORT_CHANGED" | "ACCESSIBILITY_SYNC_PROFILE_THEME_WITH_USER_THEME_TOGGLE" | "ACCESSIBILITY_SYSTEM_COLOR_PREFERENCES_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_CONTRAST_CHANGED" | "ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED" | "ACK_APPROVED_GUILD_JOIN_REQUEST" | "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" | "ACTIVITY_INVITE_EDUCATION_DISMISS" | "ACTIVITY_INVITE_MODAL_CLOSE" | "ACTIVITY_INVITE_MODAL_OPEN" | "ACTIVITY_JOIN" | "ACTIVITY_JOIN_FAILED" | "ACTIVITY_JOIN_LOADING" | "ACTIVITY_LAUNCH_FAIL" | "ACTIVITY_METADATA_UPDATE" | "ACTIVITY_PIP_MODE_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" | "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" | "ALLOW_SPAM_MESSAGES_FOR_USER" | "APPLICATIONS_FETCH" | "APPLICATIONS_FETCH_FAIL" | "APPLICATIONS_FETCH_SUCCESS" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_FAIL" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_START" | "APPLICATION_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "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_COMMANDS_FETCH" | "APPLICATION_COMMANDS_FETCH_FOR_APPLICATION" | "APPLICATION_COMMAND_AUTOCOMPLETE_REQUEST" | "APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE" | "APPLICATION_COMMAND_FETCH" | "APPLICATION_COMMAND_REGISTRY_UPDATE" | "APPLICATION_COMMAND_SEARCH_STORE_QUERY" | "APPLICATION_COMMAND_SEARCH_STORE_UI_UPDATE" | "APPLICATION_COMMAND_SEARCH_STORE_UPDATE" | "APPLICATION_COMMAND_SET_ACTIVE_COMMAND" | "APPLICATION_COMMAND_SET_PREFERRED_COMMAND" | "APPLICATION_COMMAND_UPDATE_CHANNEL_STATE" | "APPLICATION_COMMAND_UPDATE_OPTIONS" | "APPLICATION_COMMAND_USED" | "APPLICATION_FETCH" | "APPLICATION_FETCH_FAIL" | "APPLICATION_FETCH_SUCCESS" | "APPLICATION_STORE_ACCEPT_EULA" | "APPLICATION_STORE_ACCEPT_STORE_TERMS" | "APPLICATION_STORE_CLEAR_DATA" | "APPLICATION_STORE_DIRECTORY_FETCH_SUCCESS" | "APPLICATION_STORE_DIRECTORY_MUTE" | "APPLICATION_STORE_DIRECTORY_UNMUTE" | "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_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_SUBSYSTEM" | "AUDIO_SET_TEMPORARY_SELF_MUTE" | "AUDIO_TOGGLE_LOCAL_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_USER" | "AUTH_INVITE_UPDATE" | "AUTH_SESSION_CHANGE" | "AUTO_MODERATION_MENTION_RAID_DETECTION" | "AUTO_MODERATION_MENTION_RAID_NOTICE_DISMISS" | "BILLING_IP_COUNTRY_CODE_FAILURE" | "BILLING_IP_COUNTRY_CODE_FETCH_START" | "BILLING_LOCALIZED_PRICING_PROMO_FAILURE" | "BILLING_MOST_RECENT_SUBSCRIPTION_FETCH_SUCCESS" | "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_POPUP_BRIDGE_CALLBACK" | "BILLING_POPUP_BRIDGE_STATE_UPDATE" | "BILLING_PURCHASE_TOKEN_AUTH_CLEAR_STATE" | "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_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_START" | "BRAINTREE_TOKENIZE_PAYPAL_SUCCESS" | "BRAINTREE_TOKENIZE_VENMO_START" | "BRAINTREE_TOKENIZE_VENMO_SUCCESS" | "BROWSER_HANDOFF_BEGIN" | "BROWSER_HANDOFF_FROM_APP" | "BROWSER_HANDOFF_SET_USER" | "BROWSER_HANDOFF_UNAVAILABLE" | "BUILD_OVERRIDE_RESOLVED" | "BULK_ACK" | "BULK_CLEAR_RECENTS" | "BURST_REACTION_ADD" | "BURST_REACTION_ANIMATION_ADD" | "BURST_REACTION_EFFECT_CLEAR" | "BURST_REACTION_EFFECT_PLAY" | "BURST_REACTION_REMOVE" | "CACHE_LOADED" | "CACHE_LOADED_LAZY" | "CALL_CHAT_TOASTS_SET_ENABLED" | "CALL_CONNECT" | "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_CLOSE" | "CHANGE_LOG_FETCH_SUCCESS" | "CHANGE_LOG_LOCK" | "CHANGE_LOG_OPEN" | "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_HIGHLIGHTS_FETCH_START" | "CHANNEL_HIGHLIGHTS_FETCH_SUCCESS" | "CHANNEL_LOCAL_ACK" | "CHANNEL_MESSAGE_PREVIEW_LOAD_MESSAGES" | "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_MUSIC_MUTED" | "CHANNEL_RTC_UPDATE_STAGE_STREAM_SIZE" | "CHANNEL_RTC_UPDATE_STAGE_VIDEO_LIMIT_BOOST_UPSELL_DISMISSED" | "CHANNEL_RTC_UPDATE_VOICE_PARTICIPANTS_HIDDEN" | "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_TOGGLE_MEMBERS_SECTION" | "CHANNEL_UPDATES" | "CHECKING_FOR_UPDATES" | "CHECK_LAUNCHABLE_GAME" | "CLEAR_AUTHENTICATION_ERRORS" | "CLEAR_CACHES" | "CLEAR_GUILD_CACHE" | "CLEAR_INTERACTION_MODAL_STATE" | "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_CLEAR_CLIPS_SESSION" | "CLIPS_SAVE_CLIP" | "CLIPS_SETTINGS_UPDATE" | "COMMANDS_MIGRATION_NOTICE_DISMISSED" | "COMMANDS_MIGRATION_OVERVIEW_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_TOGGLE_TOOLTIP_DISMISSED" | "COMMANDS_MIGRATION_UPDATE_SUCCESS" | "CONNECTED_DEVICE_IGNORE" | "CONNECTED_DEVICE_NEVER_SHOW_MODAL" | "CONNECTED_DEVICE_SET" | "CONNECTIONS_GRID_MODAL_HIDE" | "CONNECTIONS_GRID_MODAL_SHOW" | "CONNECTION_CLOSED" | "CONNECTION_OPEN" | "CONNECTION_OPEN_SUPPLEMENTAL" | "CONNECTION_RESUMED" | "CONSOLE_COMMAND_UPDATE" | "CONTEXT_MENU_CLOSE" | "CONTEXT_MENU_OPEN" | "CREATE_PENDING_REPLY" | "CURRENT_BUILD_OVERRIDE_RESOLVED" | "CURRENT_USER_UPDATE" | "DECAY_READ_STATES" | "DELETED_ENTITY_IDS" | "DELETE_PENDING_REPLY" | "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_ENABLED" | "DEVELOPER_ACTIVITY_SHELF_TOGGLE_USE_ACTIVITY_URL_OVERRIDE" | "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_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_FAVORITE_SUGGESTION" | "DISMISS_NITRODUCTION_TOOLTIP" | "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_HEARTBEAT_FAILURE" | "DROPS_HEARTBEAT_SUCCESS" | "DROPS_PLATFORM_AVAILABILITY_SUCCESS" | "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_FREE_INDICATOR" | "EMBEDDED_ACTIVITY_FETCH_SHELF" | "EMBEDDED_ACTIVITY_FETCH_SHELF_FAIL" | "EMBEDDED_ACTIVITY_FETCH_SHELF_SUCCESS" | "EMBEDDED_ACTIVITY_FREE_ACTIVITY_UPDATE" | "EMBEDDED_ACTIVITY_INBOUND_UPDATE" | "EMBEDDED_ACTIVITY_LAUNCH_FAIL" | "EMBEDDED_ACTIVITY_LAUNCH_START" | "EMBEDDED_ACTIVITY_LAUNCH_SUCCESS" | "EMBEDDED_ACTIVITY_OPEN" | "EMBEDDED_ACTIVITY_SET_CONFIG" | "EMBEDDED_ACTIVITY_SET_ORIENTATION_LOCK_STATE" | "EMOJI_DELETE" | "EMOJI_FETCH_FAILURE" | "EMOJI_FETCH_SUCCESS" | "EMOJI_TRACK_USAGE" | "EMOJI_UPLOAD_START" | "EMOJI_UPLOAD_STOP" | "ENABLE_AUTOMATIC_ACK" | "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" | "EXPERIMENT_TRIGGER" | "FETCH_AUTH_SESSIONS_SUCCESS" | "FETCH_GUILD_EVENTS_FOR_GUILD" | "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_ICON_UPDATE" | "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_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_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_HOME_ENSURE_HOME_SESSION" | "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_DISABLE_SUBMIT" | "GUILD_IDENTITY_SETTINGS_SET_GUILD" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_AVATAR" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BANNER" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_BIO" | "GUILD_IDENTITY_SETTINGS_SET_PENDING_NICKNAME" | "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_STATUS" | "GUILD_JOIN_REQUESTS_SET_SELECTED" | "GUILD_JOIN_REQUESTS_SET_SORT_ORDER" | "GUILD_JOIN_REQUEST_CREATE" | "GUILD_JOIN_REQUEST_DELETE" | "GUILD_JOIN_REQUEST_UPDATE" | "GUILD_MEMBERS_CHUNK" | "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" | "GUILD_MOVE_BY_ID" | "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_PROGRESS_COMPLETED_SEEN" | "GUILD_PROGRESS_DISMISS" | "GUILD_PROGRESS_INITIALIZE" | "GUILD_PROMPT_VIEWED" | "GUILD_RECOMMENDATION_FETCH" | "GUILD_RECOMMENDATION_FETCH_FAILURE" | "GUILD_RECOMMENDATION_FETCH_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_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_RSVPS_FETCH_SUCESS" | "GUILD_SCHEDULED_EVENT_UPDATE" | "GUILD_SCHEDULED_EVENT_USERS_FETCH_SUCCESS" | "GUILD_SCHEDULED_EVENT_USER_ADD" | "GUILD_SCHEDULED_EVENT_USER_REMOVE" | "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_INTEGRATIONS" | "GUILD_SETTINGS_LOADED_INTEGRATIONS_WITH_COMMANDS" | "GUILD_SETTINGS_LOADED_INVITES" | "GUILD_SETTINGS_ONBOARDING_EDUCATION_UPSELL_DISMISSED" | "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_STEP" | "GUILD_SETTINGS_OPEN" | "GUILD_SETTINGS_ROLES_CLEAR_PERMISSIONS" | "GUILD_SETTINGS_ROLES_EDIT_SECTION_UPDATE" | "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_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_DELETE_SUCCESS" | "GUILD_SOUNDBOARD_FETCH" | "GUILD_SOUNDBOARD_FETCH_FAILURE" | "GUILD_SOUNDBOARD_FETCH_SUCCESS" | "GUILD_SOUNDBOARD_SOUND_PLAY_END" | "GUILD_SOUNDBOARD_SOUND_PLAY_START" | "GUILD_SOUNDBOARD_UPDATE_SUCCESS" | "GUILD_SOUNDBOARD_UPLOAD_SUCCESS" | "GUILD_STICKERS_CREATE_SUCCESS" | "GUILD_STICKERS_FETCH_SUCCESS" | "GUILD_STICKERS_UPDATE" | "GUILD_STOP_LURKING" | "GUILD_STOP_LURKING_FAILURE" | "GUILD_SUBSCRIPTIONS" | "GUILD_SUBSCRIPTIONS_CHANNEL" | "GUILD_SUBSCRIPTIONS_FLUSH" | "GUILD_SUBSCRIPTIONS_MEMBERS_ADD" | "GUILD_SUBSCRIPTIONS_MEMBERS_REMOVE" | "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_UNREADS_SET_LAST_CLEARED" | "GUILD_UPDATE" | "GUILD_UPDATE_DISCOVERY_METADATA" | "GUILD_UPDATE_DISCOVERY_METADATA_FAIL" | "GUILD_UPDATE_DISCOVERY_METADATA_FROM_SERVER" | "GUILD_VERIFICATION_CHECK" | "HIDE_ACTION_SHEET" | "HIDE_ACTION_SHEET_QUICK_SWITCHER" | "HIDE_KEYBOARD_SHORTCUTS" | "HIDE_SPAM_MESSAGES_FOR_USER" | "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" | "INSTALLATION_LOCATION_ADD" | "INSTALLATION_LOCATION_FETCH_METADATA" | "INSTALLATION_LOCATION_REMOVE" | "INSTALLATION_LOCATION_UPDATE" | "INSTANT_INVITE_CREATE" | "INSTANT_INVITE_CREATE_FAILURE" | "INSTANT_INVITE_CREATE_SUCCESS" | "INSTANT_INVITE_REVOKE_SUCCESS" | "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_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" | "LIVE_CHANNEL_NOTICE_HIDE" | "LOAD_ARCHIVED_THREADS" | "LOAD_ARCHIVED_THREADS_FAIL" | "LOAD_ARCHIVED_THREADS_SUCCESS" | "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" | "LOBBY_CONNECT" | "LOBBY_CREATE" | "LOBBY_DELETE" | "LOBBY_DISCONNECT" | "LOBBY_MEMBER_CONNECT" | "LOBBY_MEMBER_DISCONNECT" | "LOBBY_MEMBER_UPDATE" | "LOBBY_MESSAGE" | "LOBBY_UPDATE" | "LOBBY_VOICE_CONNECT" | "LOBBY_VOICE_DISCONNECT" | "LOBBY_VOICE_SERVER_UPDATE" | "LOBBY_VOICE_STATE_UPDATE" | "LOCAL_ACTIVITY_UPDATE" | "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_MFA_WEBAUTHN" | "LOGIN_MFA_WEBAUTHN_TO_TOTP" | "LOGIN_PASSWORD_RECOVERY_PHONE_VERIFICATION" | "LOGIN_PHONE_IP_AUTHORIZATION_REQUIRED" | "LOGIN_RESET" | "LOGIN_STATUS_RESET" | "LOGIN_SUCCESS" | "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_ENABLE_SOUNDSHARE" | "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_AV1" | "MEDIA_ENGINE_SET_DESKTOP_SOURCE" | "MEDIA_ENGINE_SET_EXPERIMENTAL_ENCODERS" | "MEDIA_ENGINE_SET_EXPERIMENTAL_SOUNDSHARE" | "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" | "MEMBER_VERIFICATION_FORM_FETCH_FAIL" | "MEMBER_VERIFICATION_FORM_UPDATE" | "MENTION_MODAL_CLOSE" | "MENTION_MODAL_OPEN" | "MESSAGE_ACK" | "MESSAGE_CREATE" | "MESSAGE_DELETE" | "MESSAGE_DELETE_BULK" | "MESSAGE_EDIT_FAILED_AUTOMOD" | "MESSAGE_END_EDIT" | "MESSAGE_LENGTH_UPSELL" | "MESSAGE_REACTION_ADD" | "MESSAGE_REACTION_ADD_USERS" | "MESSAGE_REACTION_REMOVE" | "MESSAGE_REACTION_REMOVE_ALL" | "MESSAGE_REACTION_REMOVE_EMOJI" | "MESSAGE_REQUEST_ACCEPT_OPTIMISTIC" | "MESSAGE_REVEAL" | "MESSAGE_SEND_FAILED" | "MESSAGE_SEND_FAILED_AUTOMOD" | "MESSAGE_START_EDIT" | "MESSAGE_TODO_ADD" | "MESSAGE_TODO_CLEANUP" | "MESSAGE_TODO_COMPLETE" | "MESSAGE_UPDATE" | "MESSAGE_UPDATE_EDIT" | "MFA_CLEAR_BACKUP_CODES" | "MFA_DISABLE_SUCCESS" | "MFA_ENABLE_SUCCESS" | "MFA_SEND_VERIFICATION_KEY" | "MFA_SMS_TOGGLE" | "MFA_SMS_TOGGLE_COMPLETE" | "MFA_VIEW_BACKUP_CODES" | "MFA_WEBAUTHN_CREDENTIALS_LOADED" | "MFA_WEBAUTHN_CREDENTIALS_LOADING" | "MFA_WEBAUTHN_CREDENTIAL_CREATE" | "MFA_WEBAUTHN_CREDENTIAL_DELETE" | "MOBILE_WEB_SIDEBAR_CLOSE" | "MOBILE_WEB_SIDEBAR_OPEN" | "MODAL_POP" | "MODAL_PUSH" | "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" | "NITRODUCTION_PERSISTENT_ONBOARDING_TOGGLE_COLLAPSE" | "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_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_SET_ACTIVE" | "NOTIFICATION_CENTER_SET_TAB" | "NOTIFICATION_CLICK" | "NOTIFICATION_CREATE" | "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_GAMES_CHANGE" | "OVERLAY_INCOMPATIBLE_APP" | "OVERLAY_INITIALIZE" | "OVERLAY_JOIN_GAME" | "OVERLAY_READY" | "OVERLAY_SELECT_CALL" | "OVERLAY_SELECT_CHANNEL" | "OVERLAY_SET_AVATAR_SIZE_MODE" | "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_TEXT_CHAT_NOTIFICATION_MODE" | "OVERLAY_SET_TEXT_WIDGET_OPACITY" | "OVERLAY_SET_UI_LOCKED" | "OVERLAY_START_SESSION" | "PARTNER_REQUIREMENTS_FETCH_FAILURE" | "PARTNER_REQUIREMENTS_FETCH_START" | "PARTNER_REQUIREMENTS_FETCH_SUCCESS" | "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_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_REQUIRED_MODAL_CLOSE" | "PREMIUM_REQUIRED_MODAL_OPEN" | "PRESENCES_REPLACE" | "PRESENCE_UPDATES" | "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_PANEL_TOGGLE_SECTION" | "PUBLIC_UPSELL_NOTICE_DISMISS" | "PURCHASE_CONFIRMATION_MODAL_CLOSE" | "PURCHASE_CONFIRMATION_MODAL_OPEN" | "PUSH_NOTIFICATION_CLICK" | "QUEUE_INTERACTION_COMPONENT_STATE" | "QUICKSWITCHER_HIDE" | "QUICKSWITCHER_SEARCH" | "QUICKSWITCHER_SELECT" | "QUICKSWITCHER_SHOW" | "QUICKSWITCHER_SWITCH_TO" | "RECENT_MENTION_DELETE" | "REGISTER" | "REGISTER_FAILURE" | "REGISTER_SAVE_FORM" | "REGISTER_SUCCESS" | "RELATIONSHIP_ADD" | "RELATIONSHIP_REMOVE" | "RELATIONSHIP_UPDATE" | "REMOTE_COMMAND" | "REMOTE_SESSION_CONNECT" | "REMOTE_SESSION_DISCONNECT" | "REMOVE_AUTOMOD_MESSAGE_NOTICE" | "REQUEST_FORUM_UNREADS" | "RESET_ALL_NITRODUCTION_TOOLTIPS" | "RESET_HAS_COMPLETED_STEP" | "RESET_NOTIFICATION_CENTER" | "RESET_PAYMENT_ID" | "RESORT_THREADS" | "RPC_APP_AUTHENTICATED" | "RPC_APP_CONNECTED" | "RPC_APP_DISCONNECTED" | "RPC_NOTIFICATION_CREATE" | "RPC_SERVER_READY" | "RTC_CONNECTION_LOSS_RATE" | "RTC_CONNECTION_PING" | "RTC_CONNECTION_STATE" | "RTC_CONNECTION_UPDATE_ID" | "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" | "RUNNING_GAMES_CHANGE" | "RUNNING_GAME_ADD_OVERRIDE" | "RUNNING_GAME_DELETE_ENTRY" | "RUNNING_GAME_EDIT_NAME" | "RUNNING_GAME_TOGGLE_OVERLAY" | "RUNNING_STREAMER_TOOLS_CHANGE" | "SAVE_LAST_NON_VOICE_ROUTE" | "SAVE_LAST_ROUTE" | "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_SET_SHOW_BLOCKED_RESULTS" | "SEARCH_START" | "SELECTIVELY_SYNCED_USER_SETTINGS_UPDATE" | "SELF_PRESENCE_STORE_UPDATE" | "SESSIONS_REPLACE" | "SET_CHANNEL_BITRATE" | "SET_CHANNEL_VIDEO_QUALITY_MODE" | "SET_CONSENT_REQUIRED" | "SET_GUILD_FOLDER_EXPANDED" | "SET_HAS_COMPLETED_STEP" | "SET_INTERACTION_COMPONENT_STATE" | "SET_LOCATION_METADATA" | "SET_LOGIN_CREDENTIALS" | "SET_NATIVE_PERMISSION" | "SET_PENDING_REPLY_SHOULD_MENTION" | "SET_RECENT_MENTIONS_FILTER" | "SET_RECENT_MENTIONS_STALE" | "SET_SOUNDPACK" | "SET_TTS_SPEECH_RATE" | "SET_VAD_PERMISSION" | "SHOW_ACTION_SHEET" | "SHOW_ACTION_SHEET_QUICK_SWITCHER" | "SHOW_KEYBOARD_SHORTCUTS" | "SIDEBAR_CLOSE" | "SIDEBAR_CREATE_THREAD" | "SIDEBAR_VIEW_CHANNEL" | "SKUS_FETCH_SUCCESS" | "SKU_FETCH_FAIL" | "SKU_FETCH_START" | "SKU_FETCH_SUCCESS" | "SKU_PURCHASE_CLEAR_ERROR" | "SKU_PURCHASE_FAIL" | "SKU_PURCHASE_MODAL_CLOSE" | "SKU_PURCHASE_MODAL_OPEN" | "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_ADD_FAVORITE_SOUND" | "SOUNDBOARD_REMOVE_FAVORITE_SOUND" | "SPEAKING" | "SPEAKING_MESSAGE" | "SPEAK_MESSAGE" | "SPEAK_TEXT" | "SPELLCHECK_LEARN_WORD" | "SPELLCHECK_TOGGLE" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN" | "SPOTIFY_ACCOUNT_ACCESS_TOKEN_REVOKE" | "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" | "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_APPLICATION_INTERACTION_FAKE_USER" | "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" | "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" | "TOP_EMOJIS_FETCH" | "TOP_EMOJIS_FETCH_SUCCESS" | "TRUNCATE_MENTIONS" | "TRUNCATE_MESSAGES" | "TUTORIAL_INDICATOR_DISMISS" | "TUTORIAL_INDICATOR_HIDE" | "TUTORIAL_INDICATOR_SHOW" | "TUTORIAL_INDICATOR_SUPPRESS_ALL" | "TYPING_START" | "TYPING_START_LOCAL" | "TYPING_STOP" | "TYPING_STOP_LOCAL" | "UNSYNCED_USER_SETTINGS_UPDATE" | "UNVERIFIED_GAME_UPDATE" | "UPDATE_APP_COLORS" | "UPDATE_AVAILABLE" | "UPDATE_CHANNEL_DIMENSIONS" | "UPDATE_CHANNEL_LIST_DIMENSIONS" | "UPDATE_CONSENTS" | "UPDATE_DOWNLOADED" | "UPDATE_ERROR" | "UPDATE_GUILD_LIST_DIMENSIONS" | "UPDATE_HAS_FLOW_START_EVENT_BEEN_EMITTED" | "UPDATE_MANUALLY" | "UPDATE_NOT_AVAILABLE" | "UPDATE_TOKEN" | "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_PROGRESS" | "UPLOAD_RESTORE_FAILED_UPLOAD" | "UPLOAD_START" | "USER_ACHIEVEMENT_UPDATE" | "USER_ACTIVITY_STATISTICS_FETCH_SUCCESS" | "USER_APPLIED_BOOSTS_FETCH_SUCCESS" | "USER_AUTHORIZED_APPS_UPDATE" | "USER_CONNECTIONS_INTEGRATION_JOINING" | "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_CLIENT_ADD" | "USER_PROFILE_ACCESSIBILITY_TOOLTIP_VIEWED" | "USER_PROFILE_FETCH_FAILURE" | "USER_PROFILE_FETCH_START" | "USER_PROFILE_FETCH_SUCCESS" | "USER_PROFILE_MODAL_CLOSE" | "USER_PROFILE_MODAL_OPEN" | "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_DISABLE_SUBMIT" | "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_PRONOUNS" | "USER_SETTINGS_ACCOUNT_SET_PENDING_THEME_COLORS" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_AVATAR" | "USER_SETTINGS_ACCOUNT_SET_TRY_IT_OUT_BANNER" | "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_PROFILE_CHANGES" | "USER_SETTINGS_THEME_OVERRIDE" | "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_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"; +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"; From c83627032091d3265ca22f8089b4857df9832087 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 26 May 2024 18:24:02 +0200 Subject: [PATCH 03/77] fix minor bugs in various plugins - FriendsSince: Don't show for friend requests - FakeNitro: Fix attempting to bypass unicode emojis #2503 - MessageLatency: ignore bots #2504 - CtrlEnterSend: use cmd+enter on macOS #2502 - ReplaceGoogleSearch: trim LF character #2488 Co-authored-by: AutumnVN Co-authored-by: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Co-authored-by: Lumap Co-authored-by: Mylloon --- src/plugins/ctrlEnterSend/index.ts | 4 ++-- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/friendsSince/index.tsx | 4 ++++ src/plugins/messageLatency/index.tsx | 3 +++ src/plugins/replaceGoogleSearch/index.tsx | 2 +- src/webpack/common/types/stores.d.ts | 4 ++-- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/plugins/ctrlEnterSend/index.ts b/src/plugins/ctrlEnterSend/index.ts index 4b9dd8e06..817da0532 100644 --- a/src/plugins/ctrlEnterSend/index.ts +++ b/src/plugins/ctrlEnterSend/index.ts @@ -18,7 +18,7 @@ export default definePlugin({ type: OptionType.SELECT, options: [ { - label: "Ctrl+Enter (Enter or Shift+Enter for new line)", + label: "Ctrl+Enter (Enter or Shift+Enter for new line) (cmd+enter on macOS)", value: "ctrl+enter" }, { @@ -54,7 +54,7 @@ export default definePlugin({ result = event.shiftKey; break; case "ctrl+enter": - result = event.ctrlKey; + result = navigator.platform.includes("Mac") ? event.metaKey : event.ctrlKey; break; case "enter": result = !event.shiftKey && !event.ctrlKey; diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 4ab0e18ee..427f36ce8 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -813,7 +813,7 @@ export default definePlugin({ }, canUseEmote(e: Emoji, channelId: string) { - if (e.type === "UNICODE") return true; + if (e.type === 0) return true; if (e.available === false) return false; const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 8ad9dc7f4..eb59fff25 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -52,6 +52,8 @@ export default definePlugin({ getFriendSince(userId: string) { try { + if (!RelationshipStore.isFriend(userId)) return null; + return RelationshipStore.getSince(userId); } catch (err) { new Logger("FriendsSince").error(err); @@ -60,6 +62,8 @@ export default definePlugin({ }, friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => { + if (!RelationshipStore.isFriend(userId)) return null; + const friendsSince = RelationshipStore.getSince(userId); if (!friendsSince) return null; diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index d517c0e01..e4e5b8771 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -97,6 +97,9 @@ export default definePlugin({ // Message wasn't received through gateway if (!isNonNullish(nonce)) return null; + // Bots basically never send a nonce, and if someone does do it then it's usually not a snowflake + if (message.bot) return null; + let isDiscordKotlin = false; let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds if (!showMillis) { diff --git a/src/plugins/replaceGoogleSearch/index.tsx b/src/plugins/replaceGoogleSearch/index.tsx index dff593a3b..9882809f7 100644 --- a/src/plugins/replaceGoogleSearch/index.tsx +++ b/src/plugins/replaceGoogleSearch/index.tsx @@ -37,7 +37,7 @@ const settings = definePluginSettings({ }); function search(src: string, engine: string) { - open(engine + encodeURIComponent(src), "_blank"); + open(engine + encodeURIComponent(src.trim()), "_blank"); } function makeSearchItem(src: string) { diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 059924f5a..083ec2694 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -63,7 +63,7 @@ export interface CustomEmoji { originalName?: string; require_colons: boolean; roles: string[]; - type: "GUILD_EMOJI"; + type: 1; } export interface UnicodeEmoji { @@ -75,7 +75,7 @@ export interface UnicodeEmoji { }; index: number; surrogates: string; - type: "UNICODE"; + type: 0; uniqueName: string; useSpriteSheet: boolean; get allNamesString(): string; From 9ec671819d27a7800c3c47287db7c29553120c7b Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 26 May 2024 19:12:18 +0200 Subject: [PATCH 04/77] build: improve fileInclude plugin --- .eslintrc.json | 2 +- browser/VencordNativeStub.ts | 4 +- package.json | 6 +- pnpm-lock.yaml | 156 +++++++++++++++++++ scripts/build/common.mjs | 61 ++++++-- src/main/ipcMain.ts | 3 +- src/modules.d.ts | 2 +- src/plugins/shikiCodeblocks.desktop/index.ts | 3 +- 8 files changed, 218 insertions(+), 19 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2ee24e8b3..918edca0c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,7 @@ { "root": true, "parser": "@typescript-eslint/parser", - "ignorePatterns": ["dist", "browser"], + "ignorePatterns": ["dist", "browser", "packages/vencord-types"], "plugins": [ "@typescript-eslint", "simple-header", diff --git a/browser/VencordNativeStub.ts b/browser/VencordNativeStub.ts index 77c72369c..79f0f2cd3 100644 --- a/browser/VencordNativeStub.ts +++ b/browser/VencordNativeStub.ts @@ -19,8 +19,8 @@ /// /// -import monacoHtmlLocal from "~fileContent/monacoWin.html"; -import monacoHtmlCdn from "~fileContent/../src/main/monacoWin.html"; +import monacoHtmlLocal from "file://monacoWin.html?minify"; +import monacoHtmlCdn from "file://../src/main/monacoWin.html?minify"; import * as DataStore from "../src/api/DataStore"; import { debounce } from "../src/utils"; import { EXTENSION_BASE_URL } from "../src/utils/web-metadata"; diff --git a/package.json b/package.json index 04b811e9c..29b1506e2 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,9 @@ }, "scripts": { "build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs", + "buildStandalone": "pnpm build --standalone", "buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", - "watch": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs --watch", + "watch": "pnpm build --watch", "generatePluginJson": "tsx scripts/generatePluginList.ts", "generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types", "inject": "node scripts/runInstaller.mjs", @@ -27,7 +28,7 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins", "lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins", "lint:fix": "pnpm lint --fix", - "test": "pnpm build && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson", + "test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson", "testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc", "testTsc": "tsc --noEmit" }, @@ -61,6 +62,7 @@ "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-unused-imports": "^2.0.0", "highlight.js": "10.6.0", + "html-minifier-terser": "^7.2.0", "moment": "^2.29.4", "puppeteer-core": "^19.11.1", "standalone-electron-types": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 065d5d4e7..b03585799 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: highlight.js: specifier: 10.6.0 version: 10.6.0 + html-minifier-terser: + specifier: ^7.2.0 + version: 7.2.0 moment: specifier: ^2.29.4 version: 2.29.4 @@ -393,6 +396,27 @@ packages: '@humanwhocodes/object-schema@1.2.1': resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -688,6 +712,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -716,6 +743,10 @@ packages: resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} engines: {node: '>=0.10.0'} + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -740,6 +771,13 @@ packages: colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + component-emitter@1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} @@ -871,12 +909,19 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1402,6 +1447,11 @@ packages: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -1673,6 +1723,9 @@ packages: lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1770,6 +1823,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -1855,6 +1911,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1863,6 +1922,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascalcase@0.1.1: resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} engines: {node: '>=0.10.0'} @@ -1985,6 +2047,10 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2228,6 +2294,11 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + terser@5.31.0: + resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} + engines: {node: '>=10'} + hasBin: true + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -2263,6 +2334,9 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tsutils@3.21.0: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -2599,6 +2673,28 @@ snapshots: '@humanwhocodes/object-schema@1.2.1': {} + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2958,6 +3054,11 @@ snapshots: callsites@3.1.0: {} + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.6.2 + camelcase-keys@6.2.2: dependencies: camelcase: 5.3.1 @@ -2991,6 +3092,10 @@ snapshots: isobject: 3.0.1 static-extend: 0.1.2 + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -3016,6 +3121,10 @@ snapshots: colord@2.9.3: {} + commander@10.0.1: {} + + commander@2.20.3: {} + component-emitter@1.3.0: {} concat-map@0.0.1: {} @@ -3139,12 +3248,19 @@ snapshots: dependencies: esutils: 2.0.3 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + emoji-regex@8.0.0: {} end-of-stream@1.4.4: dependencies: once: 1.4.0 + entities@4.5.0: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -3733,6 +3849,16 @@ snapshots: dependencies: lru-cache: 6.0.0 + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.31.0 + html-tags@3.3.1: {} https-proxy-agent@5.0.1: @@ -3974,6 +4100,10 @@ snapshots: lodash.truncate@4.4.2: {} + lower-case@2.0.2: + dependencies: + tslib: 2.6.2 + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -4071,6 +4201,11 @@ snapshots: natural-compare@1.4.0: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + node-fetch@2.6.7: dependencies: whatwg-url: 5.0.0 @@ -4168,6 +4303,11 @@ snapshots: pako@1.0.11: {} + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -4179,6 +4319,11 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + pascalcase@0.1.1: {} path-exists@4.0.0: {} @@ -4296,6 +4441,8 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 + relateurl@0.2.7: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -4605,6 +4752,13 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + terser@5.31.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.10.0 + commander: 2.20.3 + source-map-support: 0.5.21 + text-table@0.2.0: {} through@2.3.8: {} @@ -4646,6 +4800,8 @@ snapshots: tslib@1.14.1: {} + tslib@2.6.2: {} + tsutils@3.21.0(typescript@5.4.5): dependencies: tslib: 1.14.1 diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index e2c901226..457f3f22d 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -20,8 +20,10 @@ import "../suppressExperimentalWarnings.js"; import "../checkNodeVersion.js"; import { exec, execSync } from "child_process"; +import esbuild from "esbuild"; import { constants as FsConstants, readFileSync } from "fs"; import { access, readdir, readFile } from "fs/promises"; +import { minify as minifyHtml } from "html-minifier-terser"; import { join, relative } from "path"; import { promisify } from "util"; @@ -161,21 +163,62 @@ export const gitRemotePlugin = { /** * @type {import("esbuild").Plugin} */ -export const fileIncludePlugin = { - name: "file-include-plugin", +export const fileUrlPlugin = { + name: "file-uri-plugin", setup: build => { - const filter = /^~fileContent\/.+$/; + const filter = /^file:\/\/.+$/; build.onResolve({ filter }, args => ({ - namespace: "include-file", + namespace: "file-uri", path: args.path, pluginData: { - path: join(args.resolveDir, args.path.slice("include-file/".length)) + uri: args.path, + path: join(args.resolveDir, args.path.slice("file://".length).split("?")[0]) } })); - build.onLoad({ filter, namespace: "include-file" }, async ({ pluginData: { path } }) => { - const [name, format] = path.split(";"); + build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => { + const { searchParams } = new URL(uri); + const base64 = searchParams.has("base64"); + const minify = isStandalone === "true" && searchParams.has("minify"); + const noTrim = searchParams.get("trim") === "false"; + + const encoding = base64 ? "base64" : "utf-8"; + + let content; + if (!minify) { + content = await readFile(path, encoding); + if (!noTrim) content = content.trimEnd(); + } else { + if (path.endsWith(".html")) { + content = await minifyHtml(await readFile(path, "utf-8"), { + collapseWhitespace: true, + removeComments: true, + minifyCSS: true, + minifyJS: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true + }); + } else if (/[mc]?[jt]sx?$/.test(path)) { + const res = await esbuild.build({ + entryPoints: [path], + write: false, + minify: true, + bundle: true, + format: "esm" + }); + content = res.outputFiles[0].text; + } else { + throw new Error(`Don't know how to minify file type: ${path}`); + } + + if (base64) + content = Buffer.from(content).toString("base64"); + } + return { - contents: `export default ${JSON.stringify(await readFile(name, format ?? "utf-8"))}` + contents: `export default ${JSON.stringify(content)}` }; }); } @@ -217,7 +260,7 @@ export const commonOpts = { sourcemap: watch ? "inline" : "", legalComments: "linked", banner, - plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], + plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"], inject: ["./scripts/build/inject/react.mjs"], jsxFactory: "VencordCreateElement", diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 9c9741db5..62785867e 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -23,12 +23,11 @@ import "./settings"; import { debounce } from "@shared/debounce"; import { IpcEvents } from "@shared/IpcEvents"; import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron"; +import monacoHtml from "file://monacoWin.html?minify&base64"; import { FSWatcher, mkdirSync, watch, writeFileSync } from "fs"; import { open, readdir, readFile } from "fs/promises"; import { join, normalize } from "path"; -import monacoHtml from "~fileContent/monacoWin.html;base64"; - import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes"; import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, THEMES_DIR } from "./utils/constants"; import { makeLinksOpenExternally } from "./utils/externalLinks"; diff --git a/src/modules.d.ts b/src/modules.d.ts index 489799256..83a512b00 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -38,7 +38,7 @@ declare module "~git-remote" { export default remote; } -declare module "~fileContent/*" { +declare module "file://*" { const content: string; export default content; } diff --git a/src/plugins/shikiCodeblocks.desktop/index.ts b/src/plugins/shikiCodeblocks.desktop/index.ts index ef1b5d3df..27463195d 100644 --- a/src/plugins/shikiCodeblocks.desktop/index.ts +++ b/src/plugins/shikiCodeblocks.desktop/index.ts @@ -21,8 +21,7 @@ import "./shiki.css"; import { enableStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; - -import previewExampleText from "~fileContent/previewExample.tsx"; +import previewExampleText from "file://previewExample.tsx"; import { shiki } from "./api/shiki"; import { createHighlighter } from "./components/Highlighter"; From 41c5bbd9521eb0584b0516174247fe38f0069871 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 26 May 2024 15:44:04 -0400 Subject: [PATCH 05/77] new plugin WatchTogetherAdblock: block ads in youtube activity (#2021) Co-authored-by: vee --- scripts/build/common.mjs | 4 +- .../watchTogetherAdblock.desktop/README.md | 6 + .../watchTogetherAdblock.desktop/adguard.js | 262 ++++++++++++++++++ .../watchTogetherAdblock.desktop/index.ts | 15 + .../watchTogetherAdblock.desktop/native.ts | 21 ++ 5 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 src/plugins/watchTogetherAdblock.desktop/README.md create mode 100644 src/plugins/watchTogetherAdblock.desktop/adguard.js create mode 100644 src/plugins/watchTogetherAdblock.desktop/index.ts create mode 100644 src/plugins/watchTogetherAdblock.desktop/native.ts diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 457f3f22d..3b1473e1c 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -204,9 +204,7 @@ export const fileUrlPlugin = { const res = await esbuild.build({ entryPoints: [path], write: false, - minify: true, - bundle: true, - format: "esm" + minify: true }); content = res.outputFiles[0].text; } else { diff --git a/src/plugins/watchTogetherAdblock.desktop/README.md b/src/plugins/watchTogetherAdblock.desktop/README.md new file mode 100644 index 000000000..4c64df67b --- /dev/null +++ b/src/plugins/watchTogetherAdblock.desktop/README.md @@ -0,0 +1,6 @@ +# WatchTogetherAdblock + +Block ads in the YouTube WatchTogether activity via AdGuard + +Note that this only works for yourself, other users in the activity will still see ads. +Powered by a modified version of [Adguard's BlockYoutubeAdsShortcut](https://github.com/AdguardTeam/BlockYouTubeAdsShortcut) diff --git a/src/plugins/watchTogetherAdblock.desktop/adguard.js b/src/plugins/watchTogetherAdblock.desktop/adguard.js new file mode 100644 index 000000000..945f76bd5 --- /dev/null +++ b/src/plugins/watchTogetherAdblock.desktop/adguard.js @@ -0,0 +1,262 @@ +/* eslint-disable */ + +/** + * This file is part of AdGuard's Block YouTube Ads (https://github.com/AdguardTeam/BlockYouTubeAdsShortcut). + * + * Copyright (C) AdGuard Team + * + * AdGuard's Block YouTube Ads 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. + * + * AdGuard's Block YouTube Ads 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 AdGuard's Block YouTube Ads. If not, see . + */ + +const LOGO_ID = "block-youtube-ads-logo"; +const hiddenCSS = [ + "#__ffYoutube1", + "#__ffYoutube2", + "#__ffYoutube3", + "#__ffYoutube4", + "#feed-pyv-container", + "#feedmodule-PRO", + "#homepage-chrome-side-promo", + "#merch-shelf", + "#offer-module", + '#pla-shelf > ytd-pla-shelf-renderer[class="style-scope ytd-watch"]', + "#pla-shelf", + "#premium-yva", + "#promo-info", + "#promo-list", + "#promotion-shelf", + "#related > ytd-watch-next-secondary-results-renderer > #items > ytd-compact-promoted-video-renderer.ytd-watch-next-secondary-results-renderer", + "#search-pva", + "#shelf-pyv-container", + "#video-masthead", + "#watch-branded-actions", + "#watch-buy-urls", + "#watch-channel-brand-div", + "#watch7-branded-banner", + "#YtKevlarVisibilityIdentifier", + "#YtSparklesVisibilityIdentifier", + ".carousel-offer-url-container", + ".companion-ad-container", + ".GoogleActiveViewElement", + '.list-view[style="margin: 7px 0pt;"]', + ".promoted-sparkles-text-search-root-container", + ".promoted-videos", + ".searchView.list-view", + ".sparkles-light-cta", + ".watch-extra-info-column", + ".watch-extra-info-right", + ".ytd-carousel-ad-renderer", + ".ytd-compact-promoted-video-renderer", + ".ytd-companion-slot-renderer", + ".ytd-merch-shelf-renderer", + ".ytd-player-legacy-desktop-watch-ads-renderer", + ".ytd-promoted-sparkles-text-search-renderer", + ".ytd-promoted-video-renderer", + ".ytd-search-pyv-renderer", + ".ytd-video-masthead-ad-v3-renderer", + ".ytp-ad-action-interstitial-background-container", + ".ytp-ad-action-interstitial-slot", + ".ytp-ad-image-overlay", + ".ytp-ad-overlay-container", + ".ytp-ad-progress", + ".ytp-ad-progress-list", + '[class*="ytd-display-ad-"]', + '[layout*="display-ad-"]', + 'a[href^="http://www.youtube.com/cthru?"]', + 'a[href^="https://www.youtube.com/cthru?"]', + "ytd-action-companion-ad-renderer", + "ytd-banner-promo-renderer", + "ytd-compact-promoted-video-renderer", + "ytd-companion-slot-renderer", + "ytd-display-ad-renderer", + "ytd-promoted-sparkles-text-search-renderer", + "ytd-promoted-sparkles-web-renderer", + "ytd-search-pyv-renderer", + "ytd-single-option-survey-renderer", + "ytd-video-masthead-ad-advertiser-info-renderer", + "ytd-video-masthead-ad-v3-renderer", + "YTM-PROMOTED-VIDEO-RENDERER", +]; +/** +* Adds CSS to the page +*/ +const hideElements = () => { + const selectors = hiddenCSS; + if (!selectors) { + return; + } + const rule = selectors.join(", ") + " { display: none!important; }"; + const style = document.createElement("style"); + style.innerHTML = rule; + document.head.appendChild(style); +}; +/** +* Calls the "callback" function on every DOM change, but not for the tracked events +* @param {Function} callback callback function +*/ +const observeDomChanges = callback => { + const domMutationObserver = new MutationObserver(mutations => { + callback(mutations); + }); + domMutationObserver.observe(document.documentElement, { + childList: true, + subtree: true, + }); +}; +/** +* This function is supposed to be called on every DOM change +*/ +const hideDynamicAds = () => { + const elements = document.querySelectorAll("#contents > ytd-rich-item-renderer ytd-display-ad-renderer"); + if (elements.length === 0) { + return; + } + elements.forEach(el => { + if (el.parentNode && el.parentNode.parentNode) { + const parent = el.parentNode.parentNode; + if (parent.localName === "ytd-rich-item-renderer") { + parent.style.display = "none"; + } + } + }); +}; +/** +* This function checks if the video ads are currently running +* and auto-clicks the skip button. +*/ +const autoSkipAds = () => { + // If there's a video that plays the ad at this moment, scroll this ad + if (document.querySelector(".ad-showing")) { + const video = document.querySelector("video"); + if (video && video.duration) { + video.currentTime = video.duration; + // Skip button should appear after that, + // now simply click it automatically + setTimeout(() => { + const skipBtn = document.querySelector("button.ytp-ad-skip-button"); + if (skipBtn) { + skipBtn.click(); + } + }, 100); + } + } +}; +/** +* This function overrides a property on the specified object. +* +* @param {object} obj object to look for properties in +* @param {string} propertyName property to override +* @param {*} overrideValue value to set +*/ +const overrideObject = (obj, propertyName, overrideValue) => { + if (!obj) { + return false; + } + let overriden = false; + for (const key in obj) { + // eslint-disable-next-line no-prototype-builtins + if (obj.hasOwnProperty(key) && key === propertyName) { + obj[key] = overrideValue; + overriden = true; + // eslint-disable-next-line no-prototype-builtins + } else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") { + if (overrideObject(obj[key], propertyName, overrideValue)) { + overriden = true; + } + } + } + return overriden; +}; +/** +* Overrides JSON.parse and Response.json functions. +* Examines these functions arguments, looks for properties with the specified name there +* and if it exists, changes it's value to what was specified. +* +* @param {string} propertyName name of the property +* @param {*} overrideValue new value for the property +*/ +const jsonOverride = (propertyName, overrideValue) => { + const nativeJSONParse = JSON.parse; + JSON.parse = (...args) => { + const obj = nativeJSONParse.apply(this, args); + // Override it's props and return back to the caller + overrideObject(obj, propertyName, overrideValue); + return obj; + }; + // Override Response.prototype.json + const nativeResponseJson = Response.prototype.json; + Response.prototype.json = new Proxy(nativeResponseJson, { + apply(...args) { + // Call the target function, get the original Promise + const promise = Reflect.apply(...args); + // Create a new one and override the JSON inside + return new Promise((resolve, reject) => { + promise.then(data => { + overrideObject(data, propertyName, overrideValue); + resolve(data); + }).catch(error => reject(error)); + }); + }, + }); +}; +const addAdGuardLogoStyle = () => { }; +const addAdGuardLogo = () => { + if (document.getElementById(LOGO_ID)) { + return; + } + const logo = document.createElement("span"); + logo.innerHTML = "__logo_text__"; + logo.setAttribute("id", LOGO_ID); + if (window.location.hostname === "m.youtube.com") { + const btn = document.querySelector("header.mobile-topbar-header > button"); + if (btn) { + btn.parentNode?.insertBefore(logo, btn.nextSibling); + addAdGuardLogoStyle(); + } + } else if (window.location.hostname === "www.youtube.com") { + const code = document.getElementById("country-code"); + if (code) { + code.innerHTML = ""; + code.appendChild(logo); + addAdGuardLogoStyle(); + } + } else if (window.location.hostname === "music.youtube.com") { + const el = document.querySelector(".ytmusic-nav-bar#left-content"); + if (el) { + el.appendChild(logo); + addAdGuardLogoStyle(); + } + } else if (window.location.hostname === "www.youtube-nocookie.com") { + const code = document.querySelector("#yt-masthead #logo-container .content-region"); + if (code) { + code.innerHTML = ""; + code.appendChild(logo); + addAdGuardLogoStyle(); + } + } +}; +// Removes ads metadata from YouTube XHR requests +jsonOverride("adPlacements", []); +jsonOverride("playerAds", []); +// Applies CSS that hides YouTube ad elements +hideElements(); +// Some changes should be re-evaluated on every page change +addAdGuardLogo(); +hideDynamicAds(); +autoSkipAds(); +observeDomChanges(() => { + addAdGuardLogo(); + hideDynamicAds(); + autoSkipAds(); +}); diff --git a/src/plugins/watchTogetherAdblock.desktop/index.ts b/src/plugins/watchTogetherAdblock.desktop/index.ts new file mode 100644 index 000000000..2dbc13d4a --- /dev/null +++ b/src/plugins/watchTogetherAdblock.desktop/index.ts @@ -0,0 +1,15 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +// The entire code of this plugin can be found in native.ts +export default definePlugin({ + name: "WatchTogetherAdblock", + description: "Block ads in the YouTube WatchTogether activity via AdGuard", + authors: [Devs.ImLvna], +}); diff --git a/src/plugins/watchTogetherAdblock.desktop/native.ts b/src/plugins/watchTogetherAdblock.desktop/native.ts new file mode 100644 index 000000000..c4106c349 --- /dev/null +++ b/src/plugins/watchTogetherAdblock.desktop/native.ts @@ -0,0 +1,21 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { RendererSettings } from "@main/settings"; +import { app } from "electron"; +import adguard from "file://adguard.js?minify"; + +app.on("browser-window-created", (_, win) => { + win.webContents.on("frame-created", (_, { frame }) => { + frame.once("dom-ready", () => { + if (frame.url.includes("discordsays") && frame.url.includes("youtube.com")) { + if (!RendererSettings.store.plugins?.WatchTogetherAdblock?.enabled) return; + + frame.executeJavaScript(adguard); + } + }); + }); +}); From 6b4899804a64d7b2255d5b56fd19f8631dfc3600 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 26 May 2024 21:16:12 -0300 Subject: [PATCH 06/77] Summaries: Fix start error if no summaries-data exist --- src/plugins/seeSummaries/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx index 04a25198c..68c4f4a61 100644 --- a/src/plugins/seeSummaries/index.tsx +++ b/src/plugins/seeSummaries/index.tsx @@ -87,6 +87,7 @@ export default definePlugin({ async start() { await DataStore.update("summaries-data", summaries => { + summaries ??= {}; for (const key of Object.keys(summaries)) { for (let i = summaries[key].length - 1; i >= 0; i--) { if (summaries[key][i].time < Date.now() - 1000 * 60 * 60 * 24 * settings.store.summaryExpiryThresholdDays) { From 8bda3a1e6aa3285f3cd12c80821d72dac55e040d Mon Sep 17 00:00:00 2001 From: Ulysses Zhan Date: Mon, 27 May 2024 09:36:09 -0700 Subject: [PATCH 07/77] LoadingQuotes: more customization & custom quotes support (#1795) Co-authored-by: lewisakura Co-authored-by: Vendicated --- src/plugins/loadingQuotes/index.ts | 106 +++++++++++++-------------- src/plugins/loadingQuotes/quotes.txt | 37 ++++++++++ 2 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 src/plugins/loadingQuotes/quotes.txt diff --git a/src/plugins/loadingQuotes/index.ts b/src/plugins/loadingQuotes/index.ts index 97178979c..7d85cc90b 100644 --- a/src/plugins/loadingQuotes/index.ts +++ b/src/plugins/loadingQuotes/index.ts @@ -18,88 +18,82 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; +import presetQuotesText from "file://quotes.txt"; -// These are Xor encrypted to prevent you from spoiling yourself when you read the source code. -// don't worry about it :P -const quotes = [ - "Eyrokac", - "Rdcg$l`'k|~n", - 'H`tf$d&iajo+d`{"', - "Sucqplh`(Eclhualva()&", - "Lncgmka'8KNMDC,shpanf'`x./,", - "Ioqweijnfn*IeuvfvAotkfxo./,", - 'Hd{#cp\x7Ft$)nbd!{lq%mig~*\x7Fh`v#mk&sm{gx nd#idjb(a\x7Ffao"bja&amdkge!Rloìkhf)hyedfjjb*\'^hzdrdmm$lu\'|ao+mnqw$fijxh~bbmg#Tjmîefd+fnp#lpkffz5', - "h", - "sijklm&cam*rot\"hjjq'|ak\x7F xmv#wc'ep*mawmvvlrb(|ynr>\"Aqq&cgg-\x7F ugoh%rom)e\x7Fhdpp%$", - 'Tnfb}"u\'~`nno!kp$vvhfzeyee"a}%Tfam*Xh`fls%Jboldos-"lj`&hn)~ce!`jcbct|)gdbhnf$wikm$zgaxkmc%afely+og"144?\'ign+iu%p$qisiefr gpfa$', - "Ndtfv%ahfgk+ghtf$|ir(|z' Oguaw&`ggdj mgw$|ir(me|n", - "(!ͣ³$͙ʐ'ͩ¹#", - "(ネ◗ロ◑,マ-2ャユ✬", - "Ynw#hjil(ze+psgwp|&sgmkr!", - "Tikmolh`(fl+a!dvjk\x7F'y|e\x7Fe/,-", - "3/3750?5><9>885:7", - "mdmt", - "Wdn`khc+(oxbeof", - 'Ig"zkp*\'g{*xolglj`&~g|*gowg/$mgt(Eclm`.#ticf{l*xed"wl`&Kangj igbhqn\'d`dn `v#lqrw{3%$bhv-h|)kangj_imwhlhb', - "Tscmw%Tnoa~x", - "I‘f#npus(ec`e!vl$lhsm{`ncu\"ekw&f(defeov-$Rnf|)sdu‘pf$wcam{ceg!vl$du'D`d~x-\"jw%oi(okht-\"DJP)Kags,!mq$du'A‐|n sg`akrkq)~jkdl#pj&diefbnf\"jp)&@F\\*{ltq#Hlhrp'", - "Ynw$v`&cg`dl fml`%rhlhs*", - "Dnl$p%qhz{s' hv$w%hh|aceg!;#gpvt(fl+cndea`&dg|fon&v#wjjqm(", - "\ud83d)pft`gs(ec`e!13$qojmz#", - "a!njcmr'ide~nu\"lb%rheoedldpz$lu'gbkr", - "dn\"zkp&kgo4", - "hnpqkw", - "sn\"fau", - "Sn\"tmqnh}}*musvkaw&flf&+ldv$w%lr{}*aulr#vlao|)cetn\"jp$", - "Dxkmc%ot(hhxomwwai'{hln", - "hd{#}js&(pe~'sg#gprb(3#\"", - "hd{b${", - "<;vqkijbq33271:56<3799?24944:", - "Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&", - "snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno", - "Wdn`khc'|f*eghl{%" -]; +const presetQuotes = presetQuotesText.split("\n").map(quote => /^\s*[^#\s]/.test(quote) && quote.trim()).filter(Boolean) as string[]; +const noQuotesQuote = "Did you really disable all loading quotes? What a buffoon you are..."; const settings = definePluginSettings({ replaceEvents: { - description: "Replace Event Quotes too", + description: "Should this plugin also apply during events with special event themed quotes? (e.g. Halloween)", type: OptionType.BOOLEAN, default: true - } + }, + enablePluginPresetQuotes: { + description: "Enable the quotes preset by this plugin", + type: OptionType.BOOLEAN, + default: true + }, + enableDiscordPresetQuotes: { + description: "Enable Discord's preset quotes (including event quotes, during events)", + type: OptionType.BOOLEAN, + default: false + }, + additionalQuotes: { + description: "Additional custom quotes to possibly appear, separated by the below delimiter", + type: OptionType.STRING, + default: "", + }, + additionalQuotesDelimiter: { + description: "Delimiter for additional quotes", + type: OptionType.STRING, + default: "|", + }, }); export default definePlugin({ name: "LoadingQuotes", description: "Replace Discords loading quotes", - authors: [Devs.Ven, Devs.KraXen72], + authors: [Devs.Ven, Devs.KraXen72, Devs.UlyssesZhan], settings, patches: [ { - find: ".LOADING_DID_YOU_KNOW}", + find: ".LOADING_DID_YOU_KNOW", replacement: [ { - match: /"_loadingText",function\(\)\{/, - replace: "$&return $self.quote;", + match: /"_loadingText".+?(?=(\i)\[.{0,10}\.random)/, + replace: "$&$self.mutateQuotes($1)," }, { - match: /"_eventLoadingText",function\(\)\{/, - replace: "$&return $self.quote;", + match: /"_eventLoadingText".+?(?=(\i)\[.{0,10}\.random)/, + replace: "$&$self.mutateQuotes($1),", predicate: () => settings.store.replaceEvents } - ], + ] }, ], - xor(quote: string) { - const key = "read if cute"; - const codes = Array.from(quote, (s, i) => s.charCodeAt(0) ^ (i % key.length)); - return String.fromCharCode(...codes); - }, + mutateQuotes(quotes: string[]) { + try { + const { enableDiscordPresetQuotes, additionalQuotes, additionalQuotesDelimiter, enablePluginPresetQuotes } = settings.store; - get quote() { - return this.xor(quotes[Math.floor(Math.random() * quotes.length)]); + if (!enableDiscordPresetQuotes) + quotes.length = 0; + + + if (enablePluginPresetQuotes) + quotes.push(...presetQuotes); + + quotes.push(...additionalQuotes.split(additionalQuotesDelimiter).filter(Boolean)); + + if (!quotes.length) + quotes.push(noQuotesQuote); + } catch (e) { + new Logger("LoadingQuotes").error("Failed to mutate quotes", e); + } } }); diff --git a/src/plugins/loadingQuotes/quotes.txt b/src/plugins/loadingQuotes/quotes.txt new file mode 100644 index 000000000..cfb01350e --- /dev/null +++ b/src/plugins/loadingQuotes/quotes.txt @@ -0,0 +1,37 @@ +# Blank lines and lines starting with "#" are ignored + +Explode +Read if cute +Have a nice day! +Starting Lightcord... +Loading 0BDFDB.plugin.js... +Installing BetterDiscord... +h +shhhhh did you know that you're my favourite user? But don't tell the others!! +Today's video is sponsored by Raid Shadow Legends, one of the biggest mobile role-playing games of 2019 and it's totally free! +Never gonna give you up, Never gonna let you down +( ͡° ͜ʖ ͡°) +(ノ◕ヮ◕)ノ*:・゚✧ +You look so pretty today! +Thinking of a funny quote... +3.141592653589793 +meow +Welcome, friend +If you, or someone you love, has Ligma, please see the Ligma health line at https://bit.ly/ligma_hotline +Trans Rights +I’d just like to interject for a moment. What you’re refering to as Linux, is in fact, GNU/Linux, or as I’ve recently taken to calling it, GNU plus Linux. +You're doing good today! +Don't worry, it's nothing 9 cups of coffee couldn't solve! +�(repeat like 30 times) +a light amount of tomfoolery is okay +do you love? +horror +so eepy +So without further ado, let's just jump right into it! +Dying is absolutely safe +hey you! you're cute :)) +heya ~ +<:trolley:997086295010594867> +Time is gone, space is insane. Here it comes, here again. +sometimes it's okay to just guhhhhhhhhhhhhhh +Welcome to nginx! From c431b7d2ab095118f642e2aefdbae3b5de290592 Mon Sep 17 00:00:00 2001 From: nin0dev Date: Mon, 27 May 2024 20:21:12 -0400 Subject: [PATCH 08/77] fix(MessageLogger): correctly mark markdown headers red (#2511) Co-authored-by: vee --- src/plugins/messageLogger/deleteStyleText.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/messageLogger/deleteStyleText.css b/src/plugins/messageLogger/deleteStyleText.css index 3477ef229..a4e9a93c1 100644 --- a/src/plugins/messageLogger/deleteStyleText.css +++ b/src/plugins/messageLogger/deleteStyleText.css @@ -3,6 +3,11 @@ color: var(--status-danger, #f04747) !important; } +/* Markdown title highlighting */ +.messagelogger-deleted [class*="contents"] :is(h1, h2, h3) { + color: var(--status-danger, #f04747) !important; +} + /* Bot "thinking" text highlighting */ .messagelogger-deleted [class*="colorStandard"] { color: var(--status-danger, #f04747) !important; From c2f8837602d0bcb931f2cf3d743425e897ff1adf Mon Sep 17 00:00:00 2001 From: sunnie <78964224+sunnniee@users.noreply.github.com> Date: Tue, 28 May 2024 03:23:30 +0300 Subject: [PATCH 09/77] new plugin: MaskedLinkPaste (#2514) Co-authored-by: vee --- src/plugins/maskedLinkPaste/README.md | 5 ++++ src/plugins/maskedLinkPaste/index.ts | 36 +++++++++++++++++++++++++++ src/utils/constants.ts | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/plugins/maskedLinkPaste/README.md create mode 100644 src/plugins/maskedLinkPaste/index.ts diff --git a/src/plugins/maskedLinkPaste/README.md b/src/plugins/maskedLinkPaste/README.md new file mode 100644 index 000000000..30beccb64 --- /dev/null +++ b/src/plugins/maskedLinkPaste/README.md @@ -0,0 +1,5 @@ +# MaskedLinkPaste + +Pasting a link while you have text selected will paste your link as a masked link at that location + +![](https://github.com/Vendicated/Vencord/assets/78964224/1d3be2c6-7957-44c9-92ec-551069d46c02) diff --git a/src/plugins/maskedLinkPaste/index.ts b/src/plugins/maskedLinkPaste/index.ts new file mode 100644 index 000000000..dc8689928 --- /dev/null +++ b/src/plugins/maskedLinkPaste/index.ts @@ -0,0 +1,36 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants.js"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; + +const linkRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/; + +const { SlateTransforms } = findByPropsLazy("SlateTransforms"); + +export default definePlugin({ + name: "MaskedLinkPaste", + authors: [Devs.TheSun], + description: "Pasting a link while having text selected will paste a hyperlink", + patches: [{ + find: ".selection,preventEmojiSurrogates:", + replacement: { + match: /(?<=SlateTransforms.delete.{0,50})(\i)\.insertText\((\i)\)/, + replace: "$self.handlePaste($1, $2, () => $&)" + } + }], + + handlePaste(editor, content: string, originalBehavior: () => void) { + if (content && linkRegex.test(content) && editor.operations?.[0]?.type === "remove_text") { + SlateTransforms.insertText( + editor, + `[${editor.operations[0].text}](${content})` + ); + } + else originalBehavior(); + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 2f686d69d..69953855f 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -186,7 +186,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 296776625432035328n, }, TheSun: { - name: "ActuallyTheSun", + name: "sunnie", id: 406028027768733696n }, axyie: { From 5b35d7c644ce306a6a9b70d3a33b9583eaecad55 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 28 May 2024 02:35:40 +0200 Subject: [PATCH 10/77] fix occasional errors in Dearrow & ImageZoom --- src/plugins/dearrow/index.tsx | 4 ++-- src/plugins/imageZoom/components/Magnifier.tsx | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 888e2bb45..89199da8f 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -182,8 +182,8 @@ export default definePlugin({ // add dearrow button { - match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/, - replace: "children:[$self.renderButton(this),", + match: /children:\[(?=null!=\i\?(\i)\.renderSuppressButton)/, + replace: "children:[$self.renderButton($1),", predicate: () => !settings.store.hideButton } ] diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index aadd0903a..585026d60 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -67,15 +67,18 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i } }; const syncVideos = () => { - currentVideoElementRef.current!.currentTime = originalVideoElementRef.current!.currentTime; + if (currentVideoElementRef.current && originalVideoElementRef.current) + currentVideoElementRef.current.currentTime = originalVideoElementRef.current.currentTime; }; const updateMousePosition = (e: MouseEvent) => { + if (!element.current) return; + if (instance.state.mouseOver && instance.state.mouseDown) { const offset = size.current / 2; const pos = { x: e.pageX, y: e.pageY }; - const x = -((pos.x - element.current!.getBoundingClientRect().left) * zoom.current - offset); - const y = -((pos.y - element.current!.getBoundingClientRect().top) * zoom.current - offset); + const x = -((pos.x - element.current.getBoundingClientRect().left) * zoom.current - offset); + const y = -((pos.y - element.current.getBoundingClientRect().top) * zoom.current - offset); setLensPosition({ x: e.x - offset, y: e.y - offset }); setImagePosition({ x, y }); setOpacity(1); @@ -184,6 +187,7 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i src={originalVideoElementRef.current?.src ?? instance.props.src} autoPlay loop + muted /> ) : ( Date: Tue, 28 May 2024 09:15:48 +0700 Subject: [PATCH 11/77] USRBG: support new simplified profile (#2501) Co-authored-by: vee --- src/plugins/usrbg/index.tsx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index b92839a9a..1221cb9c5 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -27,10 +27,10 @@ import style from "./index.css?managed"; const API_URL = "https://usrbg.is-hardly.online/users"; interface UsrbgApiReturn { - endpoint: string - bucket: string - prefix: string - users: Record + endpoint: string; + bucket: string; + prefix: string; + users: Record; } const settings = definePluginSettings({ @@ -73,6 +73,19 @@ export default definePlugin({ } ] }, + { + find: /overrideBannerSrc:\i,profileType:/, + replacement: [ + { + match: /(\i)\.premiumType/, + replace: "$self.premiumHook($1)||$&" + }, + { + match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},overrideBannerSrc:)/, + replace: "$1.overrideBannerSrc=$self.useBannerHook($1);" + } + ] + }, { find: "\"data-selenium-video-tile\":", predicate: () => settings.store.voiceBackground, @@ -123,7 +136,7 @@ export default definePlugin({ return !!this.data?.users[userId]; }, - getImageUrl(userId: string): string|null { + getImageUrl(userId: string): string | null { if (!this.userHasBackground(userId)) return null; // We can assert that data exists because userHasBackground returned true From b9e83d9d28efba44935d242b1e2cded6510be431 Mon Sep 17 00:00:00 2001 From: Lexi <72424960+KontrollFreek@users.noreply.github.com> Date: Mon, 27 May 2024 22:22:42 -0400 Subject: [PATCH 12/77] new plugin DontRoundMyTimestamps: round 7.6y -> 7y instead of 8y (#2060) Co-authored-by: V --- src/plugins/dontRoundMyTimestamps/index.ts | 35 ++++++++++++++++++++++ src/utils/constants.ts | 4 +++ 2 files changed, 39 insertions(+) create mode 100644 src/plugins/dontRoundMyTimestamps/index.ts diff --git a/src/plugins/dontRoundMyTimestamps/index.ts b/src/plugins/dontRoundMyTimestamps/index.ts new file mode 100644 index 000000000..4c432c73f --- /dev/null +++ b/src/plugins/dontRoundMyTimestamps/index.ts @@ -0,0 +1,35 @@ +/* + * 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 { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { moment } from "@webpack/common"; + +export default definePlugin({ + name: "DontRoundMyTimestamps", + authors: [Devs.Lexi], + description: "Always rounds relative timestamps down, so 7.6y becomes 7y instead of 8y", + + start() { + moment.relativeTimeRounding(Math.floor); + }, + + stop() { + moment.relativeTimeRounding(Math.round); + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 69953855f..4e3422526 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -402,6 +402,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "maisy", id: 257109471589957632n, }, + Lexi: { + name: "Lexi", + id: 506101469787717658n + }, Mopi: { name: "Mopi", id: 1022189106614243350n From a78dba321d340a117f278367480006ba62222643 Mon Sep 17 00:00:00 2001 From: vee Date: Tue, 28 May 2024 22:31:58 +0200 Subject: [PATCH 13/77] ConsoleShortcuts: Fix autocomplete on lazies, add more utils (#2519) --- src/plugins/consoleShortcuts/index.ts | 260 +++++++++++++++---------- src/plugins/consoleShortcuts/native.ts | 16 ++ src/utils/lazy.ts | 29 +-- 3 files changed, 192 insertions(+), 113 deletions(-) create mode 100644 src/plugins/consoleShortcuts/native.ts diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index b0efe8a08..ee86b5fcf 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -17,138 +17,198 @@ */ import { Devs } from "@utils/constants"; +import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; +import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy"; import { relaunch } from "@utils/native"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; -import definePlugin, { StartAt } from "@utils/types"; +import definePlugin, { PluginNative, StartAt } from "@utils/types"; import * as Webpack from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack"; import * as Common from "@webpack/common"; import type { ComponentType } from "react"; -const WEB_ONLY = (f: string) => () => { +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 + }); + }; + +function makeShortcuts() { + function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { + 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 result = (() => { + switch (matches.length) { + case 0: return null; + case 1: return matches[0]; + default: + const uniqueMatches = [...new Set(matches)]; + if (uniqueMatches.length > 1) + console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches); + + return matches[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)!), + find, + findAll: findAll, + findByProps, + findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), + findByCode: newFindWrapper(filters.byCode), + findAllByCode: (code: string) => findAll(filters.byCode(code)), + findComponentByCode: newFindWrapper(filters.componentByCode), + findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), + findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], + findStore: newFindWrapper(filters.byStoreName), + PluginsApi: { getter: () => Vencord.Plugins }, + plugins: { getter: () => Vencord.Plugins.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) => { + const prevWin = fakeRenderWin?.deref(); + const win = prevWin?.closed === false + ? prevWin + : window.open("about:blank", "Fake Render", "popup,width=500,height=500")!; + fakeRenderWin = new WeakRef(win); + win.focus(); + + const doc = win.document; + doc.body.style.margin = "1em"; + + if (!win.prepared) { + win.prepared = true; + + [...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => { + const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement; + + if (s.parentElement?.tagName === "HEAD") + doc.head.append(n); + else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-")) + doc.documentElement.append(n); + else + doc.body.append(n); + }); + } + + 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 }, + guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false }, + me: { getter: () => Common.UserStore.getCurrentUser(), preload: false }, + meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false }, + messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false } + }; +} + +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; + + if (value) define(window.shortcutList, key, { value }); + + return value; +} + export default definePlugin({ name: "ConsoleShortcuts", description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", authors: [Devs.Ven], - getShortcuts(): Record { - function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { - 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 result = (() => { - switch (matches.length) { - case 0: return null; - case 1: return matches[0]; - default: - const uniqueMatches = [...new Set(matches)]; - if (uniqueMatches.length > 1) - console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches); - - return matches[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)!), - find, - findAll: findAll, - findByProps, - findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), - findByCode: newFindWrapper(filters.byCode), - findAllByCode: (code: string) => findAll(filters.byCode(code)), - findComponentByCode: newFindWrapper(filters.componentByCode), - findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), - findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], - findStore: newFindWrapper(filters.byStoreName), - PluginsApi: { getter: () => Vencord.Plugins }, - plugins: { getter: () => Vencord.Plugins.plugins }, - Settings: { getter: () => Vencord.Settings }, - Api: { getter: () => Vencord.Api }, - reload: () => location.reload(), - restart: IS_WEB ? WEB_ONLY("restart") : relaunch, - canonicalizeMatch, - canonicalizeReplace, - canonicalizeReplacement, - fakeRender: (component: ComponentType, props: any) => { - const prevWin = fakeRenderWin?.deref(); - const win = prevWin?.closed === false ? prevWin : window.open("about:blank", "Fake Render", "popup,width=500,height=500")!; - fakeRenderWin = new WeakRef(win); - win.focus(); - - const doc = win.document; - doc.body.style.margin = "1em"; - - if (!win.prepared) { - win.prepared = true; - - [...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => { - const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement; - - if (s.parentElement?.tagName === "HEAD") - doc.head.append(n); - else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-")) - doc.documentElement.append(n); - else - doc.body.append(n); - }); - } - - Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); - } - }; - }, - startAt: StartAt.Init, start() { - const shortcuts = this.getShortcuts(); + const shortcuts = makeShortcuts(); window.shortcutList = {}; for (const [key, val] of Object.entries(shortcuts)) { - if (val.getter != null) { - Object.defineProperty(window.shortcutList, key, { - get: val.getter, - configurable: true, - enumerable: true + if ("getter" in val) { + define(window.shortcutList, key, { + get: () => loadAndCacheShortcut(key, val, true) }); - Object.defineProperty(window, key, { - get: () => window.shortcutList[key], - configurable: true, - enumerable: true + define(window, key, { + get: () => window.shortcutList[key] }); } else { window.shortcutList[key] = val; window[key] = val; } } + + // unproxy loaded modules + Webpack.onceReady.then(() => { + setTimeout(() => this.eagerLoad(false), 1000); + + if (!IS_WEB) { + const Native = VencordNative.pluginHelpers.ConsoleShortcuts as PluginNative; + Native.initDevtoolsOpenEagerLoad(); + } + }); + }, + + async eagerLoad(forceLoad: boolean) { + await Webpack.onceReady; + + const shortcuts = makeShortcuts(); + + for (const [key, val] of Object.entries(shortcuts)) { + if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue; + + try { + loadAndCacheShortcut(key, val, forceLoad); + } catch { } // swallow not found errors in DEV + } }, stop() { delete window.shortcutList; - for (const key in this.getShortcuts()) { + for (const key in makeShortcuts()) { delete window[key]; } } diff --git a/src/plugins/consoleShortcuts/native.ts b/src/plugins/consoleShortcuts/native.ts new file mode 100644 index 000000000..763b239a4 --- /dev/null +++ b/src/plugins/consoleShortcuts/native.ts @@ -0,0 +1,16 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { IpcMainInvokeEvent } from "electron"; + +export function initDevtoolsOpenEagerLoad(e: IpcMainInvokeEvent) { + const handleDevtoolsOpened = () => e.sender.executeJavaScript("Vencord.Plugins.plugins.ConsoleShortcuts.eagerLoad(true)"); + + if (e.sender.isDevToolsOpened()) + handleDevtoolsOpened(); + else + e.sender.once("devtools-opened", () => handleDevtoolsOpened()); +} diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts index a61785df9..e46e44ad7 100644 --- a/src/utils/lazy.ts +++ b/src/utils/lazy.ts @@ -35,8 +35,8 @@ const unconfigurable = ["arguments", "caller", "prototype"]; const handler: ProxyHandler = {}; -const kGET = Symbol.for("vencord.lazy.get"); -const kCACHE = Symbol.for("vencord.lazy.cached"); +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", @@ -53,11 +53,11 @@ for (const method of [ "setPrototypeOf" ]) { handler[method] = - (target: any, ...args: any[]) => Reflect[method](target[kGET](), ...args); + (target: any, ...args: any[]) => Reflect[method](target[SYM_LAZY_GET](), ...args); } handler.ownKeys = target => { - const v = target[kGET](); + const v = target[SYM_LAZY_GET](); const keys = Reflect.ownKeys(v); for (const key of unconfigurable) { if (!keys.includes(key)) keys.push(key); @@ -69,7 +69,7 @@ handler.getOwnPropertyDescriptor = (target, p) => { if (typeof p === "string" && unconfigurable.includes(p)) return Reflect.getOwnPropertyDescriptor(target, p); - const descriptor = Reflect.getOwnPropertyDescriptor(target[kGET](), p); + const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_LAZY_GET](), p); if (descriptor) Object.defineProperty(target, p, descriptor); return descriptor; @@ -92,31 +92,34 @@ export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T let tries = 0; const proxyDummy = Object.assign(function () { }, { - [kCACHE]: void 0 as T | undefined, - [kGET]() { - if (!proxyDummy[kCACHE] && attempts > tries++) { - proxyDummy[kCACHE] = factory(); - if (!proxyDummy[kCACHE] && attempts === tries) + [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); } - return proxyDummy[kCACHE]; + return proxyDummy[SYM_LAZY_CACHED]; } }); 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[kGET](), p, receiver), + () => Reflect.get(target[SYM_LAZY_GET](), p, receiver), attempts, true ); - const lazyTarget = target[kGET](); + const lazyTarget = target[SYM_LAZY_GET](); if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { return Reflect.get(lazyTarget, p, receiver); } From 86aabe73eb4987d4fd9a96bc528f891d1998e876 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 29 May 2024 04:24:48 +0200 Subject: [PATCH 14/77] add more flags for preventing background unloading --- src/main/patcher.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/patcher.ts b/src/main/patcher.ts index f0e3c3ab4..a3725ef9b 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -140,8 +140,14 @@ if (!IS_VANILLA) { return originalAppend.apply(this, args); }; + // disable renderer backgrounding to prevent the app from unloading when in the background + // https://github.com/electron/electron/issues/2822 + // https://github.com/GoogleChrome/chrome-launcher/blob/5a27dd574d47a75fec0fb50f7b774ebf8a9791ba/docs/chrome-flags-for-tools.md#task-throttling // Work around discord unloading when in background + // Discord also recently started adding these flags but only on windows for some reason dunno why, it happens on Linux too app.commandLine.appendSwitch("disable-renderer-backgrounding"); + app.commandLine.appendSwitch("disable-background-timer-throttling"); + app.commandLine.appendSwitch("disable-backgrounding-occluded-windows"); } else { console.log("[Vencord] Running in vanilla mode. Not loading Vencord"); } From da01237c054862bebb1089e738a4354f60294b59 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 29 May 2024 05:21:05 +0200 Subject: [PATCH 15/77] Summaries: update README --- src/plugins/seeSummaries/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/seeSummaries/README.md b/src/plugins/seeSummaries/README.md index dfb90bab3..1c8b6c793 100644 --- a/src/plugins/seeSummaries/README.md +++ b/src/plugins/seeSummaries/README.md @@ -2,6 +2,8 @@ Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations. +Read more about summaries in the [official Discord help article](https://support.discord.com/hc/en-us/articles/12926016807575-In-Channel-Conversation-Summaries)! + Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled. ![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745) From 9b9a5322c94948a6a444cac0772cfcc4a92c609b Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 29 May 2024 06:16:11 +0200 Subject: [PATCH 16/77] webpack: make window exports non enumerable --- src/webpack/patchWebpack.ts | 4 ++-- src/webpack/webpack.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 311e6f2bc..da5ca8b96 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -209,7 +209,7 @@ function patchFactories(factories: Record Date: Wed, 29 May 2024 04:57:18 -0300 Subject: [PATCH 17/77] feat(API): updateMessage API for forcing re-renders --- src/api/MessageUpdater.ts | 29 ++++++++++++++++ src/api/index.ts | 6 ++++ src/plugins/_api/messageUpdater.ts | 37 +++++++++++++++++++++ src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/invisibleChat.desktop/index.tsx | 14 +++----- src/plugins/messageLinkEmbeds/index.tsx | 12 ++----- src/webpack/common/types/stores.d.ts | 25 ++++++++++++++ src/webpack/common/utils.ts | 1 + 8 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 src/api/MessageUpdater.ts create mode 100644 src/plugins/_api/messageUpdater.ts diff --git a/src/api/MessageUpdater.ts b/src/api/MessageUpdater.ts new file mode 100644 index 000000000..5cac80528 --- /dev/null +++ b/src/api/MessageUpdater.ts @@ -0,0 +1,29 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { MessageCache, MessageStore } from "@webpack/common"; +import { FluxStore } from "@webpack/types"; +import { Message } from "discord-types/general"; + +/** + * Update and re-render a message + * @param channelId The channel id of the message + * @param messageId The message id + * @param fields The fields of the message to change. Leave empty if you just want to re-render + */ +export function updateMessage(channelId: string, messageId: string, fields?: Partial) { + const channelMessageCache = MessageCache.getOrCreate(channelId); + if (!channelMessageCache.has(messageId)) return; + + // To cause a message to re-render, we basically need to create a new instance of the message and obtain a new reference + // If we have fields to modify we can use the merge method of the class, otherwise we just create a new instance with the old fields + const newChannelMessageCache = channelMessageCache.update(messageId, (oldMessage: any) => { + return fields ? oldMessage.merge(fields) : new oldMessage.constructor(oldMessage); + }); + + MessageCache.commit(newChannelMessageCache); + (MessageStore as unknown as FluxStore).emitChange(); +} diff --git a/src/api/index.ts b/src/api/index.ts index 5dca63105..02c70008a 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -26,6 +26,7 @@ import * as $MessageAccessories from "./MessageAccessories"; import * as $MessageDecorations from "./MessageDecorations"; import * as $MessageEventsAPI from "./MessageEvents"; import * as $MessagePopover from "./MessagePopover"; +import * as $MessageUpdater from "./MessageUpdater"; import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; @@ -110,3 +111,8 @@ export const ContextMenu = $ContextMenu; * An API allowing you to add buttons to the chat input */ export const ChatButtons = $ChatButtons; + +/** + * An API allowing you to update and re-render messages + */ +export const MessageUpdater = $MessageUpdater; diff --git a/src/plugins/_api/messageUpdater.ts b/src/plugins/_api/messageUpdater.ts new file mode 100644 index 000000000..8f6cca26a --- /dev/null +++ b/src/plugins/_api/messageUpdater.ts @@ -0,0 +1,37 @@ +/* + * 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 { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "MessageUpdaterAPI", + description: "API for updating and re-rendering messages.", + authors: [Devs.Nuckyz], + + patches: [ + { + // Message accessories have a custom logic to decide if they should render again, so we need to make it not ignore changed message reference + find: "}renderEmbeds(", + replacement: { + match: /(?<=this.props,\i,\[)"message",/, + replace: "" + } + } + ] +}); diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 427f36ce8..737406cf5 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -333,7 +333,7 @@ export default definePlugin({ ] }, { - find: "renderEmbeds(", + find: "}renderEmbeds(", replacement: [ { // Call our function to decide whether the embed should be ignored or not diff --git a/src/plugins/invisibleChat.desktop/index.tsx b/src/plugins/invisibleChat.desktop/index.tsx index 3dfe51e77..884ffafe3 100644 --- a/src/plugins/invisibleChat.desktop/index.tsx +++ b/src/plugins/invisibleChat.desktop/index.tsx @@ -18,12 +18,13 @@ import { addChatBarButton, ChatBarButton } from "@api/ChatButtons"; import { addButton, removeButton } from "@api/MessagePopover"; +import { updateMessage } from "@api/MessageUpdater"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getStegCloak } from "@utils/dependencies"; import definePlugin, { OptionType } from "@utils/types"; -import { ChannelStore, Constants, FluxDispatcher, RestAPI, Tooltip } from "@webpack/common"; +import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common"; import { Message } from "discord-types/general"; import { buildDecModal } from "./components/DecryptionModal"; @@ -103,7 +104,7 @@ export default definePlugin({ name: "InvisibleChat", description: "Encrypt your Messages in a non-suspicious way!", authors: [Devs.SammCheese], - dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI"], + dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI", "MessageUpdaterAPI"], patches: [ { // Indicator @@ -180,14 +181,7 @@ export default definePlugin({ message.embeds.push(embed); } - this.updateMessage(message); - }, - - updateMessage: (message: any) => { - FluxDispatcher.dispatch({ - type: "MESSAGE_UPDATE", - message, - }); + updateMessage(message.channel_id, message.id, { embeds: message.embeds }); }, popOverIcon: () => , diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 6c8fd83e9..e76d53e4a 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -17,6 +17,7 @@ */ import { addAccessory, removeAccessory } from "@api/MessageAccessories"; +import { updateMessage } from "@api/MessageUpdater"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; @@ -28,7 +29,6 @@ import { Button, ChannelStore, Constants, - FluxDispatcher, GuildStore, IconUtils, MessageStore, @@ -250,15 +250,9 @@ function MessageEmbedAccessory({ message }: { message: Message; }) { if (linkedMessage) { messageCache.set(messageID, { message: linkedMessage, fetched: true }); } else { - const msg = { ...message } as any; - delete msg.embeds; - delete msg.interaction; messageFetchQueue.unshift(() => fetchMessage(channelID, messageID) - .then(m => m && FluxDispatcher.dispatch({ - type: "MESSAGE_UPDATE", - message: msg - })) + .then(m => m && updateMessage(message.channel_id, message.id)) ); continue; } @@ -367,7 +361,7 @@ export default definePlugin({ name: "MessageLinkEmbeds", description: "Adds a preview to messages that link another message", authors: [Devs.TheSun, Devs.Ven, Devs.RyanCaoDev], - dependencies: ["MessageAccessoriesAPI"], + dependencies: ["MessageAccessoriesAPI", "MessageUpdaterAPI"], settings, diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 083ec2694..f1fc68e8b 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -41,8 +41,33 @@ export class FluxStore { __getLocalVars(): Record; } +export class FluxEmitter { + constructor(); + + changeSentinel: number; + changedStores: Set; + isBatchEmitting: boolean; + isDispatching: boolean; + isPaused: boolean; + pauseTimer: NodeJS.Timeout | null; + reactChangedStores: Set; + + batched(batch: (...args: any[]) => void): void; + destroy(): void; + emit(): void; + emitNonReactOnce(): void; + emitReactOnce(): void; + getChangeSentinel(): number; + getIsPaused(): boolean; + injectBatchEmitChanges(batch: (...args: any[]) => void): void; + markChanged(store: FluxStore): void; + pause(): void; + resume(): void; +} + export interface Flux { Store: typeof FluxStore; + Emitter: FluxEmitter; } export class WindowStore extends FluxStore { diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 72a71f31c..bb96861f1 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -144,6 +144,7 @@ const persistFilter = filters.byCode("[zustand persist middleware]"); export const { persist: zustandPersist } = findLazy(m => m.persist && persistFilter(m.persist)); export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); +export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); export const InviteActions = findByPropsLazy("resolveInvite"); From 05a40445c8bc8ec85b624a58fab5791029f67501 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 29 May 2024 06:45:44 -0300 Subject: [PATCH 18/77] refactor: improve build scripts & automatic testing - Fix reporter breaking because of ConsoleShortcuts - Fix extractAndLoadChunks issue with 2 match groups; Improve testing of lazy extractAndLoadChunks - Reporter: Properly implement reporter build of Vencord; Test more plugins; Fix running in wrong pages - Fix wrong external files and clean up build script; Remove non used stuff --- .github/workflows/build.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/reportBrokenPlugins.yml | 4 +- package.json | 3 + scripts/build/build.mjs | 22 ++--- scripts/build/buildWeb.mjs | 54 ++++++----- scripts/build/common.mjs | 30 ++++--- scripts/generateReport.ts | 65 ++++---------- src/debug/Tracer.ts | 8 +- src/globals.d.ts | 3 +- src/plugins/arRPC.web/index.tsx | 3 +- src/plugins/devCompanion.dev/index.tsx | 3 +- src/plugins/index.ts | 94 ++++++++++++++------ src/plugins/invisibleChat.desktop/index.tsx | 6 +- src/plugins/shikiCodeblocks.desktop/index.ts | 6 +- src/plugins/vcNarrator/index.tsx | 3 +- src/plugins/xsOverlay.desktop/index.ts | 4 +- src/utils/Logger.ts | 5 ++ src/utils/dependencies.ts | 10 --- src/utils/types.ts | 11 +++ src/webpack/common/internal.tsx | 4 +- src/webpack/webpack.ts | 59 +++++++----- 22 files changed, 225 insertions(+), 176 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c7a2f24e8..ba22b1230 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build web - run: pnpm buildWeb --standalone + run: pnpm buildWebStandalone - name: Build run: pnpm build --standalone diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8407e08e2..190e3069c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,7 +32,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build web - run: pnpm buildWeb --standalone + run: pnpm buildWebStandalone - name: Publish extension run: | diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml index d3a882fa3..a669c1a27 100644 --- a/.github/workflows/reportBrokenPlugins.yml +++ b/.github/workflows/reportBrokenPlugins.yml @@ -37,8 +37,8 @@ jobs: with: chrome-version: stable - - name: Build web - run: pnpm buildWeb --standalone --dev + - name: Build Vencord Reporter Version + run: pnpm buildReporter - name: Create Report timeout-minutes: 10 diff --git a/package.json b/package.json index 29b1506e2..43ac36304 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,10 @@ "build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs", "buildStandalone": "pnpm build --standalone", "buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", + "buildWebStandalone": "pnpm buildWeb --standalone", + "buildReporter": "pnpm buildWebStandalone --reporter --skip-extension", "watch": "pnpm build --watch", + "watchWeb": "pnpm buildWeb --watch", "generatePluginJson": "tsx scripts/generatePluginList.ts", "generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types", "inject": "node scripts/runInstaller.mjs", diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 0c2a930a0..fcf56f66c 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -21,19 +21,21 @@ import esbuild from "esbuild"; import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isDev, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, VERSION, watch } from "./common.mjs"; const defines = { - IS_STANDALONE: isStandalone, - IS_DEV: JSON.stringify(isDev), - IS_UPDATER_DISABLED: updaterDisabled, + IS_STANDALONE, + IS_DEV, + IS_REPORTER, + IS_UPDATER_DISABLED, IS_WEB: false, IS_EXTENSION: false, VERSION: JSON.stringify(VERSION), - BUILD_TIMESTAMP, + BUILD_TIMESTAMP }; -if (defines.IS_STANDALONE === "false") - // If this is a local build (not standalone), optimise + +if (defines.IS_STANDALONE === false) + // If this is a local build (not standalone), optimize // for the specific platform we're on defines["process.platform"] = JSON.stringify(process.platform); @@ -46,7 +48,7 @@ const nodeCommonOpts = { platform: "node", target: ["esnext"], external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], - define: defines, + define: defines }; const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; @@ -73,13 +75,13 @@ const globNativesPlugin = { let i = 0; for (const dir of pluginDirs) { const dirPath = join("src", dir); - if (!await existsAsync(dirPath)) continue; + if (!await exists(dirPath)) continue; const plugins = await readdir(dirPath); for (const p of plugins) { const nativePath = join(dirPath, p, "native.ts"); const indexNativePath = join(dirPath, p, "native/index.ts"); - if (!(await existsAsync(nativePath)) && !(await existsAsync(indexNativePath))) + if (!(await exists(nativePath)) && !(await exists(indexNativePath))) continue; const nameParts = p.split("."); diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index b4c726064..bc15ccced 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -23,7 +23,7 @@ import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises import { join } from "path"; import Zip from "zip-local"; -import { BUILD_TIMESTAMP, commonOpts, globPlugins, isDev, VERSION } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION } from "./common.mjs"; /** * @type {esbuild.BuildOptions} @@ -33,22 +33,23 @@ const commonOptions = { entryPoints: ["browser/Vencord.ts"], globalName: "Vencord", format: "iife", - external: ["plugins", "git-hash", "/assets/*"], + external: ["~plugins", "~git-hash", "/assets/*"], plugins: [ globPlugins("web"), ...commonOpts.plugins, ], target: ["esnext"], define: { - IS_WEB: "true", - IS_EXTENSION: "false", - IS_STANDALONE: "true", - IS_DEV: JSON.stringify(isDev), - IS_DISCORD_DESKTOP: "false", - IS_VESKTOP: "false", - IS_UPDATER_DISABLED: "true", + IS_WEB: true, + IS_EXTENSION: false, + IS_STANDALONE: true, + IS_DEV, + IS_REPORTER, + IS_DISCORD_DESKTOP: false, + IS_VESKTOP: false, + IS_UPDATER_DISABLED: true, VERSION: JSON.stringify(VERSION), - BUILD_TIMESTAMP, + BUILD_TIMESTAMP } }; @@ -87,16 +88,16 @@ await Promise.all( esbuild.build({ ...commonOptions, outfile: "dist/browser.js", - footer: { js: "//# sourceURL=VencordWeb" }, + footer: { js: "//# sourceURL=VencordWeb" } }), esbuild.build({ ...commonOptions, outfile: "dist/extension.js", define: { ...commonOptions?.define, - IS_EXTENSION: "true", + IS_EXTENSION: true, }, - footer: { js: "//# sourceURL=VencordWeb" }, + footer: { js: "//# sourceURL=VencordWeb" } }), esbuild.build({ ...commonOptions, @@ -112,7 +113,7 @@ await Promise.all( footer: { // UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});" - }, + } }) ] ); @@ -165,7 +166,7 @@ async function buildExtension(target, files) { f.startsWith("manifest") ? "manifest.json" : f, content ]; - }))), + }))) }; await rm(target, { recursive: true, force: true }); @@ -192,14 +193,19 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content return appendFile("dist/Vencord.user.js", cssRuntime); }); -await Promise.all([ - appendCssRuntime, - buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]), - buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]), -]); +if (!process.argv.includes("--skip-extension")) { + await Promise.all([ + appendCssRuntime, + buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]), + buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]), + ]); -Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip"); -console.info("Packed Chromium Extension written to dist/extension-chrome.zip"); + Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip"); + console.info("Packed Chromium Extension written to dist/extension-chrome.zip"); -Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); -console.info("Packed Firefox Extension written to dist/extension-firefox.zip"); + Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); + console.info("Packed Firefox Extension written to dist/extension-firefox.zip"); + +} else { + await appendCssRuntime; +} diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 3b1473e1c..cdbb26eec 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -35,24 +35,26 @@ const PackageJSON = JSON.parse(readFileSync("package.json")); export const VERSION = PackageJSON.version; // https://reproducible-builds.org/docs/source-date-epoch/ export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now(); + export const watch = process.argv.includes("--watch"); -export const isDev = watch || process.argv.includes("--dev"); -export const isStandalone = JSON.stringify(process.argv.includes("--standalone")); -export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater")); +export const IS_DEV = watch || process.argv.includes("--dev"); +export const IS_REPORTER = process.argv.includes("--reporter"); +export const IS_STANDALONE = process.argv.includes("--standalone"); + +export const IS_UPDATER_DISABLED = process.argv.includes("--disable-updater"); export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); + export const banner = { js: ` // Vencord ${gitHash} -// Standalone: ${isStandalone} -// Platform: ${isStandalone === "false" ? process.platform : "Universal"} -// Updater disabled: ${updaterDisabled} +// Standalone: ${IS_STANDALONE} +// Platform: ${IS_STANDALONE === false ? process.platform : "Universal"} +// Updater Disabled: ${IS_UPDATER_DISABLED} `.trim() }; -const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs")); - -export function existsAsync(path) { - return access(path, FsConstants.F_OK) +export async function exists(path) { + return await access(path, FsConstants.F_OK) .then(() => true) .catch(() => false); } @@ -66,7 +68,7 @@ export const makeAllPackagesExternalPlugin = { setup(build) { const filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../" build.onResolve({ filter }, args => ({ path: args.path, external: true })); - }, + } }; /** @@ -89,14 +91,14 @@ export const globPlugins = kind => ({ let plugins = "\n"; let i = 0; for (const dir of pluginDirs) { - if (!await existsAsync(`./src/${dir}`)) continue; + if (!await exists(`./src/${dir}`)) continue; const files = await readdir(`./src/${dir}`); for (const file of files) { if (file.startsWith("_") || file.startsWith(".")) continue; if (file === "index.ts") continue; const target = getPluginTarget(file); - if (target) { + if (target && !IS_REPORTER) { if (target === "dev" && !watch) continue; if (target === "web" && kind === "discordDesktop") continue; if (target === "desktop" && kind === "web") continue; @@ -178,7 +180,7 @@ export const fileUrlPlugin = { build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => { const { searchParams } = new URL(uri); const base64 = searchParams.has("base64"); - const minify = isStandalone === "true" && searchParams.has("minify"); + const minify = IS_STANDALONE === true && searchParams.has("minify"); const noTrim = searchParams.get("trim") === "false"; const encoding = base64 ? "base64" : "utf-8"; diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 8bb87d812..8233f3e5d 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -205,7 +205,12 @@ page.on("console", async e => { } if (isVencord) { - const args = await Promise.all(e.args().map(a => a.jsonValue())); + let args: unknown[] = []; + try { + args = await Promise.all(e.args().map(a => a.jsonValue())); + } catch { + return; + } const [, tag, message] = args as Array; const cause = await maybeGetError(e.args()[3]); @@ -277,7 +282,7 @@ page.on("pageerror", e => console.error("[Page Error]", e)); await page.setBypassCSP(true); -async function runtime(token: string) { +async function reporterRuntime(token: string) { console.log("[PUP_DEBUG]", "Starting test..."); try { @@ -285,43 +290,7 @@ async function runtime(token: string) { Object.defineProperty(navigator, "languages", { get: function () { return ["en-US", "en"]; - }, - }); - - // Monkey patch Logger to not log with custom css - // @ts-ignore - const originalLog = Vencord.Util.Logger.prototype._log; - // @ts-ignore - Vencord.Util.Logger.prototype._log = function (level, levelColor, args) { - if (level === "warn" || level === "error") - return console[level]("[Vencord]", this.name + ":", ...args); - - return originalLog.call(this, level, levelColor, args); - }; - - // Force enable all plugins and patches - Vencord.Plugins.patches.length = 0; - Object.values(Vencord.Plugins.plugins).forEach(p => { - // Needs native server to run - if (p.name === "WebRichPresence (arRPC)") return; - - Vencord.Settings.plugins[p.name].enabled = true; - p.patches?.forEach(patch => { - patch.plugin = p.name; - delete patch.predicate; - delete patch.group; - - Vencord.Util.canonicalizeFind(patch); - if (!Array.isArray(patch.replacement)) { - patch.replacement = [patch.replacement]; - } - - patch.replacement.forEach(r => { - delete r.predicate; - }); - - Vencord.Plugins.patches.push(patch); - }); + } }); let wreq: typeof Vencord.Webpack.wreq; @@ -338,7 +307,7 @@ async function runtime(token: string) { // True if resolved, false otherwise const chunksSearchPromises = [] as Array<() => boolean>; - const LazyChunkRegex = canonicalizeMatch(/(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\)))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); async function searchAndLoadLazyChunks(factoryCode: string) { const lazyChunks = factoryCode.matchAll(LazyChunkRegex); @@ -348,8 +317,7 @@ async function runtime(token: string) { // the chunk containing the component const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT"); - await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIdsArray, rawChunkIdsSingle, entryPoint]) => { - const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; + await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : []; if (chunkIds.length === 0) { @@ -520,14 +488,14 @@ async function runtime(token: string) { } else if (method === "extractAndLoadChunks") { const [code, matcher] = args; - const module = Vencord.Webpack.findModuleFactory(...code); - if (module) result = module.toString().match(canonicalizeMatch(matcher)); + result = await Vencord.Webpack.extractAndLoadChunks(code, matcher); + if (result === false) result = null; } else { // @ts-ignore result = Vencord.Webpack[method](...args); } - if (result == null || ("$$vencordInternal" in result && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; @@ -545,9 +513,10 @@ async function runtime(token: string) { } await page.evaluateOnNewDocument(` - ${readFileSync("./dist/browser.js", "utf-8")} - - ;(${runtime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)}); + if (location.host.endsWith("discord.com")) { + ${readFileSync("./dist/browser.js", "utf-8")}; + (${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)}); + } `); await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login"); diff --git a/src/debug/Tracer.ts b/src/debug/Tracer.ts index 4337e0019..7d80f425c 100644 --- a/src/debug/Tracer.ts +++ b/src/debug/Tracer.ts @@ -18,14 +18,14 @@ import { Logger } from "@utils/Logger"; -if (IS_DEV) { +if (IS_DEV || IS_REPORTER) { var traces = {} as Record; var logger = new Logger("Tracer", "#FFD166"); } const noop = function () { }; -export const beginTrace = !IS_DEV ? noop : +export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop : function beginTrace(name: string, ...args: any[]) { if (name in traces) throw new Error(`Trace ${name} already exists!`); @@ -33,7 +33,7 @@ export const beginTrace = !IS_DEV ? noop : traces[name] = [performance.now(), args]; }; -export const finishTrace = !IS_DEV ? noop : function finishTrace(name: string) { +export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) { const end = performance.now(); const [start, args] = traces[name]; @@ -48,7 +48,7 @@ type TraceNameMapper = (...args: Parameters) => string; const noopTracer = (name: string, f: F, mapper?: TraceNameMapper) => f; -export const traceFunction = !IS_DEV +export const traceFunction = !(IS_DEV || IS_REPORTER) ? noopTracer : function traceFunction(name: string, f: F, mapper?: TraceNameMapper): F { return function (this: any, ...args: Parameters) { diff --git a/src/globals.d.ts b/src/globals.d.ts index 94b5f15e8..e20ca4b71 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -34,9 +34,10 @@ declare global { */ export var IS_WEB: boolean; export var IS_EXTENSION: boolean; - export var IS_DEV: boolean; export var IS_STANDALONE: boolean; export var IS_UPDATER_DISABLED: boolean; + export var IS_DEV: boolean; + export var IS_REPORTER: boolean; export var IS_DISCORD_DESKTOP: boolean; export var IS_VESKTOP: boolean; export var VERSION: string; diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index 423dce9b5..e41e8675e 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -19,7 +19,7 @@ import { popNotice, showNotice } from "@api/Notices"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { ReporterTestable } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; @@ -41,6 +41,7 @@ export default definePlugin({ name: "WebRichPresence (arRPC)", description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)", authors: [Devs.Ducko], + reporterTestable: ReporterTestable.None, settingsAboutComponent: () => ( <> diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 25fd563e4..a495907b2 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -21,7 +21,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { filters, findAll, search } from "@webpack"; const PORT = 8485; @@ -243,6 +243,7 @@ export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", authors: [Devs.Ven], + reporterTestable: ReporterTestable.None, settings, toolboxActions: { diff --git a/src/plugins/index.ts b/src/plugins/index.ts index a434b4a6f..53ab7983a 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -21,7 +21,7 @@ import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; import { Logger } from "@utils/Logger"; import { canonicalizeFind } from "@utils/patches"; -import { Patch, Plugin, StartAt } from "@utils/types"; +import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; import { FluxEvents } from "@webpack/types"; @@ -39,35 +39,68 @@ export const patches = [] as Patch[]; let enabledPluginsSubscribedFlux = false; const subscribedFluxEventsPlugins = new Set(); +const pluginsValues = Object.values(Plugins); const settings = Settings.plugins; export function isPluginEnabled(p: string) { return ( + IS_REPORTER || Plugins[p]?.required || Plugins[p]?.isDependency || settings[p]?.enabled ) ?? false; } -const pluginsValues = Object.values(Plugins); +export function addPatch(newPatch: Omit, pluginName: string) { + const patch = newPatch as Patch; + patch.plugin = pluginName; -// First roundtrip to mark and force enable dependencies (only for enabled plugins) + if (IS_REPORTER) { + delete patch.predicate; + delete patch.group; + } + + canonicalizeFind(patch); + if (!Array.isArray(patch.replacement)) { + patch.replacement = [patch.replacement]; + } + + if (IS_REPORTER) { + patch.replacement.forEach(r => { + delete r.predicate; + }); + } + + patches.push(patch); +} + +function isReporterTestable(p: Plugin, part: ReporterTestable) { + return p.reporterTestable == null + ? true + : (p.reporterTestable & part) === part; +} + +// First round-trip to mark and force enable dependencies // // FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only // goes for the top level and their children, but for now this works okay with the current API plugins -for (const p of pluginsValues) if (settings[p.name]?.enabled) { +for (const p of pluginsValues) if (isPluginEnabled(p.name)) { p.dependencies?.forEach(d => { const dep = Plugins[d]; - if (dep) { - settings[d].enabled = true; - dep.isDependency = true; - } - else { + + if (!dep) { const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`); - if (IS_DEV) + + if (IS_DEV) { throw error; + } + logger.warn(error); + return; } + + settings[d].enabled = true; + dep.isDependency = true; }); } @@ -82,23 +115,18 @@ for (const p of pluginsValues) { } if (p.patches && isPluginEnabled(p.name)) { - for (const patch of p.patches) { - patch.plugin = p.name; - - canonicalizeFind(patch); - if (!Array.isArray(patch.replacement)) { - patch.replacement = [patch.replacement]; + if (!IS_REPORTER || isReporterTestable(p, ReporterTestable.Patches)) { + for (const patch of p.patches) { + addPatch(patch, p.name); } - - patches.push(patch); } } } export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) { logger.info(`Starting plugins (stage ${target})`); - for (const name in Plugins) - if (isPluginEnabled(name)) { + for (const name in Plugins) { + if (isPluginEnabled(name) && (!IS_REPORTER || isReporterTestable(Plugins[name], ReporterTestable.Start))) { const p = Plugins[name]; const startAt = p.startAt ?? StartAt.WebpackReady; @@ -106,30 +134,38 @@ export const startAllPlugins = traceFunction("startAllPlugins", function startAl startPlugin(Plugins[name]); } + } }); export function startDependenciesRecursive(p: Plugin) { let restartNeeded = false; const failures: string[] = []; - p.dependencies?.forEach(dep => { - if (!Settings.plugins[dep].enabled) { - startDependenciesRecursive(Plugins[dep]); + + p.dependencies?.forEach(d => { + if (!settings[d].enabled) { + const dep = Plugins[d]; + startDependenciesRecursive(dep); + // If the plugin has patches, don't start the plugin, just enable it. - Settings.plugins[dep].enabled = true; - if (Plugins[dep].patches) { - logger.warn(`Enabling dependency ${dep} requires restart.`); + settings[d].enabled = true; + dep.isDependency = true; + + if (dep.patches) { + logger.warn(`Enabling dependency ${d} requires restart.`); restartNeeded = true; return; } - const result = startPlugin(Plugins[dep]); - if (!result) failures.push(dep); + + const result = startPlugin(dep); + if (!result) failures.push(d); } }); + return { restartNeeded, failures }; } export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof FluxDispatcher) { - if (p.flux && !subscribedFluxEventsPlugins.has(p.name)) { + if (p.flux && !subscribedFluxEventsPlugins.has(p.name) && (!IS_REPORTER || isReporterTestable(p, ReporterTestable.FluxEvents))) { subscribedFluxEventsPlugins.add(p.name); logger.debug("Subscribing to flux events of plugin", p.name); diff --git a/src/plugins/invisibleChat.desktop/index.tsx b/src/plugins/invisibleChat.desktop/index.tsx index 884ffafe3..01199d999 100644 --- a/src/plugins/invisibleChat.desktop/index.tsx +++ b/src/plugins/invisibleChat.desktop/index.tsx @@ -23,7 +23,7 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getStegCloak } from "@utils/dependencies"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common"; import { Message } from "discord-types/general"; @@ -105,6 +105,9 @@ export default definePlugin({ description: "Encrypt your Messages in a non-suspicious way!", authors: [Devs.SammCheese], dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI", "MessageUpdaterAPI"], + reporterTestable: ReporterTestable.Patches, + settings, + patches: [ { // Indicator @@ -121,7 +124,6 @@ export default definePlugin({ URL_REGEX: new RegExp( /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/, ), - settings, async start() { addButton("InvisibleChat", message => { return this.INV_REGEX.test(message?.content) diff --git a/src/plugins/shikiCodeblocks.desktop/index.ts b/src/plugins/shikiCodeblocks.desktop/index.ts index 27463195d..e6676a866 100644 --- a/src/plugins/shikiCodeblocks.desktop/index.ts +++ b/src/plugins/shikiCodeblocks.desktop/index.ts @@ -20,7 +20,7 @@ import "./shiki.css"; import { enableStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { ReporterTestable } from "@utils/types"; import previewExampleText from "file://previewExample.tsx"; import { shiki } from "./api/shiki"; @@ -34,6 +34,9 @@ export default definePlugin({ name: "ShikiCodeblocks", description: "Brings vscode-style codeblocks into Discord, powered by Shiki", authors: [Devs.Vap], + reporterTestable: ReporterTestable.Patches, + settings, + patches: [ { find: "codeBlock:{react(", @@ -66,7 +69,6 @@ export default definePlugin({ isPreview: true, tempSettings, }), - settings, // exports shiki, diff --git a/src/plugins/vcNarrator/index.tsx b/src/plugins/vcNarrator/index.tsx index ac629e749..6e8e4bbf5 100644 --- a/src/plugins/vcNarrator/index.tsx +++ b/src/plugins/vcNarrator/index.tsx @@ -22,7 +22,7 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { wordsToTitle } from "@utils/text"; -import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types"; +import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common"; @@ -155,6 +155,7 @@ export default definePlugin({ name: "VcNarrator", description: "Announces when users join, leave, or move voice channels via narrator", authors: [Devs.Ven], + reporterTestable: ReporterTestable.None, flux: { VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) { diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index 5251959f2..caa44a40c 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -8,7 +8,7 @@ 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 } from "@utils/types"; +import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general"; @@ -143,7 +143,9 @@ export default definePlugin({ description: "Forwards discord notifications to XSOverlay, for easy viewing in VR", authors: [Devs.Nyako], tags: ["vr", "notify"], + reporterTestable: ReporterTestable.None, settings, + flux: { CALL_UPDATE({ call }: { call: Call; }) { if (call?.ringing?.includes(UserStore.getCurrentUser().id) && settings.store.callNotifications) { diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 1ae4762d7..e222d71fb 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -32,6 +32,11 @@ export class Logger { constructor(public name: string, public color: string = "white") { } private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") { + if (IS_REPORTER && (level === "warn" || level === "error")) { + console[level]("[Vencord]", this.name + ":", ...args); + return; + } + console[level]( `%c Vencord %c %c ${this.name} ${customFmt}`, `background: ${levelColor}; color: black; font-weight: bold; border-radius: 5px;`, diff --git a/src/utils/dependencies.ts b/src/utils/dependencies.ts index f05900e14..d8c361e88 100644 --- a/src/utils/dependencies.ts +++ b/src/utils/dependencies.ts @@ -17,7 +17,6 @@ */ import { makeLazy } from "./lazy"; -import { EXTENSION_BASE_URL } from "./web-metadata"; /* Add dynamically loaded dependencies for plugins here. @@ -67,15 +66,6 @@ export interface ApngFrameData { playTime: number; } -// On web (extensions), use extension uri as basepath (load files from extension) -// On desktop (electron), load from cdn -export const rnnoiseDist = IS_EXTENSION - ? new URL("/third-party/rnnoise", EXTENSION_BASE_URL).toString() - : "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist"; -export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`; -export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`; - - // The below code is only used on the Desktop (electron) build of Vencord. // Browser (extension) builds do not contain these remote imports. diff --git a/src/utils/types.ts b/src/utils/types.ts index 6e1524196..fe19a1093 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -94,6 +94,10 @@ export interface PluginDef { * @default StartAt.WebpackReady */ startAt?: StartAt, + /** + * Which parts of the plugin can be tested by the reporter. Defaults to all parts + */ + reporterTestable?: number; /** * Optionally provide settings that the user can configure in the Plugins tab of settings. * @deprecated Use `settings` instead @@ -144,6 +148,13 @@ export const enum StartAt { WebpackReady = "WebpackReady" } +export const enum ReporterTestable { + None = 1 << 1, + Start = 1 << 2, + Patches = 1 << 3, + FluxEvents = 1 << 4 +} + export const enum OptionType { STRING, NUMBER, diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 9a89af362..8957c254b 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -22,7 +22,7 @@ import { LazyComponent } from "@utils/react"; import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]): T { - if (IS_DEV) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); + 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`); @@ -38,7 +38,7 @@ export function waitForComponent = React.Comp } export function waitForStore(name: string, cb: (v: any) => void) { - if (IS_DEV) lazyWebpackSearchHistory.push(["waitForStore", [name]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForStore", [name]]); waitFor(filters.byStoreName(name), cb, { isIndirect: true }); } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index f2e6e58a8..ec7218c64 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -264,7 +264,7 @@ export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "f * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); */ export function proxyLazyWebpack(factory: () => any, attempts?: number) { - if (IS_DEV) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]); return proxyLazy(factory, attempts); } @@ -278,7 +278,7 @@ export function proxyLazyWebpack(factory: () => any, attempts?: number) * @returns Result of factory function */ export function LazyComponentWebpack(factory: () => any, attempts?: number) { - if (IS_DEV) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]); return LazyComponent(factory, attempts); } @@ -287,7 +287,7 @@ export function LazyComponentWebpack(factory: () => any, * Find the first module that matches the filter, lazily */ export function findLazy(filter: FilterFn) { - if (IS_DEV) lazyWebpackSearchHistory.push(["find", [filter]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["find", [filter]]); return proxyLazy(() => find(filter)); } @@ -306,7 +306,7 @@ export function findByProps(...props: string[]) { * Find the first module that has the specified properties, lazily */ export function findByPropsLazy(...props: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByProps", props]); return proxyLazy(() => findByProps(...props)); } @@ -325,7 +325,7 @@ export function findByCode(...code: string[]) { * Find the first function that includes all the given code, lazily */ export function findByCodeLazy(...code: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByCode", code]); return proxyLazy(() => findByCode(...code)); } @@ -344,7 +344,7 @@ export function findStore(name: string) { * Find a store by its displayName, lazily */ export function findStoreLazy(name: string) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findStore", [name]]); return proxyLazy(() => findStore(name)); } @@ -363,7 +363,7 @@ export function findComponentByCode(...code: string[]) { * Finds the first component that matches the filter, lazily. */ export function findComponentLazy(filter: FilterFn) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponent", [filter]]); return LazyComponent(() => { @@ -378,7 +378,7 @@ export function findComponentLazy(filter: FilterFn) { * Finds the first component that includes all the given code, lazily */ export function findComponentByCodeLazy(...code: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findComponentByCode", code]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponentByCode", code]); return LazyComponent(() => { const res = find(filters.componentByCode(...code), { isIndirect: true }); @@ -392,7 +392,7 @@ export function findComponentByCodeLazy(...code: string[ * Finds the first component that is exported by the first prop name, lazily */ export function findExportedComponentLazy(...props: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findExportedComponent", props]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["findExportedComponent", props]); return LazyComponent(() => { const res = find(filters.byProps(...props), { isIndirect: true }); @@ -402,14 +402,14 @@ export function findExportedComponentLazy(...props: stri }); } -export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/; -export const ChunkIdsRegex = /\("(.+?)"\)/g; +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 lazy chunk loading found in the module factory - * @returns A promise that resolves when the chunks were loaded + * @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: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) { const module = findModuleFactory(...code); @@ -417,7 +417,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def const err = new Error("extractAndLoadChunks: Couldn't find module factory"); logger.warn(err, "Code:", code, "Matcher:", matcher); - return; + // 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)); @@ -429,10 +433,10 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def if (IS_DEV && !devToolsOpen) throw err; - return; + return false; } - const [, rawChunkIdsArray, rawChunkIdsSingle, entryPointId] = match; + 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); @@ -441,16 +445,27 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def if (IS_DEV && !devToolsOpen) throw err; - return; + return false; } - const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; if (rawChunkIds) { const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => 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(entryPointId); + return true; } /** @@ -458,11 +473,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def * * 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 lazy chunk loading found in the module factory - * @returns A function that returns a promise that resolves when the chunks were loaded, on first call + * @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: string[], matcher = DefaultExtractAndLoadChunksRegex) { - if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); + if (IS_REPORTER) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); return makeLazy(() => extractAndLoadChunks(code, matcher)); } @@ -472,7 +487,7 @@ export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtrac * then call the callback with the module as the first argument */ export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) { - if (IS_DEV && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]); + if (IS_REPORTER && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]); if (typeof filter === "string") filter = filters.byProps(filter); From 2b565fed252a0eb4809aba8f9948ff18989b44af Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 30 May 2024 18:32:09 -0300 Subject: [PATCH 19/77] Make vencord-debug usable everywhere if user is pluginDev --- src/plugins/_core/supportHelper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index c7377833a..d59d82afc 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -65,7 +65,7 @@ export default definePlugin({ commands: [{ name: "vencord-debug", description: "Send Vencord Debug info", - predicate: ctx => AllowedChannelIds.includes(ctx.channel.id), + predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id), async execute() { const { RELEASE_CHANNEL } = window.GLOBAL_ENV; From 7ccd07350644414f47560df16c49f27b762ad75b Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 31 May 2024 00:16:56 -0300 Subject: [PATCH 20/77] Fix ShowConnections & FriendsSince patches --- src/plugins/friendsSince/index.tsx | 2 +- src/plugins/showConnections/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index eb59fff25..58014f362 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -36,7 +36,7 @@ export default definePlugin({ { find: ".UserPopoutUpsellSource.PROFILE_PANEL,", replacement: { - match: /\i.default,\{userId:(\i)}\)/, + match: /\i.default,\{userId:([^,]+?)}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index d70c09315..a78e4c418 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -182,7 +182,7 @@ export default definePlugin({ } }, { - find: "\"Profile Panel: user cannot be undefined\"", + find: ".UserPopoutUpsellSource.PROFILE_PANEL,", replacement: { // createElement(Divider, {}), createElement(NoteComponent) match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/, From d07e4c71b560a7671401b4d3d2044d37f112f6da Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 31 May 2024 23:28:58 -0300 Subject: [PATCH 21/77] Make Reporter runnable in desktop --- package.json | 1 + scripts/generateReport.ts | 397 ++++++++----------------------------- src/Vencord.ts | 4 + src/api/DataStore/index.ts | 2 +- src/api/Settings.ts | 16 +- src/debug/runReporter.ts | 224 +++++++++++++++++++++ src/main/updater/index.ts | 2 +- src/utils/Logger.ts | 2 +- 8 files changed, 328 insertions(+), 320 deletions(-) create mode 100644 src/debug/runReporter.ts diff --git a/package.json b/package.json index 43ac36304..eb3e95a68 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", "buildWebStandalone": "pnpm buildWeb --standalone", "buildReporter": "pnpm buildWebStandalone --reporter --skip-extension", + "buildReporterDesktop": "pnpm build --reporter", "watch": "pnpm build --watch", "watchWeb": "pnpm buildWeb --watch", "generatePluginJson": "tsx scripts/generatePluginList.ts", diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 8233f3e5d..0fde48637 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +/* eslint-disable no-fallthrough */ + // eslint-disable-next-line spaced-comment /// // eslint-disable-next-line spaced-comment @@ -40,10 +42,11 @@ const browser = await pup.launch({ const page = await browser.newPage(); await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"); +await page.setBypassCSP(true); -function maybeGetError(handle: JSHandle) { - return (handle as JSHandle)?.getProperty("message") - .then(m => m.jsonValue()); +async function maybeGetError(handle: JSHandle): Promise { + return await (handle as JSHandle)?.getProperty("message") + .then(m => m?.jsonValue()); } const report = { @@ -59,6 +62,7 @@ const report = { error: string; }[], otherErrors: [] as string[], + ignoredErrors: [] as string[], badWebpackFinds: [] as string[] }; @@ -106,15 +110,6 @@ async function printReport() { console.log(); - const ignoredErrors = [] as string[]; - report.otherErrors = report.otherErrors.filter(e => { - if (IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))) { - ignoredErrors.push(e); - return false; - } - return true; - }); - console.log("## Discord Errors"); report.otherErrors.forEach(e => { console.log(`- ${toCodeBlock(e)}`); @@ -123,7 +118,7 @@ async function printReport() { console.log(); console.log("## Ignored Discord Errors"); - ignoredErrors.forEach(e => { + report.ignoredErrors.forEach(e => { console.log(`- ${toCodeBlock(e)}`); }); @@ -188,66 +183,6 @@ page.on("console", async e => { const level = e.type(); const rawArgs = e.args(); - const firstArg = await rawArgs[0]?.jsonValue(); - if (firstArg === "[PUPPETEER_TEST_DONE_SIGNAL]") { - await browser.close(); - await printReport(); - process.exit(); - } - - const isVencord = firstArg === "[Vencord]"; - const isDebug = firstArg === "[PUP_DEBUG]"; - const isWebpackFindFail = firstArg === "[PUP_WEBPACK_FIND_FAIL]"; - - if (isWebpackFindFail) { - process.exitCode = 1; - report.badWebpackFinds.push(await rawArgs[1].jsonValue() as string); - } - - if (isVencord) { - let args: unknown[] = []; - try { - args = await Promise.all(e.args().map(a => a.jsonValue())); - } catch { - return; - } - - const [, tag, message] = args as Array; - const cause = await maybeGetError(e.args()[3]); - - switch (tag) { - case "WebpackInterceptor:": - const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; - if (!patchFailMatch) break; - - process.exitCode = 1; - - const [, plugin, type, id, regex] = patchFailMatch; - report.badPatches.push({ - plugin, - type, - id, - match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), - error: cause - }); - - break; - case "PluginManager:": - const failedToStartMatch = message.match(/Failed to start (.+)/); - if (!failedToStartMatch) break; - - process.exitCode = 1; - - const [, name] = failedToStartMatch; - report.badStarts.push({ - plugin: name, - error: cause - }); - - break; - } - } - async function getText() { try { return await Promise.all( @@ -260,256 +195,98 @@ page.on("console", async e => { } } - if (isDebug) { - const text = await getText(); + const firstArg = await rawArgs[0]?.jsonValue(); - console.error(text); - if (text.includes("A fatal error occurred:")) { - process.exit(1); + const isVencord = firstArg === "[Vencord]"; + const isDebug = firstArg === "[PUP_DEBUG]"; + + outer: + if (isVencord) { + try { + var args = await Promise.all(e.args().map(a => a.jsonValue())); + } catch { + break outer; } + + const [, tag, message, otherMessage] = args as Array; + + switch (tag) { + case "WebpackInterceptor:": + const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; + if (!patchFailMatch) break; + + console.error(await getText()); + process.exitCode = 1; + + const [, plugin, type, id, regex] = patchFailMatch; + report.badPatches.push({ + plugin, + type, + id, + match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), + error: await maybeGetError(e.args()[3]) + }); + + break; + case "PluginManager:": + const failedToStartMatch = message.match(/Failed to start (.+)/); + if (!failedToStartMatch) break; + + console.error(await getText()); + process.exitCode = 1; + + const [, name] = failedToStartMatch; + report.badStarts.push({ + plugin: name, + error: await maybeGetError(e.args()[3]) ?? "Unknown error" + }); + + break; + case "Reporter:": + console.error(await getText()); + + switch (message) { + case "Webpack Find Fail:": + process.exitCode = 1; + report.badWebpackFinds.push(otherMessage); + break; + case "A fatal error occurred:": + process.exit(1); + case "Finished test": + await browser.close(); + await printReport(); + process.exit(); + } + } + } + + if (isDebug) { + console.error(await getText()); } else if (level === "error") { const text = await getText(); if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of") && !text.includes("Webpack")) { - console.error("[Unexpected Error]", text); - report.otherErrors.push(text); + if (IGNORED_DISCORD_ERRORS.some(regex => text.match(regex))) { + report.ignoredErrors.push(text); + } else { + console.error("[Unexpected Error]", text); + report.otherErrors.push(text); + } } } }); -page.on("error", e => console.error("[Error]", e)); -page.on("pageerror", e => console.error("[Page Error]", e)); - -await page.setBypassCSP(true); +page.on("error", e => console.error("[Error]", e.message)); +page.on("pageerror", e => console.error("[Page Error]", e.message)); async function reporterRuntime(token: string) { - console.log("[PUP_DEBUG]", "Starting test..."); - - try { - // Spoof languages to not be suspicious - Object.defineProperty(navigator, "languages", { - get: function () { - return ["en-US", "en"]; - } - }); - - let wreq: typeof Vencord.Webpack.wreq; - - const { canonicalizeMatch, Logger } = Vencord.Util; - - const validChunks = new Set(); - const invalidChunks = new Set(); - const deferredRequires = new Set(); - - let chunksSearchingResolve: (value: void | PromiseLike) => void; - const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); - - // True if resolved, false otherwise - const chunksSearchPromises = [] as Array<() => boolean>; - - const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); - - async function searchAndLoadLazyChunks(factoryCode: string) { - const lazyChunks = factoryCode.matchAll(LazyChunkRegex); - const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); - - // 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(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : []; - - if (chunkIds.length === 0) { - return; - } - - let invalidChunkGroup = false; - - for (const id of chunkIds) { - if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - if (isWasm) { - invalidChunks.add(id); - invalidChunkGroup = true; - continue; - } - - validChunks.add(id); - } - - if (!invalidChunkGroup) { - validChunkGroups.add([chunkIds, entryPoint]); - } - })); - - // Loads all found valid chunk groups - await Promise.all( - Array.from(validChunkGroups) - .map(([chunkIds]) => - Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) - ) - ); - - // 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); - } catch (err) { - console.error(err); - } - } - - // setImmediate to only check if all chunks were loaded after this function resolves - // We check if all chunks were loaded every time a factory is loaded - // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved - // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them - setTimeout(() => { - let allResolved = true; - - for (let i = 0; i < chunksSearchPromises.length; i++) { - const isResolved = chunksSearchPromises[i](); - - if (isResolved) { - // Remove finished promises to avoid having to iterate through a huge array everytime - chunksSearchPromises.splice(i--, 1); - } else { - allResolved = false; - } - } - - if (allResolved) chunksSearchingResolve(); - }, 0); + Vencord.Webpack.waitFor( + "loginToken", + m => { + console.log("[PUP_DEBUG]", "Logging in with token..."); + m.loginToken(token); } - - Vencord.Webpack.waitFor( - "loginToken", - m => { - console.log("[PUP_DEBUG]", "Logging in with token..."); - m.loginToken(token); - } - ); - - Vencord.Webpack.beforeInitListeners.add(async webpackRequire => { - console.log("[PUP_DEBUG]", "Loading all chunks..."); - - wreq = webpackRequire; - - Vencord.Webpack.factoryListeners.add(factory => { - let isResolved = false; - searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - }); - - // setImmediate to only search the initial factories after Discord initialized the app - // our beforeInitListeners are called before Discord initializes the app - setTimeout(() => { - for (const factoryId in wreq.m) { - let isResolved = false; - searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - } - }, 0); - }); - - await chunksSearchingDone; - - // Require deferred entry points - for (const deferredRequire of deferredRequires) { - wreq!(deferredRequire as any); - } - - // All chunks Discord has mapped to asset files, even if they are not used anymore - const allChunks = [] as string[]; - - // Matches "id" or id: - for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { - const id = currentMatch[1] ?? currentMatch[2]; - if (id == null) continue; - - allChunks.push(id); - } - - if (allChunks.length === 0) throw new Error("Failed to get all chunks"); - - // Chunks that are not loaded (not used) by Discord code anymore - const chunksLeft = allChunks.filter(id => { - return !(validChunks.has(id) || invalidChunks.has(id)); - }); - - await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - // Loads and requires a chunk - if (!isWasm) { - await wreq.e(id as any); - if (wreq.m[id]) wreq(id as any); - } - })); - - console.log("[PUP_DEBUG]", "Finished loading all chunks!"); - - for (const patch of Vencord.Plugins.patches) { - if (!patch.all) { - new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); - } - } - - for (const [searchType, args] of Vencord.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"; - } - if (searchType === "waitForStore") method = "findStore"; - - try { - let result: any; - - if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { - const [factory] = args; - result = factory(); - } else if (method === "extractAndLoadChunks") { - const [code, matcher] = args; - - result = await Vencord.Webpack.extractAndLoadChunks(code, matcher); - if (result === false) result = null; - } else { - // @ts-ignore - result = Vencord.Webpack[method](...args); - } - - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; - } 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 logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; - - console.log("[PUP_WEBPACK_FIND_FAIL]", logMessage); - } - } - - setTimeout(() => console.log("[PUPPETEER_TEST_DONE_SIGNAL]"), 1000); - } catch (e) { - console.log("[PUP_DEBUG]", "A fatal error occurred:", e); - } + ); } await page.evaluateOnNewDocument(` diff --git a/src/Vencord.ts b/src/Vencord.ts index 72541148e..c4c6d4705 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -42,6 +42,10 @@ import { checkForUpdates, update, UpdateLogger } from "./utils/updater"; import { onceReady } from "./webpack"; import { SettingsRouter } from "./webpack/common"; +if (IS_REPORTER) { + require("./debug/runReporter"); +} + async function syncSettings() { // pre-check for local shared settings if ( diff --git a/src/api/DataStore/index.ts b/src/api/DataStore/index.ts index 97f43edd6..47ae39dbd 100644 --- a/src/api/DataStore/index.ts +++ b/src/api/DataStore/index.ts @@ -49,7 +49,7 @@ let defaultGetStoreFunc: UseStore | undefined; function defaultGetStore() { if (!defaultGetStoreFunc) { - defaultGetStoreFunc = createStore("VencordData", "VencordStore"); + defaultGetStoreFunc = createStore(!IS_REPORTER ? "VencordData" : "VencordDataReporter", "VencordStore"); } return defaultGetStoreFunc; } diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 490e6ef7f..b94e6a3fd 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -106,7 +106,7 @@ const DefaultSettings: Settings = { } }; -const settings = VencordNative.settings.get(); +const settings = !IS_REPORTER ? VencordNative.settings.get() : {} as Settings; mergeDefaults(settings, DefaultSettings); const saveSettingsOnFrequentAction = debounce(async () => { @@ -156,12 +156,14 @@ export const SettingsStore = new SettingsStoreClass(settings, { } }); -SettingsStore.addGlobalChangeListener((_, path) => { - SettingsStore.plain.cloud.settingsSyncVersion = Date.now(); - localStorage.Vencord_settingsDirty = true; - saveSettingsOnFrequentAction(); - VencordNative.settings.set(SettingsStore.plain, path); -}); +if (!IS_REPORTER) { + SettingsStore.addGlobalChangeListener((_, path) => { + SettingsStore.plain.cloud.settingsSyncVersion = Date.now(); + localStorage.Vencord_settingsDirty = true; + saveSettingsOnFrequentAction(); + VencordNative.settings.set(SettingsStore.plain, path); + }); +} /** * Same as {@link Settings} but unproxied. You should treat this as readonly, diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts new file mode 100644 index 000000000..61c9f162b --- /dev/null +++ b/src/debug/runReporter.ts @@ -0,0 +1,224 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Logger } from "@utils/Logger"; +import { canonicalizeMatch } from "@utils/patches"; +import * as Webpack from "@webpack"; +import { wreq } from "@webpack"; +import { patches } from "plugins"; + +const ReporterLogger = new Logger("Reporter"); + +async function runReporter() { + ReporterLogger.log("Starting test..."); + + try { + const validChunks = new Set(); + const invalidChunks = new Set(); + const deferredRequires = new Set(); + + let chunksSearchingResolve: (value: void | PromiseLike) => void; + const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + + // True if resolved, false otherwise + const chunksSearchPromises = [] as Array<() => boolean>; + + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); + + async function searchAndLoadLazyChunks(factoryCode: string) { + const lazyChunks = factoryCode.matchAll(LazyChunkRegex); + const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); + + // 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 => m[1]) : []; + + if (chunkIds.length === 0) { + return; + } + + let invalidChunkGroup = false; + + for (const id of chunkIds) { + if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; + + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + if (isWasm && IS_WEB) { + invalidChunks.add(id); + invalidChunkGroup = true; + continue; + } + + validChunks.add(id); + } + + if (!invalidChunkGroup) { + validChunkGroups.add([chunkIds, entryPoint]); + } + })); + + // Loads all found valid chunk groups + await Promise.all( + Array.from(validChunkGroups) + .map(([chunkIds]) => + Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) + ) + ); + + // 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); + } catch (err) { + console.error(err); + } + } + + // setImmediate to only check if all chunks were loaded after this function resolves + // We check if all chunks were loaded every time a factory is loaded + // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved + // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them + setTimeout(() => { + let allResolved = true; + + for (let i = 0; i < chunksSearchPromises.length; i++) { + const isResolved = chunksSearchPromises[i](); + + if (isResolved) { + // Remove finished promises to avoid having to iterate through a huge array everytime + chunksSearchPromises.splice(i--, 1); + } else { + allResolved = false; + } + } + + if (allResolved) chunksSearchingResolve(); + }, 0); + } + + Webpack.beforeInitListeners.add(async () => { + ReporterLogger.log("Loading all chunks..."); + + Webpack.factoryListeners.add(factory => { + let isResolved = false; + searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); + + chunksSearchPromises.push(() => isResolved); + }); + + // setImmediate to only search the initial factories after Discord initialized the app + // our beforeInitListeners are called before Discord initializes the app + setTimeout(() => { + for (const factoryId in wreq.m) { + let isResolved = false; + searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); + + chunksSearchPromises.push(() => isResolved); + } + }, 0); + }); + + await chunksSearchingDone; + + // Require deferred entry points + for (const deferredRequire of deferredRequires) { + wreq!(deferredRequire as any); + } + + // All chunks Discord has mapped to asset files, even if they are not used anymore + const allChunks = [] as string[]; + + // Matches "id" or id: + for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { + const id = currentMatch[1] ?? currentMatch[2]; + if (id == null) continue; + + allChunks.push(id); + } + + if (allChunks.length === 0) throw new Error("Failed to get all chunks"); + + // Chunks that are not loaded (not used) by Discord code anymore + const chunksLeft = allChunks.filter(id => { + return !(validChunks.has(id) || invalidChunks.has(id)); + }); + + await Promise.all(chunksLeft.map(async id => { + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + // Loads and requires a chunk + if (!isWasm) { + await wreq.e(id as any); + if (wreq.m[id]) wreq(id as any); + } + })); + + ReporterLogger.log("Finished loading all chunks!"); + + for (const patch of patches) { + if (!patch.all) { + new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); + } + } + + 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"; + } + if (searchType === "waitForStore") method = "findStore"; + + try { + let result: any; + + if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { + const [factory] = args; + result = factory(); + } else if (method === "extractAndLoadChunks") { + const [code, matcher] = args; + + result = await Webpack.extractAndLoadChunks(code, matcher); + if (result === false) result = null; + } else { + // @ts-ignore + result = Webpack[method](...args); + } + + if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + } 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 logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; + + ReporterLogger.log("Webpack Find Fail:", logMessage); + } + } + + ReporterLogger.log("Finished test"); + } catch (e) { + ReporterLogger.log("A fatal error occurred:", e); + } +} + +runReporter(); diff --git a/src/main/updater/index.ts b/src/main/updater/index.ts index 32d5cd663..539b02a48 100644 --- a/src/main/updater/index.ts +++ b/src/main/updater/index.ts @@ -17,4 +17,4 @@ */ if (!IS_UPDATER_DISABLED) - import(IS_STANDALONE ? "./http" : "./git"); + require(IS_STANDALONE ? "./http" : "./git"); diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index e222d71fb..5296184d4 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -32,7 +32,7 @@ export class Logger { constructor(public name: string, public color: string = "white") { } private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") { - if (IS_REPORTER && (level === "warn" || level === "error")) { + if (IS_REPORTER) { console[level]("[Vencord]", this.name + ":", ...args); return; } From aa7eb770506f5456adae1f2becbd284d812fe3e2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 31 May 2024 23:41:38 -0300 Subject: [PATCH 22/77] ShowHiddenChannels: Fix patch --- src/plugins/secretRingTone/index.ts | 5 ++--- src/plugins/showHiddenChannels/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/secretRingTone/index.ts b/src/plugins/secretRingTone/index.ts index 9c3956a80..be804efc4 100644 --- a/src/plugins/secretRingTone/index.ts +++ b/src/plugins/secretRingTone/index.ts @@ -16,9 +16,8 @@ export default definePlugin({ { find: '"call_ringing_beat"', replacement: { - // FIXME Remove === alternative when it hits stable - match: /500(!==|===)\i\(\)\.random\(1,1e3\)/, - replace: (_, predicate) => predicate === "!==" ? "false" : "true", + match: /500!==\i\(\)\.random\(1,1e3\)/, + replace: "false", } }, ], diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index c120d72d8..35d56091a 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -73,8 +73,9 @@ export default definePlugin({ find: '"placeholder-channel-id"', replacement: [ // Remove the special logic for channels we don't have access to + // FIXME Remove variable matcher from threadsIds when it hits stable { - match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\i}}/, + match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:(?:\[\]|\i)}}/, replace: "" }, // Do not check for unreads when selecting the render level if the channel is hidden From a66138f157eaa0b3e67ed90ac91ccb3aa5e9644d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:30:18 -0300 Subject: [PATCH 23/77] Bump to 1.8.8 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index eb3e95a68..01fe3552b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.6", + "version": "1.8.8", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { @@ -107,6 +107,6 @@ }, "engines": { "node": ">=18", - "pnpm": ">=8" + "pnpm": ">=9" } } From 06824c273fc3eb245fff3567d2c3223340e3729d Mon Sep 17 00:00:00 2001 From: lewisakura Date: Mon, 3 Jun 2024 13:01:34 +0100 Subject: [PATCH 24/77] chore: security advisory link for blank issues [skip ci] (#2542) --- .github/ISSUE_TEMPLATE/blank.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/blank.yml b/.github/ISSUE_TEMPLATE/blank.yml index e8ca246de..2439d86a7 100644 --- a/.github/ISSUE_TEMPLATE/blank.yml +++ b/.github/ISSUE_TEMPLATE/blank.yml @@ -12,7 +12,8 @@ body: DO NOT USE THIS FORM, unless - you are a vencord contributor - you were given explicit permission to use this form by a moderator in our support server - - you are filing a security related report + + DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new) - type: textarea id: content From 8fd5d068da549c7e296c31438358061e310a9bea Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 1 Jun 2024 19:13:27 +0200 Subject: [PATCH 25/77] fix(css): brand-experiment is now brand-500 --- src/api/Notifications/NotificationComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx index caa4b64ef..d07143c45 100644 --- a/src/api/Notifications/NotificationComponent.tsx +++ b/src/api/Notifications/NotificationComponent.tsx @@ -113,7 +113,7 @@ export default ErrorBoundary.wrap(function NotificationComponent({ {timeout !== 0 && !permanent && (
)} From ed5ae2ba5c92fe24d73d2e7ed8dd4ca30e1a0faf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:39:01 -0300 Subject: [PATCH 26/77] Add shortcut for lazy loading chunks --- scripts/generateReport.ts | 13 +- src/api/Settings.ts | 2 +- src/debug/loadLazyChunks.ts | 167 ++++++++++++++++++++++++++ src/debug/runReporter.ts | 163 ++----------------------- src/plugins/consoleShortcuts/index.ts | 2 + src/plugins/index.ts | 1 - src/plugins/partyMode/index.ts | 3 +- src/utils/Logger.ts | 2 +- 8 files changed, 191 insertions(+), 162 deletions(-) create mode 100644 src/debug/loadLazyChunks.ts diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 0fde48637..cf4210779 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -241,17 +241,26 @@ page.on("console", async e => { error: await maybeGetError(e.args()[3]) ?? "Unknown error" }); + break; + case "LazyChunkLoader:": + console.error(await getText()); + + switch (message) { + case "A fatal error occurred:": + process.exit(1); + } + break; case "Reporter:": console.error(await getText()); switch (message) { + case "A fatal error occurred:": + process.exit(1); case "Webpack Find Fail:": process.exitCode = 1; report.badWebpackFinds.push(otherMessage); break; - case "A fatal error occurred:": - process.exit(1); case "Finished test": await browser.close(); await printReport(); diff --git a/src/api/Settings.ts b/src/api/Settings.ts index b94e6a3fd..70ba0bd4a 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -129,7 +129,7 @@ export const SettingsStore = new SettingsStoreClass(settings, { if (path === "plugins" && key in plugins) return target[key] = { - enabled: plugins[key].required ?? plugins[key].enabledByDefault ?? false + enabled: IS_REPORTER ?? plugins[key].required ?? plugins[key].enabledByDefault ?? false }; // Since the property is not set, check if this is a plugin's setting and if so, try to resolve diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts new file mode 100644 index 000000000..d8f84335c --- /dev/null +++ b/src/debug/loadLazyChunks.ts @@ -0,0 +1,167 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Logger } from "@utils/Logger"; +import { canonicalizeMatch } from "@utils/patches"; +import * as Webpack from "@webpack"; +import { wreq } from "@webpack"; + +const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); + +export async function loadLazyChunks() { + try { + LazyChunkLoaderLogger.log("Loading all chunks..."); + + const validChunks = new Set(); + const invalidChunks = new Set(); + const deferredRequires = new Set(); + + let chunksSearchingResolve: (value: void | PromiseLike) => void; + const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + + // True if resolved, false otherwise + const chunksSearchPromises = [] as Array<() => boolean>; + + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); + + async function searchAndLoadLazyChunks(factoryCode: string) { + const lazyChunks = factoryCode.matchAll(LazyChunkRegex); + const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); + + // 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 => m[1]) : []; + + if (chunkIds.length === 0) { + return; + } + + let invalidChunkGroup = false; + + for (const id of chunkIds) { + if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; + + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + if (isWasm && IS_WEB) { + invalidChunks.add(id); + invalidChunkGroup = true; + continue; + } + + validChunks.add(id); + } + + if (!invalidChunkGroup) { + validChunkGroups.add([chunkIds, entryPoint]); + } + })); + + // Loads all found valid chunk groups + await Promise.all( + Array.from(validChunkGroups) + .map(([chunkIds]) => + Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) + ) + ); + + // 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); + } catch (err) { + console.error(err); + } + } + + // setImmediate to only check if all chunks were loaded after this function resolves + // We check if all chunks were loaded every time a factory is loaded + // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved + // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them + setTimeout(() => { + let allResolved = true; + + for (let i = 0; i < chunksSearchPromises.length; i++) { + const isResolved = chunksSearchPromises[i](); + + if (isResolved) { + // Remove finished promises to avoid having to iterate through a huge array everytime + chunksSearchPromises.splice(i--, 1); + } else { + allResolved = false; + } + } + + if (allResolved) chunksSearchingResolve(); + }, 0); + } + + Webpack.factoryListeners.add(factory => { + 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); + + chunksSearchPromises.push(() => isResolved); + } + + await chunksSearchingDone; + + // Require deferred entry points + for (const deferredRequire of deferredRequires) { + wreq!(deferredRequire as any); + } + + // All chunks Discord has mapped to asset files, even if they are not used anymore + const allChunks = [] as string[]; + + // Matches "id" or id: + for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { + const id = currentMatch[1] ?? currentMatch[2]; + if (id == null) continue; + + allChunks.push(id); + } + + if (allChunks.length === 0) throw new Error("Failed to get all chunks"); + + // Chunks that are not loaded (not used) by Discord code anymore + const chunksLeft = allChunks.filter(id => { + return !(validChunks.has(id) || invalidChunks.has(id)); + }); + + await Promise.all(chunksLeft.map(async id => { + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + // Loads and requires a chunk + if (!isWasm) { + await wreq.e(id as any); + if (wreq.m[id]) wreq(id as any); + } + })); + + LazyChunkLoaderLogger.log("Finished loading all chunks!"); + } catch (e) { + LazyChunkLoaderLogger.log("A fatal error occurred:", e); + } +} diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 61c9f162b..6c7a2a03f 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -5,171 +5,22 @@ */ import { Logger } from "@utils/Logger"; -import { canonicalizeMatch } from "@utils/patches"; import * as Webpack from "@webpack"; -import { wreq } from "@webpack"; import { patches } from "plugins"; +import { loadLazyChunks } from "./loadLazyChunks"; + const ReporterLogger = new Logger("Reporter"); async function runReporter() { - ReporterLogger.log("Starting test..."); - try { - const validChunks = new Set(); - const invalidChunks = new Set(); - const deferredRequires = new Set(); + ReporterLogger.log("Starting test..."); - let chunksSearchingResolve: (value: void | PromiseLike) => void; - const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + let loadLazyChunksResolve: (value: void | PromiseLike) => void; + const loadLazyChunksDone = new Promise(r => loadLazyChunksResolve = r); - // True if resolved, false otherwise - const chunksSearchPromises = [] as Array<() => boolean>; - - const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); - - async function searchAndLoadLazyChunks(factoryCode: string) { - const lazyChunks = factoryCode.matchAll(LazyChunkRegex); - const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); - - // 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 => m[1]) : []; - - if (chunkIds.length === 0) { - return; - } - - let invalidChunkGroup = false; - - for (const id of chunkIds) { - if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - if (isWasm && IS_WEB) { - invalidChunks.add(id); - invalidChunkGroup = true; - continue; - } - - validChunks.add(id); - } - - if (!invalidChunkGroup) { - validChunkGroups.add([chunkIds, entryPoint]); - } - })); - - // Loads all found valid chunk groups - await Promise.all( - Array.from(validChunkGroups) - .map(([chunkIds]) => - Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) - ) - ); - - // 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); - } catch (err) { - console.error(err); - } - } - - // setImmediate to only check if all chunks were loaded after this function resolves - // We check if all chunks were loaded every time a factory is loaded - // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved - // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them - setTimeout(() => { - let allResolved = true; - - for (let i = 0; i < chunksSearchPromises.length; i++) { - const isResolved = chunksSearchPromises[i](); - - if (isResolved) { - // Remove finished promises to avoid having to iterate through a huge array everytime - chunksSearchPromises.splice(i--, 1); - } else { - allResolved = false; - } - } - - if (allResolved) chunksSearchingResolve(); - }, 0); - } - - Webpack.beforeInitListeners.add(async () => { - ReporterLogger.log("Loading all chunks..."); - - Webpack.factoryListeners.add(factory => { - let isResolved = false; - searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - }); - - // setImmediate to only search the initial factories after Discord initialized the app - // our beforeInitListeners are called before Discord initializes the app - setTimeout(() => { - for (const factoryId in wreq.m) { - let isResolved = false; - searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - } - }, 0); - }); - - await chunksSearchingDone; - - // Require deferred entry points - for (const deferredRequire of deferredRequires) { - wreq!(deferredRequire as any); - } - - // All chunks Discord has mapped to asset files, even if they are not used anymore - const allChunks = [] as string[]; - - // Matches "id" or id: - for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { - const id = currentMatch[1] ?? currentMatch[2]; - if (id == null) continue; - - allChunks.push(id); - } - - if (allChunks.length === 0) throw new Error("Failed to get all chunks"); - - // Chunks that are not loaded (not used) by Discord code anymore - const chunksLeft = allChunks.filter(id => { - return !(validChunks.has(id) || invalidChunks.has(id)); - }); - - await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - // Loads and requires a chunk - if (!isWasm) { - await wreq.e(id as any); - if (wreq.m[id]) wreq(id as any); - } - })); - - ReporterLogger.log("Finished loading all chunks!"); + Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve))); + await loadLazyChunksDone; for (const patch of patches) { if (!patch.all) { diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index ee86b5fcf..0a1323e75 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -25,6 +25,7 @@ import definePlugin, { PluginNative, StartAt } from "@utils/types"; import * as Webpack from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack"; import * as Common from "@webpack/common"; +import { loadLazyChunks } from "debug/loadLazyChunks"; import type { ComponentType } from "react"; const DESKTOP_ONLY = (f: string) => () => { @@ -82,6 +83,7 @@ function makeShortcuts() { wpsearch: search, wpex: extract, wpexs: (code: string) => extract(findModuleId(code)!), + loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); }, find, findAll: findAll, findByProps, diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 53ab7983a..32bfe7e97 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -44,7 +44,6 @@ const settings = Settings.plugins; export function isPluginEnabled(p: string) { return ( - IS_REPORTER || Plugins[p]?.required || Plugins[p]?.isDependency || settings[p]?.enabled diff --git a/src/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts index 56c19c02c..c40f2e3c7 100644 --- a/src/plugins/partyMode/index.ts +++ b/src/plugins/partyMode/index.ts @@ -18,7 +18,7 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; const enum Intensity { @@ -46,6 +46,7 @@ export default definePlugin({ name: "PartyMode", description: "Allows you to use party mode cause the party never ends ✨", authors: [Devs.UwUDev], + reporterTestable: ReporterTestable.None, settings, start() { diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 5296184d4..22a381360 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -32,7 +32,7 @@ export class Logger { constructor(public name: string, public color: string = "white") { } private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") { - if (IS_REPORTER) { + if (IS_REPORTER && IS_WEB) { console[level]("[Vencord]", this.name + ":", ...args); return; } From 23584393a9b2ebbdb2ff1212fb0dae1300702caf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:39:58 -0300 Subject: [PATCH 27/77] NoPendingCount: Fix for message requests --- src/plugins/noPendingCount/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugins/noPendingCount/index.ts b/src/plugins/noPendingCount/index.ts index 29458df9d..57a65f52c 100644 --- a/src/plugins/noPendingCount/index.ts +++ b/src/plugins/noPendingCount/index.ts @@ -62,6 +62,16 @@ export default definePlugin({ replace: "return 0;" } }, + // New message requests hook + { + find: "useNewMessageRequestsCount:", + predicate: () => settings.store.hideMessageRequestsCount, + replacement: { + match: /getNonChannelAckId\(\i\.\i\.MESSAGE_REQUESTS\).+?return /, + replace: "$&0;" + } + }, + // Old message requests hook { find: "getMessageRequestsCount(){", predicate: () => settings.store.hideMessageRequestsCount, From 9ab7b8b9c98b38a7502ee363c7f7c6d697808f72 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 5 Jun 2024 23:45:27 +0200 Subject: [PATCH 28/77] experiments: remove obsolete isStaff patch; rename ServerProfile -> ServerInfo --- src/plugins/experiments/index.tsx | 54 ++++--------------- .../GuildInfoModal.tsx} | 6 +-- src/plugins/serverInfo/README.md | 7 +++ .../{serverProfile => serverInfo}/index.tsx | 14 ++--- .../{serverProfile => serverInfo}/styles.css | 0 src/plugins/serverProfile/README.md | 7 --- 6 files changed, 28 insertions(+), 60 deletions(-) rename src/plugins/{serverProfile/GuildProfileModal.tsx => serverInfo/GuildInfoModal.tsx} (98%) create mode 100644 src/plugins/serverInfo/README.md rename src/plugins/{serverProfile => serverInfo}/index.tsx (65%) rename src/plugins/{serverProfile => serverInfo}/styles.css (100%) delete mode 100644 src/plugins/serverProfile/README.md diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 50b9521f9..626e06a99 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,31 +16,19 @@ * along with this program. If not, see . */ -import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Forms, React, UserStore } from "@webpack/common"; -import { User } from "discord-types/general"; +import { Forms, React } from "@webpack/common"; const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); -const settings = definePluginSettings({ - enableIsStaff: { - description: "Enable isStaff", - type: OptionType.BOOLEAN, - default: false, - restartNeeded: true - } -}); - export default definePlugin({ name: "Experiments", - description: "Enable Access to Experiments in Discord!", + description: "Enable Access to Experiments & other dev-only features in Discord!", authors: [ Devs.Megu, Devs.Ven, @@ -48,7 +36,6 @@ export default definePlugin({ Devs.BanTheNons, Devs.Nuckyz ], - settings, patches: [ { @@ -65,20 +52,6 @@ export default definePlugin({ replace: "$1=!0;" } }, - { - find: '"isStaff",', - predicate: () => settings.store.enableIsStaff, - replacement: [ - { - match: /(?<=>)(\i)\.hasFlag\((\i\.\i)\.STAFF\)(?=})/, - replace: (_, user, flags) => `$self.isStaff(${user},${flags})` - }, - { - match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/, - replace: "hasFreePremium(){return ", - } - ] - }, { find: 'H1,title:"Experiments"', replacement: { @@ -88,15 +61,6 @@ export default definePlugin({ } ], - isStaff(user: User, flags: any) { - try { - return UserStore.getCurrentUser()?.id === user.id || user.hasFlag(flags.STAFF); - } catch (err) { - new Logger("Experiments").error(err); - return user.hasFlag(flags.STAFF); - } - }, - settingsAboutComponent: () => { const isMacOS = navigator.platform.includes("Mac"); const modKey = isMacOS ? "cmd" : "ctrl"; @@ -105,14 +69,10 @@ export default definePlugin({ More Information - You can enable client DevTools{" "} + You can open Discord's DevTools via {" "} {modKey} +{" "} {altKey} +{" "} O{" "} - after enabling isStaff below - - - and then toggling Enable DevTools in the Developer Options tab in settings. ); @@ -128,6 +88,12 @@ export default definePlugin({ Only use experiments if you know what you're doing. Vencord is not responsible for any damage caused by enabling experiments. + + If you don't know what an experiment does, ignore it. Do not ask us what experiments do either, we probably don't know. + + + + No, you cannot use server-side features like checking the "Send to Client" box. ), { noop: true }) diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx similarity index 98% rename from src/plugins/serverProfile/GuildProfileModal.tsx rename to src/plugins/serverInfo/GuildInfoModal.tsx index 8e6f60518..bed520b67 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -20,10 +20,10 @@ const FriendRow = findExportedComponentLazy("FriendRow"); const cl = classNameFactory("vc-gp-"); -export function openGuildProfileModal(guild: Guild) { +export function openGuildInfoModal(guild: Guild) { openModal(props => - + ); } @@ -53,7 +53,7 @@ function renderTimestamp(timestamp: number) { ); } -function GuildProfileModal({ guild }: GuildProps) { +function GuildInfoModal({ guild }: GuildProps) { const [friendCount, setFriendCount] = useState(); const [blockedCount, setBlockedCount] = useState(); diff --git a/src/plugins/serverInfo/README.md b/src/plugins/serverInfo/README.md new file mode 100644 index 000000000..98c9013e0 --- /dev/null +++ b/src/plugins/serverInfo/README.md @@ -0,0 +1,7 @@ +# ServerInfo + +Allows you to view info about servers and see friends and blocked users + +![](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580) +![](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630) +![Available as "Server Profile" option in the server context menu](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54) diff --git a/src/plugins/serverProfile/index.tsx b/src/plugins/serverInfo/index.tsx similarity index 65% rename from src/plugins/serverProfile/index.tsx rename to src/plugins/serverInfo/index.tsx index 9d495c9d3..be3172f01 100644 --- a/src/plugins/serverProfile/index.tsx +++ b/src/plugins/serverInfo/index.tsx @@ -5,30 +5,32 @@ */ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { Menu } from "@webpack/common"; import { Guild } from "discord-types/general"; -import { openGuildProfileModal } from "./GuildProfileModal"; +import { openGuildInfoModal } from "./GuildInfoModal"; const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { const group = findGroupChildrenByChildId("privacy", children); group?.push( openGuildProfileModal(guild)} + action={() => openGuildInfoModal(guild)} /> ); }; +migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao export default definePlugin({ - name: "ServerProfile", - description: "Allows you to view info about a server by right clicking it in the server list", + name: "ServerInfo", + description: "Allows you to view info about a server", authors: [Devs.Ven, Devs.Nuckyz], - tags: ["guild", "info"], + tags: ["guild", "info", "ServerProfile"], contextMenus: { "guild-context": Patch, "guild-header-popout": Patch diff --git a/src/plugins/serverProfile/styles.css b/src/plugins/serverInfo/styles.css similarity index 100% rename from src/plugins/serverProfile/styles.css rename to src/plugins/serverInfo/styles.css diff --git a/src/plugins/serverProfile/README.md b/src/plugins/serverProfile/README.md deleted file mode 100644 index 9da70e74e..000000000 --- a/src/plugins/serverProfile/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# ServerProfile - -Allows you to view info about servers and see friends and blocked users - -![image](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580) -![image](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630) -![image](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54) From 0aa7bef9fa86258f0b89afbc7a3e9a979d460044 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Thu, 6 Jun 2024 07:19:53 +0800 Subject: [PATCH 29/77] new plugin AppleMusicRichPresence (#2455) Co-authored-by: Vendicated --- .vscode/settings.json | 2 + src/components/PluginSettings/index.tsx | 3 +- src/plugins/appleMusic.desktop/README.md | 9 + src/plugins/appleMusic.desktop/index.tsx | 253 +++++++++++++++++++++++ src/plugins/appleMusic.desktop/native.ts | 120 +++++++++++ src/utils/types.ts | 4 + 6 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 src/plugins/appleMusic.desktop/README.md create mode 100644 src/plugins/appleMusic.desktop/index.tsx create mode 100644 src/plugins/appleMusic.desktop/native.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index fa543b38c..8be0795f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,6 +14,8 @@ "typescript.preferences.quoteStyle": "double", "javascript.preferences.quoteStyle": "double", + "eslint.experimental.useFlatConfig": false, + "gitlens.remotes": [ { "domain": "codeberg.org", diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index e6b2cf1fb..9c26a9cf1 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -261,8 +261,9 @@ export default function PluginSettings() { plugins = []; requiredPlugins = []; + const showApi = searchValue.value === "API"; for (const p of sortedPlugins) { - if (!p.options && p.name.endsWith("API") && searchValue.value !== "API") + if (p.hidden || (!p.options && p.name.endsWith("API") && !showApi)) continue; if (!pluginFilter(p)) continue; diff --git a/src/plugins/appleMusic.desktop/README.md b/src/plugins/appleMusic.desktop/README.md new file mode 100644 index 000000000..52ab93bfd --- /dev/null +++ b/src/plugins/appleMusic.desktop/README.md @@ -0,0 +1,9 @@ +# AppleMusicRichPresence + +This plugin enables Discord rich presence for your Apple Music! (This only works on macOS with the Music app.) + +![Screenshot of the activity in Discord](https://github.com/Vendicated/Vencord/assets/70191398/1f811090-ab5f-4060-a9ee-d0ac44a1d3c0) + +## Configuration + +For the customizable activity format strings, you can use several special strings to include track data in activities! `{name}` is replaced with the track name; `{artist}` is replaced with the artist(s)' name(s); and `{album}` is replaced with the album name. diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx new file mode 100644 index 000000000..16591028d --- /dev/null +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -0,0 +1,253 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; + +const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; + +interface ActivityAssets { + large_image?: string; + large_text?: string; + small_image?: string; + small_text?: string; +} + +interface ActivityButton { + label: string; + url: string; +} + +interface Activity { + state: string; + details?: string; + timestamps?: { + start?: number; + end?: number; + }; + assets?: ActivityAssets; + buttons?: Array; + name: string; + application_id: string; + metadata?: { + button_urls?: Array; + }; + type: number; + flags: number; +} + +const enum ActivityType { + PLAYING = 0, + LISTENING = 2, +} + +const enum ActivityFlag { + INSTANCE = 1 << 0, +} + +export interface TrackData { + name: string; + album: string; + artist: string; + + appleMusicLink?: string; + songLink?: string; + + albumArtwork?: string; + artistArtwork?: string; + + playerPosition: number; + duration: number; +} + +const enum AssetImageType { + Album = "Album", + Artist = "Artist", +} + +const applicationId = "1239490006054207550"; + +function setActivity(activity: Activity | null) { + FluxDispatcher.dispatch({ + type: "LOCAL_ACTIVITY_UPDATE", + activity, + socketId: "AppleMusic", + }); +} + +const settings = definePluginSettings({ + activityType: { + type: OptionType.SELECT, + description: "Which type of activity", + options: [ + { label: "Playing", value: ActivityType.PLAYING, default: true }, + { label: "Listening", value: ActivityType.LISTENING } + ], + }, + refreshInterval: { + type: OptionType.SLIDER, + description: "The interval between activity refreshes (seconds)", + markers: [1, 2, 2.5, 3, 5, 10, 15], + default: 5, + restartNeeded: true, + }, + enableTimestamps: { + type: OptionType.BOOLEAN, + description: "Whether or not to enable timestamps", + default: true, + }, + enableButtons: { + type: OptionType.BOOLEAN, + description: "Whether or not to enable buttons", + default: true, + }, + nameString: { + type: OptionType.STRING, + description: "Activity name format string", + default: "Apple Music" + }, + detailsString: { + type: OptionType.STRING, + description: "Activity details format string", + default: "{name}" + }, + stateString: { + type: OptionType.STRING, + description: "Activity state format string", + default: "{artist}" + }, + largeImageType: { + type: OptionType.SELECT, + description: "Activity assets large image type", + options: [ + { label: "Album artwork", value: AssetImageType.Album, default: true }, + { label: "Artist artwork", value: AssetImageType.Artist } + ], + }, + largeTextString: { + type: OptionType.STRING, + description: "Activity assets large text format string", + default: "{album}" + }, + smallImageType: { + type: OptionType.SELECT, + description: "Activity assets small image type", + options: [ + { label: "Album artwork", value: AssetImageType.Album }, + { label: "Artist artwork", value: AssetImageType.Artist, default: true } + ], + }, + smallTextString: { + type: OptionType.STRING, + description: "Activity assets small text format string", + default: "{artist}" + }, +}); + +function customFormat(formatStr: string, data: TrackData) { + return formatStr + .replaceAll("{name}", data.name) + .replaceAll("{album}", data.album) + .replaceAll("{artist}", data.artist); +} + +function getImageAsset(type: AssetImageType, data: TrackData) { + const source = type === AssetImageType.Album + ? data.albumArtwork + : data.artistArtwork; + + if (!source) return undefined; + + return ApplicationAssetUtils.fetchAssetIds(applicationId, [source]).then(ids => ids[0]); +} + +export default definePlugin({ + name: "AppleMusicRichPresence", + description: "Discord rich presence for your Apple Music!", + authors: [Devs.RyanCaoDev], + hidden: !navigator.platform.startsWith("Mac"), + + settingsAboutComponent() { + return <> + + For the customizable activity format strings, you can use several special strings to include track data in activities!{" "} + {"{name}"} is replaced with the track name; {"{artist}"} is replaced with the artist(s)' name(s); and {"{album}"} is replaced with the album name. + + ; + }, + + settings, + + start() { + this.updatePresence(); + this.updateInterval = setInterval(() => { this.updatePresence(); }, settings.store.refreshInterval * 1000); + }, + + stop() { + clearInterval(this.updateInterval); + FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null }); + }, + + updatePresence() { + this.getActivity().then(activity => { setActivity(activity); }); + }, + + async getActivity(): Promise { + const trackData = await Native.fetchTrackData(); + if (!trackData) return null; + + const [largeImageAsset, smallImageAsset] = await Promise.all([ + getImageAsset(settings.store.largeImageType, trackData), + getImageAsset(settings.store.smallImageType, trackData) + ]); + + const assets: ActivityAssets = { + large_image: largeImageAsset, + large_text: customFormat(settings.store.largeTextString, trackData), + small_image: smallImageAsset, + small_text: customFormat(settings.store.smallTextString, trackData), + }; + + const buttons: ActivityButton[] = []; + + if (settings.store.enableButtons) { + if (trackData.appleMusicLink) + buttons.push({ + label: "Listen on Apple Music", + url: trackData.appleMusicLink, + }); + + if (trackData.songLink) + buttons.push({ + label: "View on SongLink", + url: trackData.songLink, + }); + } + + return { + application_id: applicationId, + + name: customFormat(settings.store.nameString, trackData), + details: customFormat(settings.store.detailsString, trackData), + state: customFormat(settings.store.stateString, trackData), + + timestamps: (settings.store.enableTimestamps ? { + start: Date.now() - (trackData.playerPosition * 1000), + end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000), + } : undefined), + + assets, + + buttons: buttons.length ? buttons.map(v => v.label) : undefined, + metadata: { button_urls: buttons.map(v => v.url) || undefined, }, + + type: settings.store.activityType, + flags: ActivityFlag.INSTANCE, + }; + } +}); diff --git a/src/plugins/appleMusic.desktop/native.ts b/src/plugins/appleMusic.desktop/native.ts new file mode 100644 index 000000000..2eb2a0757 --- /dev/null +++ b/src/plugins/appleMusic.desktop/native.ts @@ -0,0 +1,120 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { execFile } from "child_process"; +import { promisify } from "util"; + +import type { TrackData } from "."; + +const exec = promisify(execFile); + +// function exec(file: string, args: string[] = []) { +// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => { +// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] }); + +// let stdout: string | null = null; +// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; }); +// let stderr: string | null = null; +// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; }); + +// process.on("exit", code => { resolve({ code, stdout, stderr }); }); +// process.on("error", err => reject(err)); +// }); +// } + +async function applescript(cmds: string[]) { + const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat()); + return stdout; +} + +function makeSearchUrl(type: string, query: string) { + const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json"); + url.searchParams.set("types", type); + url.searchParams.set("limit", "1"); + url.searchParams.set("term", query); + return url; +} + +const requestOptions: RequestInit = { + headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" }, +}; + +interface RemoteData { + appleMusicLink?: string, + songLink?: string, + albumArtwork?: string, + artistArtwork?: string; +} + +let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null; + +async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) { + if (id === cachedRemoteData?.id) { + if ("data" in cachedRemoteData) return cachedRemoteData.data; + if ("failures" in cachedRemoteData && cachedRemoteData.failures >= 5) return null; + } + + try { + const [songData, artistData] = await Promise.all([ + fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()), + fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json()) + ]); + + const appleMusicLink = songData?.songs?.data[0]?.attributes.url; + const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined; + + const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512"); + const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512"); + + cachedRemoteData = { + id, + data: { appleMusicLink, songLink, albumArtwork, artistArtwork } + }; + return cachedRemoteData.data; + } catch (e) { + console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e); + cachedRemoteData = { + id, + failures: (id === cachedRemoteData?.id && "failures" in cachedRemoteData ? cachedRemoteData.failures : 0) + 1 + }; + return null; + } +} + +export async function fetchTrackData(): Promise { + try { + await exec("pgrep", ["^Music$"]); + } catch (error) { + return null; + } + + const playerState = await applescript(['tell application "Music"', "get player state", "end tell"]) + .then(out => out.trim()); + if (playerState !== "playing") return null; + + const playerPosition = await applescript(['tell application "Music"', "get player position", "end tell"]) + .then(text => Number.parseFloat(text.trim())); + + const stdout = await applescript([ + 'set output to ""', + 'tell application "Music"', + "set t_id to database id of current track", + "set t_name to name of current track", + "set t_album to album of current track", + "set t_artist to artist of current track", + "set t_duration to duration of current track", + 'set output to "" & t_id & "\\n" & t_name & "\\n" & t_album & "\\n" & t_artist & "\\n" & t_duration', + "end tell", + "return output" + ]); + + const [id, name, album, artist, durationStr] = stdout.split("\n").filter(k => !!k); + const duration = Number.parseFloat(durationStr); + + const remoteData = await fetchRemoteData({ id, name, artist, album }); + + return { name, album, artist, playerPosition, duration, ...remoteData }; +} diff --git a/src/utils/types.ts b/src/utils/types.ts index fe19a1093..2fa4a826e 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -85,6 +85,10 @@ export interface PluginDef { * Whether this plugin is required and forcefully enabled */ required?: boolean; + /** + * Whether this plugin should be hidden from the user + */ + hidden?: boolean; /** * Whether this plugin should be enabled by default, but can be disabled */ From e5e8b9ba0143608752577dc277ab5d340c99382a Mon Sep 17 00:00:00 2001 From: vishnyanetchereshnya <151846235+vishnyanetchereshnya@users.noreply.github.com> Date: Thu, 6 Jun 2024 02:40:02 +0300 Subject: [PATCH 30/77] new plugin CopyEmojiMarkdown ~ more easily copy emoji formatting (#2266) Co-authored-by: Happy enderman <66224387+happyendermangit@users.noreply.github.com> Co-authored-by: vee --- src/plugins/copyEmojiMarkdown/README.md | 5 ++ src/plugins/copyEmojiMarkdown/index.tsx | 75 +++++++++++++++++++++++++ src/utils/constants.ts | 8 +++ 3 files changed, 88 insertions(+) create mode 100644 src/plugins/copyEmojiMarkdown/README.md create mode 100644 src/plugins/copyEmojiMarkdown/index.tsx diff --git a/src/plugins/copyEmojiMarkdown/README.md b/src/plugins/copyEmojiMarkdown/README.md new file mode 100644 index 000000000..9e62e6635 --- /dev/null +++ b/src/plugins/copyEmojiMarkdown/README.md @@ -0,0 +1,5 @@ +# CopyEmojiMarkdown + +Allows you to copy emojis as formatted string. Custom emojis will be copied as `<:trolley:1024751352028602449>`, default emojis as `🛒` + +![](https://github.com/Vendicated/Vencord/assets/45497981/417f345a-7031-4fe7-8e42-e238870cd547) diff --git a/src/plugins/copyEmojiMarkdown/index.tsx b/src/plugins/copyEmojiMarkdown/index.tsx new file mode 100644 index 000000000..a9c018a91 --- /dev/null +++ b/src/plugins/copyEmojiMarkdown/index.tsx @@ -0,0 +1,75 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +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 { Menu } from "@webpack/common"; + +const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate"); + +interface Emoji { + type: string; + id: string; + name: string; +} + +interface Target { + dataset: Emoji; + firstChild: HTMLImageElement; +} + +function getEmojiMarkdown(target: Target, copyUnicode: boolean): string { + const { id: emojiId, name: emojiName } = target.dataset; + + if (!emojiId) { + return copyUnicode + ? convertNameToSurrogate(emojiName) + : `:${emojiName}:`; + } + + const extension = target?.firstChild.src.match( + /https:\/\/cdn\.discordapp\.com\/emojis\/\d+\.(\w+)/ + )?.[1]; + + return `<${extension === "gif" ? "a" : ""}:${emojiName.replace(/~\d+$/, "")}:${emojiId}>`; +} + +const settings = definePluginSettings({ + copyUnicode: { + type: OptionType.BOOLEAN, + description: "Copy the raw unicode character instead of :name: for default emojis (👽)", + default: true, + }, +}); + +export default definePlugin({ + name: "CopyEmojiMarkdown", + description: "Allows you to copy emojis as formatted string (<:blobcatcozy:1026533070955872337>)", + authors: [Devs.HappyEnderman, Devs.Vishnya], + settings, + + contextMenus: { + "expression-picker"(children, { target }: { target: Target }) { + if (target.dataset.type !== "emoji") return; + + children.push( + { + copyWithToast( + getEmojiMarkdown(target, settings.store.copyUnicode), + "Success! Copied emoji markdown." + ); + }} + /> + ); + }, + }, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 4e3422526..7f172395b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -442,6 +442,14 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Elvyra", id: 708275751816003615n, }, + HappyEnderman: { + name: "Happy enderman", + id: 1083437693347827764n + }, + Vishnya: { + name: "Vishnya", + id: 282541644484575233n + }, Inbestigator: { name: "Inbestigator", id: 761777382041714690n From b88be8014e3fd2e76df7ac6a2be63ce821d2071a Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 6 Jun 2024 02:55:18 +0200 Subject: [PATCH 31/77] experiments: change toolbar help button -> dev menu --- src/plugins/experiments/hideBugReport.css | 3 +++ src/plugins/experiments/index.tsx | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/plugins/experiments/hideBugReport.css diff --git a/src/plugins/experiments/hideBugReport.css b/src/plugins/experiments/hideBugReport.css new file mode 100644 index 000000000..ff78555d7 --- /dev/null +++ b/src/plugins/experiments/hideBugReport.css @@ -0,0 +1,3 @@ +#staff-help-popout-staff-help-bug-reporter { + display: none; +} diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 626e06a99..cf4dbf249 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; @@ -24,6 +25,8 @@ import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; +import hideBugReport from "./hideBugReport.css?managed"; + const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); export default definePlugin({ @@ -58,9 +61,20 @@ export default definePlugin({ match: 'title:"Experiments",children:[', replace: "$&$self.WarningCard()," } + }, + // change top right chat toolbar button from the help one to the dev one + { + find: "toolbar:function", + replacement: { + match: /\i\.isStaff\(\)/, + replace: "true" + } } ], + start: () => enableStyle(hideBugReport), + stop: () => disableStyle(hideBugReport), + settingsAboutComponent: () => { const isMacOS = navigator.platform.includes("Mac"); const modKey = isMacOS ? "cmd" : "ctrl"; From 0dac08c17df154b6e2f6d3d79ab82fd79aaada1f Mon Sep 17 00:00:00 2001 From: nyx <60797172+verticalsync@users.noreply.github.com> Date: Thu, 6 Jun 2024 04:39:18 +0300 Subject: [PATCH 32/77] PlatformIndicators: fix embedded (console) devices (#2546) Co-authored-by: Vendicated Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/plugins/platformIndicators/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 9fae9adfa..ea2ae125c 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -51,14 +51,17 @@ const Icons = { desktop: Icon("M4 2.5c-1.103 0-2 .897-2 2v11c0 1.104.897 2 2 2h7v2H7v2h10v-2h-4v-2h7c1.103 0 2-.896 2-2v-11c0-1.103-.897-2-2-2H4Zm16 2v9H4v-9h16Z"), web: Icon("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93Zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39Z"), mobile: Icon("M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z", { viewBox: "0 0 1000 1500", height: 17, width: 17 }), - console: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }), + embedded: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }), }; type Platform = keyof typeof Icons; const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { - const tooltip = platform[0].toUpperCase() + platform.slice(1); + const tooltip = platform === "embedded" + ? "Console" + : platform[0].toUpperCase() + platform.slice(1); + const Icon = Icons[platform] ?? Icons.desktop; return ; From 67b709a7962afd370a2708e4e3252a58841a28c5 Mon Sep 17 00:00:00 2001 From: nekohaxx <151578517+nekohaxx@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:01:44 +0800 Subject: [PATCH 33/77] new plugin NoOnboardingDelay: skip long onboarding animations (#2533) Co-authored-by: Vendicated --- src/plugins/noOnboardingDelay/index.ts | 35 ++++++++++++++++++++++++++ src/utils/constants.ts | 4 +++ 2 files changed, 39 insertions(+) create mode 100644 src/plugins/noOnboardingDelay/index.ts diff --git a/src/plugins/noOnboardingDelay/index.ts b/src/plugins/noOnboardingDelay/index.ts new file mode 100644 index 000000000..6211e97c2 --- /dev/null +++ b/src/plugins/noOnboardingDelay/index.ts @@ -0,0 +1,35 @@ +/* + * 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 { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NoOnboardingDelay", + description: "Skips the slow and annoying onboarding delay", + authors: [Devs.nekohaxx], + patches: [ + { + find: "Messages.ONBOARDING_COVER_WELCOME_SUBTITLE", + replacement: { + match: "3e3", + replace: "0" + }, + }, + ], +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7f172395b..ff754d5c2 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -526,6 +526,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "verticalsync", id: 328165170536775680n }, + nekohaxx: { + name: "nekohaxx", + id: 1176270221628153886n + } } satisfies Record); // iife so #__PURE__ works correctly From 9cafe8084cf4850db777c6389dd403a535751da5 Mon Sep 17 00:00:00 2001 From: notsu Date: Thu, 6 Jun 2024 07:17:47 +0500 Subject: [PATCH 34/77] SpotifyControls: fix no artists on local files (#2543) Co-authored-by: vee --- src/plugins/spotifyControls/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index 06595892f..f7972aa36 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -77,6 +77,13 @@ export default definePlugin({ match: /repeat:"off"!==(.{1,3}),/, replace: "actual_repeat:$1,$&" } + }, + { + find: "artists.filter", + replacement: { + match: /\(0,(\i)\.isNotNullish\)\((\i)\.id\)&&/, + replace: "" + } } ], From 5976d52cbc24785a02588833decc859c93294ade Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 6 Jun 2024 10:05:53 +0700 Subject: [PATCH 35/77] viewIcons: support new simplified profile (#2535) Co-authored-by: Sqaaakoi --- src/plugins/viewIcons/index.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index 09254d511..a94689689 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -184,16 +184,16 @@ export default definePlugin({ patches: [ // Profiles Modal pfp - { - find: "User Profile Modal - Context Menu", + ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ + find, replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, replace: "{src:$1,onClick:()=>$self.openImage($1)" } - }, + })), // Banners - { - find: ".NITRO_BANNER,", + ...[".NITRO_BANNER,", /overrideBannerSrc:\i,overrideBannerWidth:/].map(find => ({ + find, replacement: { // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/, @@ -201,7 +201,7 @@ export default definePlugin({ // onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0, 'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,' } - }, + })), // User DMs "User Profile" popup in the right { find: ".avatarPositionPanel", @@ -210,6 +210,14 @@ export default definePlugin({ replace: "$1style:($2)?{cursor:\"pointer\"}:{},onClick:$2?()=>{$self.openImage($3)}" } }, + { + find: ".canUsePremiumProfileCustomization,{avatarSrc:", + replacement: { + match: /children:\(0,\i\.jsx\)\(\i,{src:(\i)/, + replace: "style:{cursor:\"pointer\"},onClick:()=>{$self.openImage($1)},$&" + + } + }, // Group DMs top small & large icon { find: /\.recipients\.length>=2(?! Date: Thu, 6 Jun 2024 10:07:20 +0700 Subject: [PATCH 36/77] USRBG: fix in simplified profile (#2549) --- src/plugins/usrbg/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 1221cb9c5..b8e9f14b3 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -74,15 +74,15 @@ export default definePlugin({ ] }, { - find: /overrideBannerSrc:\i,profileType:/, + find: /overrideBannerSrc:\i,overrideBannerWidth:/, replacement: [ { match: /(\i)\.premiumType/, replace: "$self.premiumHook($1)||$&" }, { - match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},overrideBannerSrc:)/, - replace: "$1.overrideBannerSrc=$self.useBannerHook($1);" + match: /function \i\((\i)\)\{/, + replace: "$&$1.overrideBannerSrc=$self.useBannerHook($1);" } ] }, From 43b6933fe69fbf30f542c7da41d0e812150f8447 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 6 Jun 2024 00:47:57 -0300 Subject: [PATCH 37/77] Reporter: Include page errors; load wasm chunks --- scripts/generateReport.ts | 9 ++++++++- src/debug/loadLazyChunks.ts | 14 ++++++++------ src/plugins/appleMusic.desktop/index.tsx | 3 ++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index cf4210779..2a802da8c 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -286,7 +286,14 @@ page.on("console", async e => { }); page.on("error", e => console.error("[Error]", e.message)); -page.on("pageerror", e => console.error("[Page Error]", e.message)); +page.on("pageerror", e => { + if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { + console.error("[Page Error]", e.message); + report.otherErrors.push(e.message); + } else { + report.ignoredErrors.push(e.message); + } +}); async function reporterRuntime(token: string) { Vencord.Webpack.waitFor( diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index d8f84335c..0aeae732b 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -47,11 +47,11 @@ export async function loadLazyChunks() { for (const id of chunkIds) { if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); - if (isWasm && IS_WEB) { + if (isWorkerAsset) { invalidChunks.add(id); invalidChunkGroup = true; continue; @@ -149,13 +149,15 @@ export async function loadLazyChunks() { }); await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); // Loads and requires a chunk - if (!isWasm) { + 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); } })); diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index 16591028d..ef3ee3efc 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -6,7 +6,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; @@ -171,6 +171,7 @@ export default definePlugin({ description: "Discord rich presence for your Apple Music!", authors: [Devs.RyanCaoDev], hidden: !navigator.platform.startsWith("Mac"), + reporterTestable: ReporterTestable.None, settingsAboutComponent() { return <> From c54650b29a4af243aa7f79d01340235bedc3172b Mon Sep 17 00:00:00 2001 From: NuclideK <88620225+NuclideK@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:24:49 +0300 Subject: [PATCH 38/77] customRPC: fix typos in settings descriptions (#2559) --- src/plugins/customRPC/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index f1b2fbf53..ed354cba4 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -178,7 +178,7 @@ const settings = definePluginSettings({ }, startTime: { type: OptionType.NUMBER, - description: "Start timestamp in milisecond (only for custom timestamp mode)", + description: "Start timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -188,7 +188,7 @@ const settings = definePluginSettings({ }, endTime: { type: OptionType.NUMBER, - description: "End timestamp in milisecond (only for custom timestamp mode)", + description: "End timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { From 40db2f507808d200c8a4037d047ffc4db942a11c Mon Sep 17 00:00:00 2001 From: Lumap Date: Fri, 7 Jun 2024 23:04:40 +0200 Subject: [PATCH 39/77] AppleMusicRichPresence: add option to disable large/small image (#2562) --- src/plugins/appleMusic.desktop/index.tsx | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index ef3ee3efc..0d81204e9 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -68,6 +68,7 @@ export interface TrackData { const enum AssetImageType { Album = "Album", Artist = "Artist", + Disabled = "Disabled" } const applicationId = "1239490006054207550"; @@ -126,7 +127,8 @@ const settings = definePluginSettings({ description: "Activity assets large image type", options: [ { label: "Album artwork", value: AssetImageType.Album, default: true }, - { label: "Artist artwork", value: AssetImageType.Artist } + { label: "Artist artwork", value: AssetImageType.Artist }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, largeTextString: { @@ -139,7 +141,8 @@ const settings = definePluginSettings({ description: "Activity assets small image type", options: [ { label: "Album artwork", value: AssetImageType.Album }, - { label: "Artist artwork", value: AssetImageType.Artist, default: true } + { label: "Artist artwork", value: AssetImageType.Artist, default: true }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, smallTextString: { @@ -207,12 +210,17 @@ export default definePlugin({ getImageAsset(settings.store.smallImageType, trackData) ]); - const assets: ActivityAssets = { - large_image: largeImageAsset, - large_text: customFormat(settings.store.largeTextString, trackData), - small_image: smallImageAsset, - small_text: customFormat(settings.store.smallTextString, trackData), - }; + const assets: ActivityAssets = {}; + + if (settings.store.largeImageType !== AssetImageType.Disabled) { + assets.large_image = largeImageAsset; + assets.large_text = customFormat(settings.store.largeTextString, trackData); + } + + if (settings.store.smallImageType !== AssetImageType.Disabled) { + assets.small_image = smallImageAsset; + assets.small_text = customFormat(settings.store.smallTextString, trackData); + } const buttons: ActivityButton[] = []; From 1bc9a800a66e3cef6ef8d8cd54c4db6db7a73007 Mon Sep 17 00:00:00 2001 From: Elvyra <88881326+EdVraz@users.noreply.github.com> Date: Fri, 7 Jun 2024 23:05:14 +0200 Subject: [PATCH 40/77] fix moreUserTags (#2563) --- src/plugins/moreUserTags/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 9c848df6d..9d2790d0e 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -200,7 +200,7 @@ export default definePlugin({ } }, { - find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP,", + find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,", replacement: [ // make the tag show the right text { From 29c65948b43df64c9d8f527bb0967ad05685221c Mon Sep 17 00:00:00 2001 From: Nickyux <30734036+nmsturcke@users.noreply.github.com> Date: Fri, 7 Jun 2024 23:28:17 +0200 Subject: [PATCH 41/77] MessageLogger: add context menu option to clear channel history (#2008) Co-authored-by: vee --- src/api/MessageUpdater.ts | 2 +- src/plugins/messageLogger/index.tsx | 121 ++++++++++++++++------------ 2 files changed, 70 insertions(+), 53 deletions(-) diff --git a/src/api/MessageUpdater.ts b/src/api/MessageUpdater.ts index 5cac80528..284a20886 100644 --- a/src/api/MessageUpdater.ts +++ b/src/api/MessageUpdater.ts @@ -14,7 +14,7 @@ import { Message } from "discord-types/general"; * @param messageId The message id * @param fields The fields of the message to change. Leave empty if you just want to re-render */ -export function updateMessage(channelId: string, messageId: string, fields?: Partial) { +export function updateMessage(channelId: string, messageId: string, fields?: Partial>) { const channelMessageCache = MessageCache.getOrCreate(channelId); if (!channelMessageCache.has(messageId)) return; diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 892c819b7..daafeb2dc 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -18,7 +18,8 @@ import "./messageLogger.css"; -import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { updateMessage } from "@api/MessageUpdater"; import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -26,11 +27,17 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { ChannelStore, FluxDispatcher, i18n, Menu, Parser, Timestamp, UserStore } from "@webpack/common"; +import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; +import { Message } from "discord-types/general"; import overlayStyle from "./deleteStyleOverlay.css?managed"; import textStyle from "./deleteStyleText.css?managed"; +interface MLMessage extends Message { + deleted?: boolean; + editHistory?: { timestamp: Date; content: string; }[]; +} + const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); function addDeleteStyle() { @@ -89,35 +96,77 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) = )); }; +const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channel }) => { + const messages = MessageStore.getMessages(channel?.id) as MLMessage[]; + if (!messages?.some(msg => msg.deleted || msg.editHistory?.length)) return; + + const group = findGroupChildrenByChildId("mark-channel-read", children) ?? children; + group.push( + { + messages.forEach(msg => { + if (msg.deleted) + FluxDispatcher.dispatch({ + type: "MESSAGE_DELETE", + channelId: channel.id, + id: msg.id, + mlDeleted: true + }); + else + updateMessage(channel.id, msg.id, { + editHistory: [] + }); + }); + }} + /> + ); +}; + export default definePlugin({ name: "MessageLogger", description: "Temporarily logs deleted and edited messages.", - authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN], + authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux], + dependencies: ["MessageUpdaterAPI"], contextMenus: { - "message": patchMessageContextMenu + "message": patchMessageContextMenu, + "channel-context": patchChannelContextMenu, + "user-context": patchChannelContextMenu, + "gdm-context": patchChannelContextMenu }, start() { addDeleteStyle(); }, - renderEdit(edit: { timestamp: any, content: string; }) { - return ( - -
- {Parser.parse(edit.content)} - - {" "}({i18n.Messages.MESSAGE_EDITED}) - -
-
+ renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => { + const message = useStateFromStores( + [MessageStore], + () => MessageStore.getMessage(channelId, messageId) as MLMessage, + null, + (oldMsg, newMsg) => oldMsg.editHistory === newMsg.editHistory ); - }, + + return ( + <> + {message.editHistory?.map(edit => ( +
+ {Parser.parse(edit.content)} + + {" "}({i18n.Messages.MESSAGE_EDITED}) + +
+ ))} + + ); + }, { noop: true }), makeEdit(newMessage: any, oldMessage: any): any { return { @@ -222,11 +271,9 @@ export default definePlugin({ (message.channel_id === "1026515880080842772" && message.author?.id === "1017176847865352332"); }, - // Based on canary 63b8f1b4f2025213c5cf62f0966625bee3d53136 patches: [ { // MessageStore - // Module 171447 find: '"MessageStore"', replacement: [ { @@ -271,7 +318,6 @@ export default definePlugin({ { // Message domain model - // Module 451 find: "}addReaction(", replacement: [ { @@ -285,14 +331,8 @@ export default definePlugin({ { // Updated message transformer(?) - // Module 819525 find: "THREAD_STARTER_MESSAGE?null===", replacement: [ - // { - // // DEBUG: Log the params of the target function to the patch below - // match: /function N\(e,t\){/, - // replace: "function L(e,t){console.log('pre-transform', e, t);" - // }, { // Pass through editHistory & deleted & original attachments to the "edited message" transformer match: /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/, @@ -300,11 +340,6 @@ export default definePlugin({ "Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, attachments:$1.attachments })" }, - // { - // // DEBUG: Log the params of the target function to the patch below - // match: /function R\(e\){/, - // replace: "function R(e){console.log('after-edit-transform', arguments);" - // }, { // Construct new edited message and add editHistory & deleted (ref above) // Pass in custom data to attachment parser to mark attachments deleted as well @@ -335,7 +370,6 @@ export default definePlugin({ { // Attachment renderer - // Module 96063 find: ".removeMosaicItemHoverButton", group: true, replacement: [ @@ -352,7 +386,6 @@ export default definePlugin({ { // Base message component renderer - // Module 748241 find: "Message must not be a thread starter message", replacement: [ { @@ -365,20 +398,18 @@ export default definePlugin({ { // Message content renderer - // Module 43016 find: "Messages.MESSAGE_EDITED,\")\"", replacement: [ { // Render editHistory in the deepest div for message content match: /(\)\("div",\{id:.+?children:\[)/, - replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " + replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0]))," } ] }, { // ReferencedMessageStore - // Module 778667 find: '"ReferencedMessageStore"', replacement: [ { @@ -394,7 +425,6 @@ export default definePlugin({ { // Message context base menu - // Module 600300 find: "useMessageMenu:", replacement: [ { @@ -404,18 +434,5 @@ export default definePlugin({ } ] } - - // { - // // MessageStore caching internals - // // Module 819525 - // find: "e.getOrCreate=function(t)", - // replacement: [ - // // { - // // // DEBUG: log getOrCreate return values from MessageStore caching internals - // // match: /getOrCreate=function(.+?)return/, - // // replace: "getOrCreate=function$1console.log('getOrCreate',n);return" - // // } - // ] - // } ] }); From 5996e67c7d9d6512f734592f1e44e72897d37797 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 04:42:12 +0700 Subject: [PATCH 42/77] fix USRBG & ViewIcons in new profiles (#2557) Co-authored-by: Vendicated --- src/plugins/usrbg/index.tsx | 40 ++++++++++++++++----------------- src/plugins/viewIcons/index.tsx | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index b8e9f14b3..32da95af6 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -61,11 +61,7 @@ export default definePlugin({ replacement: [ { match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},bannerSrc:)/, - replace: "$1.bannerSrc=$self.useBannerHook($1);" + replace: "$self.patchPremiumType($1)||$&" }, { match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/, @@ -74,17 +70,19 @@ export default definePlugin({ ] }, { - find: /overrideBannerSrc:\i,overrideBannerWidth:/, - replacement: [ - { - match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /function \i\((\i)\)\{/, - replace: "$&$1.overrideBannerSrc=$self.useBannerHook($1);" - } - ] + find: "=!1,canUsePremiumCustomization:", + replacement: { + match: /(\i)\.premiumType/, + replace: "$self.patchPremiumType($1)||$&" + } + }, + { + find: "BannerLoadingStatus:function", + replacement: { + match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/, + replace: "$self.patchBannerUrl(arguments[0])||$&" + + } }, { find: "\"data-selenium-video-tile\":", @@ -92,7 +90,7 @@ export default definePlugin({ replacement: [ { match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/, - replace: "$1.style=$self.voiceBackgroundHook($1);" + replace: "$1.style=$self.getVoiceBackgroundStyles($1);" } ] } @@ -106,7 +104,7 @@ export default definePlugin({ ); }, - voiceBackgroundHook({ className, participantUserId }: any) { + getVoiceBackgroundStyles({ className, participantUserId }: any) { if (className.includes("tile_")) { if (this.userHasBackground(participantUserId)) { return { @@ -119,12 +117,12 @@ export default definePlugin({ } }, - useBannerHook({ displayProfile, user }: any) { + patchBannerUrl({ displayProfile }: any) { if (displayProfile?.banner && settings.store.nitroFirst) return; - if (this.userHasBackground(user.id)) return this.getImageUrl(user.id); + if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId); }, - premiumHook({ userId }: any) { + patchPremiumType({ userId }: any) { if (this.userHasBackground(userId)) return 2; }, diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index a94689689..9d1745116 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -192,7 +192,7 @@ export default definePlugin({ } })), // Banners - ...[".NITRO_BANNER,", /overrideBannerSrc:\i,overrideBannerWidth:/].map(find => ({ + ...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({ find, replacement: { // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, From 239da032ecf3136da5f9eade7ecb8321b971ef93 Mon Sep 17 00:00:00 2001 From: vee Date: Sat, 8 Jun 2024 00:48:17 +0200 Subject: [PATCH 43/77] README: add shiggy background on github --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a43c9f834..e67db93a2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ The cutest Discord client mod | ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | -|:--:| -| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | +| :--------------------------------------------------------------------------------------------------: | +| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | ## Features @@ -32,9 +32,9 @@ https://discord.gg/D9uwnFnqmd ## Sponsors -| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | -|:--:| -| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | +| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | +| :------------------------------------------------------------------------------------------: | +| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | | *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | @@ -65,3 +65,8 @@ Regardless, if your account is very important to you and it getting disabled wou Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it + +```math +\ce{$\unicode[goombafont; color:red; z-index: -10; position: fixed; top: 0; left: 0; height: 100%; object-fit: cover; width: 100%; opacity: 1; background: url('https://github.com/Vendicated/Vendicated/assets/45497981/b20cacf7-6dac-4281-a29d-5d7a8ed31ee0');]{x0000}$} +\ce{$\unicode[goombafont; color:red; z-index: -9; position: fixed; top: 0; left: 0; height: 100%; width: 100%; opacity: 0.9; background: var(--bgColor-default);]{x0000}$} +``` From 2c5079b49fb3731acaa73ada69ec282a1eeb4428 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 04:15:29 +0200 Subject: [PATCH 44/77] MessageLogger: fix potential error --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index daafeb2dc..39a059b8c 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -147,7 +147,7 @@ export default definePlugin({ [MessageStore], () => MessageStore.getMessage(channelId, messageId) as MLMessage, null, - (oldMsg, newMsg) => oldMsg.editHistory === newMsg.editHistory + (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory ); return ( From 914b211a91f21e827ffe9d333f19c1d8d31cb193 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 09:21:01 +0700 Subject: [PATCH 45/77] betterRoleDot: fix click to copy role color in profile (#2551) Co-authored-by: vee --- src/plugins/betterRoleDot/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index 3886de3f7..a8cadd8b0 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -48,6 +48,7 @@ export default definePlugin({ { find: ".ADD_ROLE_A11Y_LABEL", + all: true, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { @@ -57,6 +58,7 @@ export default definePlugin({ }, { find: ".roleVerifiedIcon", + all: true, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { From 810ff894dc9cb5ec5d62d16851b705cb0e88ef31 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 09:21:27 +0700 Subject: [PATCH 46/77] ui(RestartCard): fix yellow button now being blue (#2550) --- src/components/PluginSettings/index.tsx | 2 +- src/components/PluginSettings/styles.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 9c26a9cf1..978d2e85a 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) { Restart now to apply new plugins and their settings - diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index 66b2a2158..d3d182e58 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -78,6 +78,7 @@ .vc-plugins-restart-card button { margin-top: 0.5em; + background: var(--info-warning-foreground) !important; } .vc-plugins-info-button svg:not(:hover, :focus) { From 65970618d89d4c7383fc88dde8380423e67cd5e5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 05:26:03 +0200 Subject: [PATCH 47/77] fix badges in new profiles --- src/api/Badges.ts | 5 +- src/plugins/_api/badges/index.tsx | 58 +++++++++++++++++++++--- src/plugins/platformIndicators/index.tsx | 4 +- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 061bdeb8a..24c68c4ed 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -17,7 +17,6 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { User } from "discord-types/general"; import { ComponentType, HTMLProps } from "react"; import Plugins from "~plugins"; @@ -79,14 +78,14 @@ export function _getBadges(args: BadgeUserArgs) { : badges.push({ ...badge, ...args }); } } - const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.user.id); + const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId); if (donorBadges) badges.unshift(...donorBadges); return badges; } export interface BadgeUserArgs { - user: User; + userId: string; guildId: string; } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index bbccf0a11..b4ee45a1d 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -18,18 +18,20 @@ import "./fixBadgeOverflow.css"; -import { BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; +import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import DonateButton from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; import { openContributorModal } from "@components/PluginSettings/ContributorModal"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { isPluginDev } from "@utils/misc"; import { closeModal, Modals, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; -import { Forms, Toasts } from "@webpack/common"; +import { Forms, Toasts, UserStore } from "@webpack/common"; +import { User } from "discord-types/general"; const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png"; @@ -37,8 +39,8 @@ const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", image: CONTRIBUTOR_BADGE, position: BadgePosition.START, - shouldShow: ({ user }) => isPluginDev(user.id), - onClick: (_, { user }) => openContributorModal(user) + shouldShow: ({ userId }) => isPluginDev(userId), + onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId)) }; let DonorBadges = {} as Record>>; @@ -66,7 +68,7 @@ export default definePlugin({ replacement: [ { match: /&&(\i)\.push\(\{id:"premium".+?\}\);/, - replace: "$&$1.unshift(...Vencord.Api.Badges._getBadges(arguments[0]));", + replace: "$&$1.unshift(...$self.getBadges(arguments[0]));", }, { // alt: "", aria-hidden: false, src: originalSrc @@ -82,7 +84,40 @@ export default definePlugin({ // conditionally override their onClick with badge.onClick if it exists { match: /href:(\i)\.link/, - replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&" + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" + } + ] + }, + + /* new profiles */ + { + find: ".PANEL]:14", + replacement: { + match: /(?<=\i=\(0,\i\.default\)\(\i\);)return 0===\i.length/, + replace: "$& && $self.getBadges(arguments[0]?.displayProfile).length===0" + } + }, + { + find: ".description,delay:", + replacement: [ + { + match: /...(\i)\}=\(0,\i\.useUserProfileAnalyticsContext\)\(\);/, + replace: "$&arguments[0].badges?.unshift(...$self.getBadges($1));" + }, + { + // alt: "", aria-hidden: false, src: originalSrc + match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/, + // ...badge.props, ..., src: badge.image ?? ... + replace: "...$1.props,$& $1.image??" + }, + { + match: /(?<=text:(\i)\.description,.{0,50})children:/, + replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :" + }, + // conditionally override their onClick with badge.onClick if it exists + { + match: /href:(\i)\.link/, + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" } ] } @@ -104,6 +139,17 @@ export default definePlugin({ await loadBadges(); }, + getBadges(props: { userId: string; user?: User; guildId: string; }) { + try { + props.userId ??= props.user?.id!; + + return _getBadges(props); + } catch (e) { + new Logger("BadgeAPI#hasBadges").error(e); + return []; + } + }, + renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => { const Component = badge.component!; return ; diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index ea2ae125c..eef74d65e 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -130,9 +130,9 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, sma }; const badge: ProfileBadge = { - component: p => , + component: p => , position: BadgePosition.START, - shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length, + shouldShow: userInfo => !!Object.keys(getStatus(userInfo.userId) ?? {}).length, key: "indicator" }; From 4ec01d0f40a4b1d4ca8fb7fb8a78a23a9ea3055e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 05:54:36 +0200 Subject: [PATCH 48/77] disable UseEcoQoSForBackgroundProcess --- src/main/patcher.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/patcher.ts b/src/main/patcher.ts index a3725ef9b..e5b87290d 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -131,11 +131,16 @@ if (!IS_VANILLA) { process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); - // Monkey patch commandLine to disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // Monkey patch commandLine to: + // - disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // - disable UseEcoQoSForBackgroundProcess: Work around Discord unloading when in background const originalAppend = app.commandLine.appendSwitch; app.commandLine.appendSwitch = function (...args) { - if (args[0] === "disable-features" && !args[1]?.includes("WidgetLayering")) { - args[1] += ",WidgetLayering"; + if (args[0] === "disable-features") { + const disabledFeatures = new Set((args[1] ?? "").split(",")); + disabledFeatures.add("WidgetLayering"); + disabledFeatures.add("UseEcoQoSForBackgroundProcess"); + args[1] += [...disabledFeatures].join(","); } return originalAppend.apply(this, args); }; From 62830464af48016f6a5b6398fb49f8c5122d5991 Mon Sep 17 00:00:00 2001 From: Masterjoona <69722179+Masterjoona@users.noreply.github.com> Date: Sat, 8 Jun 2024 19:33:58 +0300 Subject: [PATCH 49/77] fix showconnections in new profiles (#2567) Co-authored-by: Vendicated --- src/plugins/showConnections/index.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index a78e4c418..505f696d8 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -74,15 +74,15 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile; }) => - +const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile, compactSpacing; }) => + ); const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => ); -function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { +function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme: string, compactSpacing?: boolean; }) { const profile = UserProfileStore.getUserProfile(id); if (!profile) return null; @@ -91,8 +91,10 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { if (!connections?.length) return null; + const Container = compactSpacing ? "div" : Section; + return ( -
+ {connections.map(connection => )} -
+ ); } @@ -178,7 +180,7 @@ export default definePlugin({ find: "{isUsingGuildBio:null!==(", replacement: { match: /,theme:\i\}\)(?=,.{0,150}setNote:)/, - replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })" + replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile, compactSpacing: false })" } }, { @@ -188,6 +190,13 @@ export default definePlugin({ match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/, replace: "$self.profilePanelComponent({ id: $1.recipients[0] }),$&" } + }, + { + find: "autoFocusNote:!0})", + replacement: { + match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, + replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, compactSpacing: true })" + } } ], settings, From 50c45137378f0a1813a06325c1c360492262805f Mon Sep 17 00:00:00 2001 From: programminglaboratorys <107296738+programminglaboratorys@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:56:23 +0300 Subject: [PATCH 50/77] RoleColorEverywhere: show role colors in the reactor list (#2490) Co-authored-by: vee --- src/plugins/roleColorEverywhere/index.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 56b224da8..23084a680 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -40,9 +40,16 @@ const settings = definePluginSettings({ default: true, description: "Show role colors in the voice chat user list", restartNeeded: true + }, + reactorsList: { + type: OptionType.BOOLEAN, + default: true, + description: "Show role colors in the reactors list", + restartNeeded: true } }); + export default definePlugin({ name: "RoleColorEverywhere", authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN], @@ -99,6 +106,14 @@ export default definePlugin({ } ], predicate: () => settings.store.voiceUsers, + }, + { + find: ".reactorDefault", + replacement: { + match: /\.openUserContextMenu\)\((\i),(\i),\i\).{0,250}tag:"strong"/, + replace: "$&,style:{color:$self.getColor($2?.id,$1)}" + }, + predicate: () => settings.store.reactorsList, } ], settings, From 4bf28f46349a8db217f46616930445d323afa8d4 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 21:56:59 +0200 Subject: [PATCH 51/77] BadgeAPI: fix our badges not showing if there are 0 discord badges --- src/plugins/_api/badges/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index b4ee45a1d..d8e391ae9 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -93,17 +93,13 @@ export default definePlugin({ { find: ".PANEL]:14", replacement: { - match: /(?<=\i=\(0,\i\.default\)\(\i\);)return 0===\i.length/, - replace: "$& && $self.getBadges(arguments[0]?.displayProfile).length===0" + match: /(?<=(\i)=\(0,\i\.default\)\(\i\);)return 0===\i.length\?/, + replace: "$1.unshift(...$self.getBadges(arguments[0].displayProfile));$&" } }, { find: ".description,delay:", replacement: [ - { - match: /...(\i)\}=\(0,\i\.useUserProfileAnalyticsContext\)\(\);/, - replace: "$&arguments[0].badges?.unshift(...$self.getBadges($1));" - }, { // alt: "", aria-hidden: false, src: originalSrc match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/, From aaba22f5773751ce0ed5f4ccba4795bc789dfa2d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 9 Jun 2024 03:44:52 +0200 Subject: [PATCH 52/77] ShowConnections: improve look in simplified prof; fix tooltip overflow --- src/plugins/_core/settings.tsx | 1 + src/plugins/showConnections/index.tsx | 54 +++++++++++++++++--------- src/plugins/showConnections/styles.css | 8 ++++ src/plugins/usrbg/index.tsx | 7 ---- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index fd221d27e..88ee05ff0 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -60,6 +60,7 @@ export default definePlugin({ // FIXME: remove once change merged to stable { find: "Messages.ACTIVITY_SETTINGS", + noWarn: true, replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 505f696d8..733d069e3 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -74,15 +74,28 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile, compactSpacing; }) => - +const profilePopoutComponent = ErrorBoundary.wrap( + (props: { user: User; displayProfile?: any; simplified?: boolean; }) => ( + + ), + { noop: true } ); -const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => - +const profilePanelComponent = ErrorBoundary.wrap( + (props: { id: string; simplified?: boolean; }) => ( + + ), + { noop: true } ); -function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme: string, compactSpacing?: boolean; }) { +function ConnectionsComponent({ id, theme, simplified }: { id: string, theme: string, simplified?: boolean; }) { const profile = UserProfileStore.getUserProfile(id); if (!profile) return null; @@ -91,10 +104,21 @@ function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme if (!connections?.length) return null; - const Container = compactSpacing ? "div" : Section; + const connectionsContainer = ( + + {connections.map(connection => )} + + ); + + if (simplified) + return connectionsContainer; return ( - +
Connections - - {connections.map(connection => )} - - + {connectionsContainer} +
); } @@ -134,7 +152,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect - {connection.name} + {connection.name} {connection.verified && } @@ -180,7 +198,7 @@ export default definePlugin({ find: "{isUsingGuildBio:null!==(", replacement: { match: /,theme:\i\}\)(?=,.{0,150}setNote:)/, - replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile, compactSpacing: false })" + replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })" } }, { @@ -195,7 +213,7 @@ export default definePlugin({ find: "autoFocusNote:!0})", replacement: { match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, - replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, compactSpacing: true })" + replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })" } } ], diff --git a/src/plugins/showConnections/styles.css b/src/plugins/showConnections/styles.css index 383593c11..cead5201c 100644 --- a/src/plugins/showConnections/styles.css +++ b/src/plugins/showConnections/styles.css @@ -9,3 +9,11 @@ gap: 0.25em; align-items: center; } + +.vc-sc-connection-name { + word-break: break-all; +} + +.vc-sc-tooltip svg { + min-width: 16px; +} diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 32da95af6..d9af54c39 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -69,13 +69,6 @@ export default definePlugin({ } ] }, - { - find: "=!1,canUsePremiumCustomization:", - replacement: { - match: /(\i)\.premiumType/, - replace: "$self.patchPremiumType($1)||$&" - } - }, { find: "BannerLoadingStatus:function", replacement: { From 26f5e829fee9084b589d9ba1c8a8f358f742fed9 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:45:35 -0300 Subject: [PATCH 53/77] o7 ResurrectHome - Home feature was removed from Discord Discord deleted pretty much all the client side code for the legacy home. While it's still possible for the client side code to be reconstructed, it won't be an easy task, so the plugin is getting deleted for now (in case someone ever implements the home again). --- src/plugins/resurrectHome/README.md | 5 - src/plugins/resurrectHome/index.tsx | 195 ---------------------------- 2 files changed, 200 deletions(-) delete mode 100644 src/plugins/resurrectHome/README.md delete mode 100644 src/plugins/resurrectHome/index.tsx diff --git a/src/plugins/resurrectHome/README.md b/src/plugins/resurrectHome/README.md deleted file mode 100644 index 2d26635d2..000000000 --- a/src/plugins/resurrectHome/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# ResurrectHome - -Brings back the phased out [Server Home](https://support.discord.com/hc/en-us/articles/6156116949911-Server-Home-Beta) feature! - -![](https://github.com/Vendicated/Vencord/assets/61953774/98d5d667-bbb9-48b8-872d-c9b3980f6506) diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx deleted file mode 100644 index 5193090ea..000000000 --- a/src/plugins/resurrectHome/index.tsx +++ /dev/null @@ -1,195 +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 { findGroupChildrenByChildId } from "@api/ContextMenu"; -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 { Button, Menu, Tooltip, useEffect, useState } from "@webpack/common"; - -const ChannelRowClasses = findByPropsLazy("modeConnected", "modeLocked", "icon"); - -let currentShouldViewServerHome = false; -const shouldViewServerHomeStates = new Set>>(); - -function ViewServerHomeButton() { - return ( - - {tooltipProps => ( - - )} - - ); -} - -function useForceServerHome() { - const { forceServerHome } = settings.use(["forceServerHome"]); - const [shouldViewServerHome, setShouldViewServerHome] = useState(currentShouldViewServerHome); - - useEffect(() => { - shouldViewServerHomeStates.add(setShouldViewServerHome); - - return () => { - shouldViewServerHomeStates.delete(setShouldViewServerHome); - }; - }, []); - - return shouldViewServerHome || forceServerHome; -} - -function useDisableViewServerHome() { - useEffect(() => () => { - currentShouldViewServerHome = false; - for (const setState of shouldViewServerHomeStates) { - setState(false); - } - }, []); -} - -const settings = definePluginSettings({ - forceServerHome: { - type: OptionType.BOOLEAN, - description: "Force the Server Guide to be the Server Home tab when it is enabled.", - default: false - } -}); - -export default definePlugin({ - name: "ResurrectHome", - description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking a server.", - authors: [Devs.Dolfies, Devs.Nuckyz], - settings, - - patches: [ - // Force home deprecation override - { - find: "GuildFeatures.GUILD_HOME_DEPRECATION_OVERRIDE", - all: true, - replacement: [ - { - match: /\i\.hasFeature\(\i\.GuildFeatures\.GUILD_HOME_DEPRECATION_OVERRIDE\)/g, - replace: "true" - } - ], - }, - // Disable feedback prompts - { - find: "GuildHomeFeedbackExperiment.definition.id", - replacement: [ - { - match: /return{showFeedback:.+?,setOnDismissedFeedback:(\i)}/, - replace: "return{showFeedback:false,setOnDismissedFeedback:$1}" - } - ] - }, - // This feature was never finished, so the patch is disabled - - // Enable guild feed render mode selector - // { - // find: "2022-01_home_feed_toggle", - // replacement: [ - // { - // match: /showSelector:!1/, - // replace: "showSelector:true" - // } - // ] - // }, - - // Fix focusMessage clearing previously cached messages and causing a loop when fetching messages around home messages - { - find: '"MessageActionCreators"', - replacement: { - match: /focusMessage\(\i\){.+?(?=focus:{messageId:(\i)})/, - replace: "$&after:$1," - } - }, - // Force Server Home instead of Server Guide - { - find: "61eef9_2", - replacement: { - match: /getMutableGuildChannelsForGuild\(\i\);return\(0,\i\.useStateFromStores\).+?\]\)(?=}function)/, - replace: m => `${m}&&!$self.useForceServerHome()` - } - }, - // Add View Server Home Button to Server Guide - { - find: "487e85_1", - replacement: { - match: /(?<=text:(\i)\?\i\.\i\.Messages\.SERVER_GUIDE:\i\.\i\.Messages\.GUILD_HOME,)/, - replace: "trailing:$self.ViewServerHomeButton({serverGuide:$1})," - } - }, - // Disable view Server Home override when the Server Home is unmouted - { - find: "69386d_5", - replacement: { - match: /location:"69386d_5".+?;/, - replace: "$&$self.useDisableViewServerHome();" - } - } - ], - - ViewServerHomeButton: ErrorBoundary.wrap(({ serverGuide }: { serverGuide?: boolean; }) => { - if (serverGuide !== true) return null; - - return ; - }), - - useForceServerHome, - useDisableViewServerHome, - - contextMenus: { - "guild-context"(children, props) { - const { forceServerHome } = settings.use(["forceServerHome"]); - - if (!props?.guild) return; - - const group = findGroupChildrenByChildId("hide-muted-channels", children); - - group?.unshift( - settings.store.forceServerHome = !forceServerHome} - /> - ); - } - } -}); From 64025bc5231857181df30f36dee2ee782417c3ec Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 02:32:33 +0200 Subject: [PATCH 54/77] MessageLogger: fix bugs with embeds & edits back to prev state --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 39a059b8c..fdd6dc9b9 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -302,7 +302,7 @@ export default definePlugin({ replace: "$1" + ".update($3,m =>" + " (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :" + - " $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" + + " $2.message.edited_timestamp && $2.message.content !== m.content ?" + " m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" + " m" + ")" + From 9de18ac8c7d5b585bd959933fb50acdc2d357610 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 03:44:51 +0200 Subject: [PATCH 55/77] Experiments: add toggle for toolbar dev button Co-Authored-By: F53 --- src/plugins/experiments/index.tsx | 24 +++++++++++++++++++++++- src/utils/constants.ts | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index cf4dbf249..9cb225211 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,12 +16,13 @@ * along with this program. If not, see . */ +import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; @@ -29,6 +30,15 @@ import hideBugReport from "./hideBugReport.css?managed"; const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); +const settings = definePluginSettings({ + toolbarDevMenu: { + type: OptionType.BOOLEAN, + description: "Change the Help (?) toolbar button (top right in chat) to Discord's developer menu", + default: false, + restartNeeded: true + } +}); + export default definePlugin({ name: "Experiments", description: "Enable Access to Experiments & other dev-only features in Discord!", @@ -40,6 +50,8 @@ export default definePlugin({ Devs.Nuckyz ], + settings, + patches: [ { find: "Object.defineProperties(this,{isDeveloper", @@ -68,6 +80,16 @@ export default definePlugin({ replacement: { match: /\i\.isStaff\(\)/, replace: "true" + }, + predicate: () => settings.store.toolbarDevMenu + }, + + // makes the Favourites Server experiment allow favouriting DMs and threads + { + find: "useCanFavoriteChannel", + replacement: { + match: /!\(\i\.isDM\(\)\|\|\i\.isThread\(\)\)/, + replace: "true", } } ], diff --git a/src/utils/constants.ts b/src/utils/constants.ts index ff754d5c2..09fc0285b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -38,7 +38,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 0n, }, Ven: { - name: "Vendicated", + name: "Vee", id: 343383572805058560n }, Arjix: { @@ -327,7 +327,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 305288513941667851n }, ImLvna: { - name: "Luna <3", + name: "lillith <3", id: 799319081723232267n }, rad: { From fd2311db3b8a3be9d5797e8dee9263fa56502650 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 23:10:47 -0300 Subject: [PATCH 56/77] Fix broken patches --- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/pinDms/components/CreateCategoryModal.tsx | 9 +++++++-- src/plugins/viewIcons/index.tsx | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 737406cf5..a9c44a1cd 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -338,7 +338,7 @@ export default definePlugin({ { // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\(?(\i)(?:,\i\))?=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index 06e1c3568..c0122e7c3 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 } from "@webpack"; +import { extractAndLoadChunksLazy, filters, find, findComponentByCode, findComponentByCodeLazy, LazyComponentWebpack } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -31,7 +31,12 @@ interface ColorPickerWithSwatchesProps { } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ColorPickerWithSwatches = findComponentByCodeLazy("presets,", "customColor:"); +// FIXME: Replace with the following when it reaches stable +// const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); +const ColorPickerWithSwatches = LazyComponentWebpack(() => + find(filters.byProps("ColorPicker", "CustomColorPicker"), { isIndirect: true })?.ColorPicker || + findComponentByCode("presets,", "customColor:") +); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index 9d1745116..a0ee5bcf9 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -184,7 +184,7 @@ export default definePlugin({ patches: [ // Profiles Modal pfp - ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ + ...[".UserProfileTypes.MODAL,hasProfileEffect", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ find, replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, From 008227cdfcfb3980e9607b27d492ea080bb8c690 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 23:31:42 -0300 Subject: [PATCH 57/77] Bump to 1.8.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01fe3552b..1bc01bac3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.8", + "version": "1.8.9", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 40c5ade82d2c1a5471a0d053299342acfad76cc5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 04:48:42 +0200 Subject: [PATCH 58/77] MessageLinkEmbeds: fix display when using compact mode --- src/plugins/messageLinkEmbeds/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index e76d53e4a..0620df53a 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -281,6 +281,8 @@ function getChannelLabelAndIconUrl(channel: Channel) { } function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null { + const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting(); + const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]); const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel); @@ -305,6 +307,7 @@ function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): message={message} channel={channel} subscribeToComponentDispatch={false} + compact={compact} />
)} From 0561bd19512e6f0e6db9d4d5b16298e4f68b3ca2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:39:04 -0300 Subject: [PATCH 59/77] fix: ShowConnections patch; chore: Remove dead code --- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/pinDms/components/CreateCategoryModal.tsx | 9 ++------- src/plugins/showConnections/index.tsx | 4 ++-- src/plugins/showHiddenChannels/index.tsx | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index a9c44a1cd..a6c3540d7 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -338,7 +338,7 @@ export default definePlugin({ { // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\(?(\i)(?:,\i\))?=>{)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\((\i),\i\)?=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index c0122e7c3..123bc8380 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, filters, find, findComponentByCode, findComponentByCodeLazy, LazyComponentWebpack } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -31,12 +31,7 @@ interface ColorPickerWithSwatchesProps { } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -// FIXME: Replace with the following when it reaches stable -// const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); -const ColorPickerWithSwatches = LazyComponentWebpack(() => - find(filters.byProps("ColorPicker", "CustomColorPicker"), { isIndirect: true })?.ColorPicker || - findComponentByCode("presets,", "customColor:") -); +const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 733d069e3..953feb264 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -210,9 +210,9 @@ export default definePlugin({ } }, { - find: "autoFocusNote:!0})", + find: ".UserProfileTypes.BITE_SIZE,onOpenProfile", replacement: { - match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, + match: /currentUser:\i,guild:\i,onOpenProfile:.+?}\)(?=])(?<=user:(\i),bio:null==(\i)\?.+?)/, replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })" } } diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 35d56091a..48342caef 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -73,9 +73,8 @@ export default definePlugin({ find: '"placeholder-channel-id"', replacement: [ // Remove the special logic for channels we don't have access to - // FIXME Remove variable matcher from threadsIds when it hits stable { - match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:(?:\[\]|\i)}}/, + match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\[\]}}/, replace: "" }, // Do not check for unreads when selecting the render level if the channel is hidden From dc74d28b8610a2d5b3b157caac1f3b5b9c3b9a85 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:30:11 -0300 Subject: [PATCH 60/77] Reporter: Fix summary code blocks --- scripts/generateReport.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 2a802da8c..41c9ece9f 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -75,9 +75,11 @@ const IGNORED_DISCORD_ERRORS = [ "Attempting to set fast connect zstd when unsupported" ] as Array; -function toCodeBlock(s: string) { +function toCodeBlock(s: string, indentation = 0, isDiscord = false) { s = s.replace(/```/g, "`\u200B`\u200B`"); - return "```" + s + " ```"; + + const indentationStr = Array(indentation).fill(" ").join(""); + return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${!isDiscord ? indentationStr : ""}\`\`\``; } async function printReport() { @@ -91,35 +93,35 @@ async function printReport() { report.badPatches.forEach(p => { console.log(`- ${p.plugin} (${p.type})`); console.log(` - ID: \`${p.id}\``); - console.log(` - Match: ${toCodeBlock(p.match)}`); - if (p.error) console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Match: ${toCodeBlock(p.match, " - Match: ".length)}`); + if (p.error) console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Bad Webpack Finds"); - report.badWebpackFinds.forEach(p => console.log("- " + p)); + report.badWebpackFinds.forEach(p => console.log("- " + toCodeBlock(p, "- ".length))); console.log(); console.log("## Bad Starts"); report.badStarts.forEach(p => { console.log(`- ${p.plugin}`); - console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Discord Errors"); report.otherErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); console.log("## Ignored Discord Errors"); report.ignoredErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); @@ -141,16 +143,16 @@ async function printReport() { const lines = [ `**__${p.plugin} (${p.type}):__**`, `ID: \`${p.id}\``, - `Match: ${toCodeBlock(p.match)}` + `Match: ${toCodeBlock(p.match, "Match: ".length, true)}` ]; - if (p.error) lines.push(`Error: ${toCodeBlock(p.error)}`); + if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`); return lines.join("\n"); }).join("\n\n") || "None", color: report.badPatches.length ? 0xff0000 : 0x00ff00 }, { title: "Bad Webpack Finds", - description: report.badWebpackFinds.map(toCodeBlock).join("\n") || "None", + description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None", color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00 }, { @@ -158,7 +160,7 @@ async function printReport() { description: report.badStarts.map(p => { const lines = [ `**__${p.plugin}:__**`, - toCodeBlock(p.error) + toCodeBlock(p.error, 0, true) ]; return lines.join("\n"); } @@ -167,7 +169,7 @@ async function printReport() { }, { title: "Discord Errors", - description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n")) : "None", + description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None", color: report.otherErrors.length ? 0xff0000 : 0x00ff00 } ] From e0e35058fdff83b3018026fdcf50014810008d80 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:15:26 -0300 Subject: [PATCH 61/77] Discord code blocks can't have indentation --- scripts/generateReport.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 41c9ece9f..b05a424ed 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -78,8 +78,8 @@ const IGNORED_DISCORD_ERRORS = [ function toCodeBlock(s: string, indentation = 0, isDiscord = false) { s = s.replace(/```/g, "`\u200B`\u200B`"); - const indentationStr = Array(indentation).fill(" ").join(""); - return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${!isDiscord ? indentationStr : ""}\`\`\``; + const indentationStr = Array(!isDiscord ? indentation : 0).fill(" ").join(""); + return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${indentationStr}\`\`\``; } async function printReport() { From 2f4e346e26d53b88998682672ba6d10a3460c06e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:28:38 -0300 Subject: [PATCH 62/77] FixCodeblockGap: Fix broken patch --- src/plugins/fixCodeblockGap/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/fixCodeblockGap/index.ts b/src/plugins/fixCodeblockGap/index.ts index 133409959..721175fe7 100644 --- a/src/plugins/fixCodeblockGap/index.ts +++ b/src/plugins/fixCodeblockGap/index.ts @@ -25,7 +25,7 @@ export default definePlugin({ authors: [Devs.Grzesiek11], patches: [ { - find: ".default.Messages.DELETED_ROLE_PLACEHOLDER", + find: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replacement: { match: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replace: "$&\\n?", From ca810250d1fcc5fa543890bdab9f19a0005e5a8e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 14 Jun 2024 22:56:21 +0200 Subject: [PATCH 63/77] ConsoleShortcuts: add `Stores` map with all stores --- src/plugins/consoleShortcuts/index.ts | 10 +++++++++- src/webpack/common/types/stores.d.ts | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 0a1323e75..2fdf87356 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -141,7 +141,15 @@ function makeShortcuts() { guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false }, me: { getter: () => Common.UserStore.getCurrentUser(), preload: false }, meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false }, - messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false } + messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }, + + Stores: { + getter: () => Object.fromEntries( + Common.Flux.Store.getAll() + .map(store => [store.getName(), store] as const) + .filter(([name]) => name.length > 1) + ) + } }; } diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index f1fc68e8b..037b2d81c 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -39,6 +39,8 @@ export class FluxStore { syncWith: GenericFunction; waitFor: GenericFunction; __getLocalVars(): Record; + + static getAll(): FluxStore[]; } export class FluxEmitter { From e79430ca84fef990eb79a3404e054ab6f981be73 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 14 Jun 2024 23:04:43 +0200 Subject: [PATCH 64/77] RelationShipNotifier: try to fix false positives for unavailable guilds --- src/plugins/relationshipNotifier/functions.ts | 4 ++-- src/plugins/relationshipNotifier/utils.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plugins/relationshipNotifier/functions.ts b/src/plugins/relationshipNotifier/functions.ts index 980b11300..5bff474b6 100644 --- a/src/plugins/relationshipNotifier/functions.ts +++ b/src/plugins/relationshipNotifier/functions.ts @@ -21,7 +21,7 @@ import { UserUtils } from "@webpack/common"; import settings from "./settings"; import { ChannelDelete, ChannelType, GuildDelete, RelationshipRemove, RelationshipType } from "./types"; -import { deleteGroup, deleteGuild, getGroup, getGuild, notify } from "./utils"; +import { deleteGroup, deleteGuild, getGroup, getGuild, GuildAvailabilityStore, notify } from "./utils"; let manuallyRemovedFriend: string | undefined; let manuallyRemovedGuild: string | undefined; @@ -63,7 +63,7 @@ export async function onRelationshipRemove({ relationship: { type, id } }: Relat export function onGuildDelete({ guild: { id, unavailable } }: GuildDelete) { if (!settings.store.servers) return; - if (unavailable) return; + if (unavailable || GuildAvailabilityStore.isUnavailable(id)) return; if (manuallyRemovedGuild === id) { deleteGuild(id); diff --git a/src/plugins/relationshipNotifier/utils.ts b/src/plugins/relationshipNotifier/utils.ts index 16f1892af..053cff835 100644 --- a/src/plugins/relationshipNotifier/utils.ts +++ b/src/plugins/relationshipNotifier/utils.ts @@ -19,11 +19,20 @@ import { DataStore, Notices } from "@api/index"; import { showNotification } from "@api/Notifications"; import { getUniqueUsername, openUserProfile } from "@utils/discord"; +import { findStoreLazy } 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 & { + totalGuilds: number; + totalUnavailableGuilds: number; + unavailableGuilds: string[]; + isUnavailable(guildId: string): boolean; +}; + const guilds = new Map(); const groups = new Map(); const friends = { @@ -59,7 +68,7 @@ export async function syncAndRunChecks() { if (settings.store.servers && oldGuilds?.size) { for (const [id, guild] of oldGuilds) { - if (!guilds.has(id)) + if (!guilds.has(id) && !GuildAvailabilityStore.isUnavailable(id)) notify(`You are no longer in the server ${guild.name}.`, guild.iconURL); } } From c1593e180646dbfa264ab8d1e1da66cc352cec77 Mon Sep 17 00:00:00 2001 From: SuperStormer <41648788+SuperStormer@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:13:27 -0400 Subject: [PATCH 65/77] Dearrow: fix ">" handling (#2582) --- src/plugins/dearrow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 89199da8f..5fb438256 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -69,7 +69,7 @@ async function embedDidMount(this: Component) { if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) { embed.dearrow.oldTitle = embed.rawTitle; - embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1"); + embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2"); } if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) { From 098124175fa28935824300f55cff3964aaf5ebb0 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 17 Jun 2024 23:00:25 +0200 Subject: [PATCH 66/77] Fix crashes & settings on canary --- src/plugins/_core/settings.tsx | 2 +- src/plugins/index.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 88ee05ff0..e998b8643 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -182,7 +182,7 @@ export default definePlugin({ patchedSettings: new WeakSet(), addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: SectionTypes) { - if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; + if (this.patchedSettings.has(elements)) return; this.patchedSettings.add(elements); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 32bfe7e97..e7cfb82dc 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -42,7 +42,18 @@ const subscribedFluxEventsPlugins = new Set(); const pluginsValues = Object.values(Plugins); const settings = Settings.plugins; +const forceDisabled = new Set([ + "MessageLogger", + "ShowHiddenChannels", + "MoreUserTags", + "Decor", + "IgnoreActivities", + "NoBlockedMessages", + "BetterFolders", + "NoPendingCount" +]); export function isPluginEnabled(p: string) { + if (forceDisabled.has(p)) return false; return ( Plugins[p]?.required || Plugins[p]?.isDependency || From db6b1f5aaff37bbed0d93687f8e1be92c59b2752 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:13:57 -0300 Subject: [PATCH 67/77] Fix plugins on stable --- src/plugins/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plugins/index.ts b/src/plugins/index.ts index e7cfb82dc..48c55c660 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -19,6 +19,7 @@ import { registerCommand, unregisterCommand } from "@api/Commands"; import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; +import { onceDefined } from "@shared/onceDefined"; import { Logger } from "@utils/Logger"; import { canonicalizeFind } from "@utils/patches"; import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types"; @@ -33,7 +34,7 @@ const logger = new Logger("PluginManager", "#a6d189"); export const PMLogger = logger; export const plugins = Plugins; -export const patches = [] as Patch[]; +export let patches = [] as Patch[]; /** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */ let enabledPluginsSubscribedFlux = false; @@ -53,7 +54,6 @@ const forceDisabled = new Set([ "NoPendingCount" ]); export function isPluginEnabled(p: string) { - if (forceDisabled.has(p)) return false; return ( Plugins[p]?.required || Plugins[p]?.isDependency || @@ -133,9 +133,17 @@ for (const p of pluginsValues) { } } +onceDefined(window, "GLOBAL_ENV", v => { + if (v.SENTRY_TAGS.buildId !== "366c746173a6ca0a801e9f4a4d7b6745e6de45d4") { + patches = patches.filter(p => !forceDisabled.has(p.plugin)); + } +}); + export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) { logger.info(`Starting plugins (stage ${target})`); for (const name in Plugins) { + if (window.GLOBAL_ENV?.SENTRY_TAGS.buildId !== "366c746173a6ca0a801e9f4a4d7b6745e6de45d4" && forceDisabled.has(name)) continue; + if (isPluginEnabled(name) && (!IS_REPORTER || isReporterTestable(Plugins[name], ReporterTestable.Start))) { const p = Plugins[name]; From d6f120943866a8acfc27e4d2143cd4167eb13e22 Mon Sep 17 00:00:00 2001 From: vee Date: Wed, 19 Jun 2024 03:04:15 +0200 Subject: [PATCH 68/77] fix first set of plugins (#2591) * Add back mangled webpack searching * Make window non enumerable in all cases * fix some webpack commons * oops * fix more webpack commons * fix some finds * fix more webpack commons * fix common names * fix reporter * fix Constants common * more fix * fix SettingsStores (return of old SettingsStoreAPI) * doomsday fix: MutualGroupDMs (#2585) * fix SettingsStoreAPI * fix MessageLinkEmbeds * fix checking uninitialised settings * doomsday fix: BetterSessions (#2587) * doomsday fix: ReviewDB and Summaries (#2586) Co-authored-by: vee * fix various things that use default/other names * fix settings * wbctxmenus * fix BetterSettings * wouldnt it be funny if discord reverted again once we're done * fix ViewIcons * fix showconnections * fix FriendsSince * FakeNitro: fix app icons * doomsday fix: NoPendingCount (#2590) --------- Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: Amia <9750071+aamiaa@users.noreply.github.com> Co-authored-by: Manti <67705577+mantikafasi@users.noreply.github.com> --- scripts/generateReport.ts | 3 +- src/api/Commands/commandHelpers.ts | 6 +- src/api/SettingsStores.ts | 69 +++++++++++++++ src/api/index.ts | 3 + src/plugins/_api/badges/index.tsx | 2 +- src/plugins/_api/settingsStores.ts | 43 ++++++++++ src/plugins/_core/settings.tsx | 6 +- src/plugins/arRPC.web/index.tsx | 6 +- src/plugins/betterGifAltText/index.ts | 2 +- src/plugins/betterRoleContext/index.tsx | 8 +- src/plugins/betterSessions/index.tsx | 17 ++-- src/plugins/betterSettings/index.tsx | 10 +-- src/plugins/customRPC/index.tsx | 11 ++- src/plugins/customidle/index.ts | 6 +- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/friendsSince/index.tsx | 8 +- src/plugins/gameActivityToggle/index.tsx | 9 +- src/plugins/ignoreActivities/index.tsx | 8 +- src/plugins/index.ts | 3 +- src/plugins/messageLinkEmbeds/index.tsx | 12 +-- src/plugins/mutualGroupDMs/index.tsx | 2 +- src/plugins/noPendingCount/index.ts | 2 +- src/plugins/pinDms/index.tsx | 4 +- .../reviewDB/components/ReviewComponent.tsx | 2 +- .../reviewDB/components/ReviewsView.tsx | 13 ++- src/plugins/roleColorEverywhere/index.tsx | 2 +- src/plugins/searchReply/index.tsx | 8 +- src/plugins/seeSummaries/index.tsx | 8 +- src/plugins/serverInfo/GuildInfoModal.tsx | 4 +- src/plugins/showConnections/index.tsx | 9 +- .../components/HiddenChannelLockScreen.tsx | 2 +- src/plugins/spotifyControls/SpotifyStore.ts | 6 +- src/plugins/spotifyControls/index.tsx | 4 +- src/plugins/viewIcons/index.tsx | 4 +- src/plugins/webContextMenus.web/index.ts | 4 +- src/plugins/xsOverlay.desktop/index.ts | 8 +- src/utils/discord.tsx | 2 + src/utils/modal.tsx | 5 +- src/webpack/common/menu.ts | 8 +- src/webpack/common/settingsStores.ts | 13 ++- src/webpack/common/stores.ts | 6 +- src/webpack/common/types/utils.d.ts | 14 ++-- src/webpack/common/utils.ts | 42 ++++++---- src/webpack/patchWebpack.ts | 50 ++++++++--- src/webpack/webpack.ts | 84 +++++++++++++++++++ 45 files changed, 393 insertions(+), 147 deletions(-) create mode 100644 src/api/SettingsStores.ts create mode 100644 src/plugins/_api/settingsStores.ts diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index b05a424ed..d8cbb44a0 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -46,7 +46,8 @@ await page.setBypassCSP(true); async function maybeGetError(handle: JSHandle): Promise { return await (handle as JSHandle)?.getProperty("message") - .then(m => m?.jsonValue()); + .then(m => m?.jsonValue()) + .catch(() => undefined); } const report = { diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index 2f7039137..4ae022c59 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/mergeDefaults"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } 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 MessageCreator = findByPropsLazy("createBotMessage"); +const createBotMessage = findByCodeLazy('username:"Clyde"'); export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; @@ -37,7 +37,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = createBotMessage({ channelId, content: "", embeds: [] }); MessageActions.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/api/SettingsStores.ts b/src/api/SettingsStores.ts new file mode 100644 index 000000000..18139e4e6 --- /dev/null +++ b/src/api/SettingsStores.ts @@ -0,0 +1,69 @@ +/* + * 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 { proxyLazy } from "@utils/lazy"; +import { Logger } from "@utils/Logger"; +import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; + +import { Settings } from "./Settings"; + +interface Setting { + /** + * Get the setting value + */ + getSetting(): T; + /** + * Update the setting value + * @param value The new value + */ + updateSetting(value: T | ((old: T) => T)): Promise; + /** + * React hook for automatically updating components when the setting is updated + */ + useSetting(): T; + settingsStoreApiGroup: string; + settingsStoreApiName: string; +} + +export const SettingsStores: Array> | undefined = proxyLazyWebpack(() => { + const modId = findModuleId('"textAndImages","renderSpoilers"') as any; + if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); + + const mod = wreq(modId); + if (mod == null) return; + + return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; +}); + +/** + * Get the store for a setting + * @param group The setting group + * @param name The name of the setting + */ +export function getSettingStore(group: string, name: string): Setting | undefined { + if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); + + return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); +} + +/** + * getSettingStore but lazy + */ +export function getSettingStoreLazy(group: string, name: string) { + return proxyLazy(() => getSettingStore(group, name)); +} diff --git a/src/api/index.ts b/src/api/index.ts index 02c70008a..737e06d60 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,6 +31,7 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; +import * as $SettingsStores from "./SettingsStores"; import * as $Styles from "./Styles"; /** @@ -116,3 +117,5 @@ export const ChatButtons = $ChatButtons; * An API allowing you to update and re-render messages */ export const MessageUpdater = $MessageUpdater; + +export const SettingsStores = $SettingsStores; diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index d8e391ae9..cb153c6a9 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -93,7 +93,7 @@ export default definePlugin({ { find: ".PANEL]:14", replacement: { - match: /(?<=(\i)=\(0,\i\.default\)\(\i\);)return 0===\i.length\?/, + match: /(?<=(\i)=\(0,\i\.\i\)\(\i\);)return 0===\i.length\?/, replace: "$1.unshift(...$self.getBadges(arguments[0].displayProfile));$&" } }, diff --git a/src/plugins/_api/settingsStores.ts b/src/plugins/_api/settingsStores.ts new file mode 100644 index 000000000..a888532ee --- /dev/null +++ b/src/plugins/_api/settingsStores.ts @@ -0,0 +1,43 @@ +/* + * 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 { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "SettingsStoreAPI", + description: "Patches Discord's SettingsStores to expose their group and name", + authors: [Devs.Nuckyz], + + patches: [ + { + find: ",updateSetting:", + replacement: [ + { + match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:/, + replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" + }, + // some wrapper. just make it copy the group and name + { + match: /updateSetting:.{0,20}shouldSync/, + replace: "settingsStoreApiGroup:arguments[0].settingsStoreApiGroup,settingsStoreApiName:arguments[0].settingsStoreApiName,$&" + } + ] + } + ] +}); diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index e998b8643..85300862a 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -94,8 +94,8 @@ export default definePlugin({ { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/, - replace: "$2.default.open($1);return;" + match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/, + replace: "$2.open($1);return;" } } ], @@ -182,7 +182,7 @@ export default definePlugin({ patchedSettings: new WeakSet(), addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: SectionTypes) { - if (this.patchedSettings.has(elements)) return; + if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; this.patchedSettings.add(elements); diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index e41e8675e..df307e756 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 { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL"); +const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID"); async function lookupAsset(applicationId: string, key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; @@ -32,7 +32,7 @@ async function lookupAsset(applicationId: string, key: string): Promise const apps: any = {}; async function lookupApp(applicationId: string): Promise { const socket: any = {}; - await RpcUtils.fetchApplicationsRPC(socket, applicationId); + await fetchApplicationsRPC(socket, applicationId); return socket.application; } diff --git a/src/plugins/betterGifAltText/index.ts b/src/plugins/betterGifAltText/index.ts index f0090343e..55fa22525 100644 --- a/src/plugins/betterGifAltText/index.ts +++ b/src/plugins/betterGifAltText/index.ts @@ -36,7 +36,7 @@ export default definePlugin({ { find: ".Messages.GIF,", replacement: { - match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/, + match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/, replace: // rename prop so we can always use default value "alt_$$:$1=$self.altify($3)||$2", diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index ecb1ed400..d69e188c0 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,15 +5,18 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; 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 { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; +import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); +const DeveloperMode = getSettingStoreLazy("appearance", "developerMode")!; + function PencilIcon() { return ( `${leftPart} 48 - ((this.props.lowerBadgeHeight ?? 16) + 8) + 4 ${rightPart} (this.props.lowerBadgeHeight ?? 16) + 8,` - } } ], @@ -153,14 +144,16 @@ export default definePlugin({ } - lowerBadgeWidth={20} - lowerBadgeHeight={20} + lowerBadgeSize={{ + width: 20, + height: 20 + }} >
- +
); diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index e0267e4b0..6d8d9855a 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -83,19 +83,19 @@ export default definePlugin({ find: "this.renderArtisanalHack()", replacement: [ { // Fade in on layer - match: /(?<=\((\i),"contextType",\i\.AccessibilityPreferencesContext\);)/, + match: /(?<=\((\i),"contextType",\i\.\i\);)/, replace: "$1=$self.Layer;", predicate: () => settings.store.disableFade }, { // Lazy-load contents - match: /createPromise:\(\)=>([^:}]*?),webpackId:"\d+",name:(?!="CollectiblesShop")"[^"]+"/g, + match: /createPromise:\(\)=>([^:}]*?),webpackId:\d+,name:(?!="CollectiblesShop")"[^"]+"/g, replace: "$&,_:$1", predicate: () => settings.store.eagerLoad } ] }, { // For some reason standardSidebarView also has a small fade-in - find: "DefaultCustomContentScroller:function()", + find: 'minimal:"contentColumnMinimal"', replacement: [ { match: /\(0,\i\.useTransition\)\((\i)/, @@ -111,7 +111,7 @@ export default definePlugin({ { // Load menu TOC eagerly find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format", replacement: { - match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, + match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?\i\.\i\).{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, replace: "$&(async ()=>$2)()," }, predicate: () => settings.store.eagerLoad @@ -119,7 +119,7 @@ export default definePlugin({ { // Settings cog context menu find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /\(0,\i.useDefaultUserSettingsSections\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, + match: /\(0,\i.\i\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, replace: "$self.wrapMenu($&)" } } diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index ed354cba4..7e4e9a938 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -17,6 +17,7 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -26,12 +27,15 @@ import { classes } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, StatusSettingsStores, UserStore } from "@webpack/common"; +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 ActivityClassName = findByPropsLazy("activity", "buttonColor"); +const ShowCurrentGame = getSettingStoreLazy("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)\//, ""); return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; @@ -390,13 +394,14 @@ export default definePlugin({ name: "CustomRPC", description: "Allows you to set a custom rich presence.", authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev], + dependencies: ["SettingsStoreAPI"], start: setRpc, stop: () => setRpc(true), settings, settingsAboutComponent: () => { const activity = useAwaiter(createActivity); - const gameActivityEnabled = StatusSettingsStores.ShowCurrentGame.useSetting(); + const gameActivityEnabled = ShowCurrentGame.useSetting(); const { profileThemeStyle } = useProfileThemeStyle({}); return ( @@ -412,7 +417,7 @@ export default definePlugin({ diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts index a59bbcb01..87caea75e 100644 --- a/src/plugins/customidle/index.ts +++ b/src/plugins/customidle/index.ts @@ -44,15 +44,15 @@ export default definePlugin({ find: 'type:"IDLE",idle:', replacement: [ { - match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/, + match: /Math\.min\((\i\.\i\.getSetting\(\)\*\i\.\i\.\i\.SECOND),\i\.\i\)/, replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum) }, { - match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/, + match: /\i\.\i\.dispatch\({type:"IDLE",idle:!1}\)/, replace: "$self.handleOnline()" }, { - match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/, + match: /(setInterval\(\i,\.25\*)\i\.\i/, replace: "$1$self.getIntervalDelay()" // For web installs } ] diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index a6c3540d7..cdf74d153 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -399,7 +399,7 @@ export default definePlugin({ }, // Separate patch for allowing using custom app icons { - find: ".FreemiumAppIconIds.DEFAULT&&(", + find: /\.getCurrentDesktopIcon.{0,25}\.isPremium/, replacement: { match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/, replace: "true" diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 58014f362..6c3542780 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -26,17 +26,17 @@ export default definePlugin({ patches: [ // User popup { - find: ".AnalyticsSections.USER_PROFILE}", + find: ".USER_PROFILE}};return", replacement: { - match: /\i.default,\{userId:(\i.id).{0,30}}\)/, + match: /\i.\i,\{userId:(\i.id).{0,30}}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, // User DMs "User Profile" popup in the right { - find: ".UserPopoutUpsellSource.PROFILE_PANEL,", + find: ".PROFILE_PANEL,", replacement: { - match: /\i.default,\{userId:([^,]+?)}\)/, + match: /\i.\i,\{userId:([^,]+?)}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 51feb9165..4e2a390d6 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -17,17 +17,19 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); +const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; + function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); @@ -60,7 +62,7 @@ function makeIcon(showCurrentGame?: boolean) { } function GameActivityToggleButton() { - const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting(); + const showCurrentGame = ShowCurrentGame.useSetting(); return (