Fix FakeNitro completely (#1903)

This commit is contained in:
Nuckyz 2023-10-26 00:19:26 -03:00 committed by GitHub
parent 03523446c1
commit 64c6f5740f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 47 deletions

@ -24,35 +24,34 @@ import { getCurrentGuild } from "@utils/discord";
import { proxyLazy } from "@utils/lazy"; import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; import { findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UserStore } from "@webpack/common"; import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UserSettingsActionCreators, UserStore } from "@webpack/common";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
import type { ReactElement, ReactNode } from "react"; import type { ReactElement, ReactNode } from "react";
const DRAFT_TYPE = 0; const DRAFT_TYPE = 0;
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR"); const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
const ReaderFactory = findByPropsLazy("readerFactory");
const StickerStore = findStoreLazy("StickersStore") as { const StickerStore = findStoreLazy("StickersStore") as {
getPremiumPacks(): StickerPack[]; getPremiumPacks(): StickerPack[];
getAllGuildStickers(): Map<string, Sticker[]>; getAllGuildStickers(): Map<string, Sticker[]>;
getStickerById(id: string): Sticker | undefined; getStickerById(id: string): Sticker | undefined;
}; };
function searchProtoClass(localName: string, parentProtoClass: any) { const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
if (!parentProtoClass) return; const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS");
const field = parentProtoClass.fields.find(field => field.localName === localName); function searchProtoClassField(localName: string, protoClass: any) {
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
if (!field) return; if (!field) return;
const getter: any = Object.values(field).find(value => typeof value === "function"); const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
return getter?.(); return fieldGetter?.();
} }
const AppearanceSettingsProto = proxyLazy(() => searchProtoClass("appearance", PreloadedUserSettingsProtoHandler.ProtoClass)); const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSettings", AppearanceSettingsProto)); const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
const USE_EXTERNAL_EMOJIS = 1n << 18n; const USE_EXTERNAL_EMOJIS = 1n << 18n;
const USE_EXTERNAL_STICKERS = 1n << 37n; const USE_EXTERNAL_STICKERS = 1n << 37n;
@ -176,31 +175,37 @@ export default definePlugin({
predicate: () => settings.store.enableEmojiBypass, predicate: () => settings.store.enableEmojiBypass,
replacement: [ replacement: [
{ {
// Create a variable for the intention of listing the emoji
match: /(?<=,intention:(\i).+?;)/, match: /(?<=,intention:(\i).+?;)/,
replace: (_, intention) => `var fakeNitroIntention=${intention};` replace: (_, intention) => `let fakeNitroIntention=${intention};`
}, },
{ {
// Send the intention of listing the emoji to the nitro permission check functions
match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g, match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0' replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
}, },
{ {
// Disallow the emoji if the intention doesn't allow it
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/, match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))` replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
}, },
{ {
// Make the emoji always available if the intention allows it
match: /if\(!\i\.available/, match: /if\(!\i\.available/,
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))` replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
} }
] ]
}, },
// Allow emojis and animated emojis to be sent everywhere
{ {
find: "canUseAnimatedEmojis:function", find: "canUseAnimatedEmojis:function",
predicate: () => settings.store.enableEmojiBypass, predicate: () => settings.store.enableEmojiBypass,
replacement: { replacement: {
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g, match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))(?=})/g,
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)` replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention!=null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
} }
}, },
// Allow stickers to be sent everywhere
{ {
find: "canUseStickersEverywhere:function", find: "canUseStickersEverywhere:function",
predicate: () => settings.store.enableStickerBypass, predicate: () => settings.store.enableStickerBypass,
@ -209,6 +214,7 @@ export default definePlugin({
replace: "$&return true;" replace: "$&return true;"
}, },
}, },
// Make stickers always available
{ {
find: "\"SENDABLE\"", find: "\"SENDABLE\"",
predicate: () => settings.store.enableStickerBypass, predicate: () => settings.store.enableStickerBypass,
@ -217,6 +223,7 @@ export default definePlugin({
replace: "true?" replace: "true?"
} }
}, },
// Allow streaming with high quality
{ {
find: "canUseHighVideoUploadQuality:function", find: "canUseHighVideoUploadQuality:function",
predicate: () => settings.store.enableStreamQualityBypass, predicate: () => settings.store.enableStreamQualityBypass,
@ -230,6 +237,7 @@ export default definePlugin({
}; };
}) })
}, },
// Remove boost requirements to stream with high quality
{ {
find: "STREAM_FPS_OPTION.format", find: "STREAM_FPS_OPTION.format",
predicate: () => settings.store.enableStreamQualityBypass, predicate: () => settings.store.enableStreamQualityBypass,
@ -238,6 +246,7 @@ export default definePlugin({
replace: "" replace: ""
} }
}, },
// Allow client themes to be changeable
{ {
find: "canUseClientThemes:function", find: "canUseClientThemes:function",
replacement: { replacement: {
@ -249,19 +258,22 @@ export default definePlugin({
find: '.displayName="UserSettingsProtoStore"', find: '.displayName="UserSettingsProtoStore"',
replacement: [ replacement: [
{ {
// Overwrite incoming connection settings proto with our local settings
match: /CONNECTION_OPEN:function\((\i)\){/, match: /CONNECTION_OPEN:function\((\i)\){/,
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);` replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
}, },
{ {
match: /=(\i)\.local;/, // Overwrite non local proto changes with our local settings
replace: (m, props) => `${m}${props}.local||$self.handleProtoChange(${props}.settings.proto);` match: /let{settings:/,
replace: "arguments[0].local||$self.handleProtoChange(arguments[0].settings.proto);$&"
} }
] ]
}, },
// Call our function to handle changing the gradient theme when selecting a new one
{ {
find: "updateTheme:function", find: ",updateTheme(",
replacement: { replacement: {
match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)(\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\))/, match: /(function \i\(\i\){let{backgroundGradientPresetId:(\i).+?)(\i\.\i\.updateAsync.+?theme=(.+?),.+?},\i\))/,
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});` replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
} }
}, },
@ -269,11 +281,13 @@ export default definePlugin({
find: '["strong","em","u","text","inlineCode","s","spoiler"]', find: '["strong","em","u","text","inlineCode","s","spoiler"]',
replacement: [ replacement: [
{ {
// Call our function to decide whether the emoji link should be kept or not
predicate: () => settings.store.transformEmojis, predicate: () => settings.store.transformEmojis,
match: /1!==(\i)\.length\|\|1!==\i\.length/, match: /1!==(\i)\.length\|\|1!==\i\.length/,
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])` replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
}, },
{ {
// Patch the rendered message content to add fake nitro emojis or remove sticker links
predicate: () => settings.store.transformEmojis || settings.store.transformStickers, predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/, match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);` replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
@ -281,36 +295,41 @@ export default definePlugin({
] ]
}, },
{ {
find: "renderEmbeds=function", find: "renderEmbeds(",
replacement: [ replacement: [
{ {
// Call our function to decide whether the embed should be ignored or not
predicate: () => settings.store.transformEmojis || settings.store.transformStickers, predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
match: /(renderEmbeds=function\((\i)\){)(.+?embeds\.map\(\(function\((\i)\){)/, match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/,
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
}, },
{ {
// Patch the stickers array to add fake nitro stickers
predicate: () => settings.store.transformStickers, predicate: () => settings.store.transformStickers,
match: /renderStickersAccessories=function\((\i)\){var (\i)=\(0,\i\.\i\)\(\i\),/, match: /(?<=renderStickersAccessories\((\i)\){let (\i)=\(0,\i\.\i\)\(\i\).+?;)/,
replace: (m, message, stickers) => `${m}${stickers}=$self.patchFakeNitroStickers(${stickers},${message}),` replace: (_, message, stickers) => `${stickers}=$self.patchFakeNitroStickers(${stickers},${message});`
}, },
{ {
// Filter attachments to remove fake nitro stickers or emojis
predicate: () => settings.store.transformStickers, predicate: () => settings.store.transformStickers,
match: /renderAttachments=function\(\i\){var \i=this,(\i)=\i.attachments.+?;/, match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});` replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
} }
] ]
}, },
{ {
find: ".STICKER_IN_MESSAGE_HOVER,", find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format",
predicate: () => settings.store.transformStickers, predicate: () => settings.store.transformStickers,
replacement: [ replacement: [
{ {
match: /var (\i)=\i\.renderableSticker,.{0,50}closePopout.+?channel:\i,closePopout:\i,/, // Export the renderable sticker to be used in the fake nitro sticker notice
replace: (m, renderableSticker) => `${m}renderableSticker:${renderableSticker},` match: /let{renderableSticker:(\i).{0,250}isGuildSticker.+?channel:\i,/,
replace: (m, renderableSticker) => `${m}fakeNitroRenderableSticker:${renderableSticker},`
}, },
{ {
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/, // Add the fake nitro sticker notice
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!${props}.renderableSticker?.fake)` match: /(let \i,{sticker:\i,channel:\i,closePopout:\i.+?}=(\i).+?;)(.+?description:)(\i)(?=,sticker:\i)/,
replace: (_, rest, props, rest2, reactNode) => `${rest}let{fakeNitroRenderableSticker}=${props};${rest2}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!fakeNitroRenderableSticker?.fake)`
} }
] ]
}, },
@ -318,6 +337,7 @@ export default definePlugin({
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,", find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
predicate: () => settings.store.transformEmojis, predicate: () => settings.store.transformEmojis,
replacement: { replacement: {
// Export the emoji node to be used in the fake nitro emoji notice
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<={node:(\i),.+?)/, match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<={node:(\i),.+?)/,
replace: (m, node) => `${m}fakeNitroNode:${node},` replace: (m, node) => `${m}fakeNitroNode:${node},`
} }
@ -326,10 +346,12 @@ export default definePlugin({
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION", find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
predicate: () => settings.store.transformEmojis, predicate: () => settings.store.transformEmojis,
replacement: { replacement: {
// Add the fake nitro emoji notice
match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/, match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
replace: (_, props, rest, reactNode) => `var fakeNitroNode=${props}.fakeNitroNode;${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},fakeNitroNode?.fake)` replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)`
} }
}, },
// Allow using custom app icons
{ {
find: "canUsePremiumAppIcons:function", find: "canUsePremiumAppIcons:function",
replacement: { replacement: {
@ -337,6 +359,7 @@ export default definePlugin({
replace: "$&return true;" replace: "$&return true;"
} }
}, },
// Separate patch for allowing using custom app icons
{ {
find: "location:\"AppIconHome\"", find: "location:\"AppIconHome\"",
replacement: { replacement: {
@ -359,26 +382,30 @@ export default definePlugin({
}, },
handleProtoChange(proto: any, user: any) { handleProtoChange(proto: any, user: any) {
if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || (!proto.appearance && !AppearanceSettingsProto)) return; if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || !PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators) return;
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0; const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
if (premiumType !== 2) { if (premiumType !== 2) {
proto.appearance ??= AppearanceSettingsProto.create(); proto.appearance ??= AppearanceSettingsActionCreators.create();
if (UserSettingsProtoStore.settings.appearance?.theme != null) { if (UserSettingsProtoStore.settings.appearance?.theme != null) {
proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme; const appearanceSettingsDummy = AppearanceSettingsActionCreators.create({
theme: UserSettingsProtoStore.settings.appearance.theme
});
proto.appearance.theme = appearanceSettingsDummy.theme;
} }
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null && ClientThemeSettingsProto) { if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) {
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({ const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
backgroundGradientPresetId: { backgroundGradientPresetId: {
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
} }
}); });
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto; proto.appearance.clientThemeSettings ??= clientThemeSettingsDummy;
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId; proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
} }
} }
}, },
@ -387,26 +414,26 @@ export default definePlugin({
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0; const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
if (premiumType === 2 || backgroundGradientPresetId == null) return original(); if (premiumType === 2 || backgroundGradientPresetId == null) return original();
if (!AppearanceSettingsProto || !ClientThemeSettingsProto || !ReaderFactory) return; if (!PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators || !ProtoUtils) return;
const currentAppearanceProto = PreloadedUserSettingsProtoHandler.getCurrentValue().appearance; const currentAppearanceSettings = PreloadedUserSettingsActionCreators.getCurrentValue().appearance;
const newAppearanceProto = currentAppearanceProto != null const newAppearanceProto = currentAppearanceSettings != null
? AppearanceSettingsProto.fromBinary(AppearanceSettingsProto.toBinary(currentAppearanceProto), ReaderFactory) ? AppearanceSettingsActionCreators.fromBinary(AppearanceSettingsActionCreators.toBinary(currentAppearanceSettings), ProtoUtils.BINARY_READ_OPTIONS)
: AppearanceSettingsProto.create(); : AppearanceSettingsActionCreators.create();
newAppearanceProto.theme = theme; newAppearanceProto.theme = theme;
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({ const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
backgroundGradientPresetId: { backgroundGradientPresetId: {
value: backgroundGradientPresetId value: backgroundGradientPresetId
} }
}); });
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummyProto; newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummy;
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId; newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
const proto = PreloadedUserSettingsProtoHandler.ProtoClass.create(); const proto = PreloadedUserSettingsActionCreators.ProtoClass.create();
proto.appearance = newAppearanceProto; proto.appearance = newAppearanceProto;
FluxDispatcher.dispatch({ FluxDispatcher.dispatch({

@ -416,7 +416,7 @@ export default definePlugin({
{ {
// Filter hidden channels from GuildChannelStore.getChannels unless told otherwise // Filter hidden channels from GuildChannelStore.getChannels unless told otherwise
match: /(?<=getChannels\(\i)(\){.+?)return (.+?)}/, match: /(?<=getChannels\(\i)(\){.+?)return (.+?)}/,
replace: (_, rest, channels) => `,shouldIncludeHidden=false${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden);}` replace: (_, rest, channels) => `,shouldIncludeHidden${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden??false);}`
} }
] ]
}, },