Remove FakeProfile

This commit is contained in:
thororen1234 2024-07-18 03:09:59 -04:00
parent ee6a447c6f
commit de726bb51b
6 changed files with 3 additions and 722 deletions

View file

@ -48,7 +48,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
- Encryptcord by Inbestigator
- EquicordCSS by FoxStorm1 and thororen (and all respective css developers)
- ExportContacts by dat_insanity
- FakeProfile by Sampath
- FindReply by newwares
- FriendshipRanks by Samwich
- GensokyoRadioRPC by RyanCaoDev and Prince527

View file

@ -1,13 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export const BASE_URL = "https://i.sampath.tech";
export const API_URL = BASE_URL + "/v3/users";
export const SKU_ID_DISCORD = "100101099222222";
export const SKU_ID = "100101099222224";
export const GUILD_ID = "1117373291095662623";
export const INVITE_KEY = "ffmkewQ4R7";
export const VERSION = "v2.17";

View file

@ -1,644 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { definePluginSettings } from "@api/Settings";
import { classNameFactory, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Link } from "@components/Link";
import { Devs, EquicordDevs } from "@utils/constants";
import { Margins } from "@utils/margins";
import { copyWithToast } from "@utils/misc";
import { closeModal, Modals, openModal } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy } from "@webpack";
import { Button, Forms, Toasts, Tooltip, useEffect, useState } from "@webpack/common";
import { User } from "discord-types/general";
import virtualMerge from "virtual-merge";
import { API_URL, BASE_URL, SKU_ID, SKU_ID_DISCORD, VERSION } from "./constants";
const CustomizationSection = findByCodeLazy(".customizationSectionBackground");
const cl = classNameFactory("vc-decoration-");
import style from "./style.css?managed";
import { AvatarDecoration, Colors, fakeProfileSectionProps, UserProfile, UserProfileData } from "./types";
let UsersData = {} as Record<string, UserProfileData>;
const UserBadges: Record<string, ProfileBadge[]> = {};
const updateBadgesForAllUsers = () => {
Object.keys(UsersData).forEach(userId => {
const newBadges = UsersData[userId].badges;
const existingBadges = UserBadges[userId] || [];
if (newBadges) {
newBadges.forEach((badge, index) => {
const existingBadge = existingBadges[index];
if (!existingBadge) {
const newBadge = {
image: badge.icon,
position: BadgePosition.START,
description: badge.description,
props: {
style: {
borderRadius: "15%",
transform: "scale(0.9)"
}
},
shouldShow: userInfo => userInfo.userId === userId,
onClick() {
const modalKey = openModal(props => (
<ErrorBoundary noop onError={() => {
closeModal(modalKey);
VencordNative.native.openExternal("https://github.com/sampathgujarathi/fakeProfile");
}}>
<Modals.ModalRoot {...props}>
<Modals.ModalHeader>
<Flex style={{ width: "100%", justifyContent: "center" }}>
<Forms.FormTitle
tag="h2"
style={{
width: "100%",
textAlign: "center",
margin: 0
}}
>
fakeProfile
</Forms.FormTitle>
</Flex>
</Modals.ModalHeader>
<Modals.ModalContent>
<div style={{ textAlign: "center" }}>
<img
role="presentation"
src="https://cdn.discordapp.com/emojis/1217777696650563614.webp"
alt=""
style={{ margin: "auto", display: "block" }}
/>
</div>
<div style={{ padding: "0.5em", textAlign: "center" }}>
<Forms.FormText>
Disclaimer: This badge is generated by the fakeProfile plugin. Please be aware that it may not represent genuine credentials or affiliations. Thank you for your understanding.
</Forms.FormText>
</div>
</Modals.ModalContent>
</Modals.ModalRoot>
</ErrorBoundary>
));
},
};
addBadge(newBadge);
if (!UserBadges[userId]) {
UserBadges[userId] = [];
}
UserBadges[userId].splice(index, 0, newBadge);
}
});
}
existingBadges.forEach((existingBadge, index) => {
const badgeStillExists = newBadges && newBadges[index];
if (!badgeStillExists) {
removeBadge(existingBadge);
UserBadges[userId].splice(index, 1);
}
});
});
};
async function loadfakeProfile(noCache = false) {
try {
const init = {} as RequestInit;
if (noCache)
init.cache = "no-cache";
const response = await fetch(API_URL + "/fakeProfile", init);
const data = await response.json();
UsersData = data;
} catch (error) {
console.error("Error loading fake profile:", error);
}
}
function getUserEffect(profileId: string) {
return UsersData[profileId] ? UsersData[profileId].profile_effect : null;
}
function encode(primary: number, accent: number): string {
const message = `[#${primary.toString(16).padStart(6, "0")},#${accent.toString(16).padStart(6, "0")}]`;
const padding = "";
const encoded = Array.from(message)
.map(x => x.codePointAt(0))
.filter(x => x! >= 0x20 && x! <= 0x7f)
.map(x => String.fromCodePoint(x! + 0xe0000))
.join("");
return (padding || "") + " " + encoded;
}
function decode(bio: string): Array<number> | null {
if (bio == null) return null;
const colorString = bio.match(
/\u{e005b}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]+?)\u{e002c}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]+?)\u{e005d}/u,
);
if (colorString != null) {
const parsed = [...colorString[0]]
.map(x => String.fromCodePoint(x.codePointAt(0)! - 0xe0000))
.join("");
const colors = parsed
.substring(1, parsed.length - 1)
.split(",")
.map(x => parseInt(x.replace("#", "0x"), 16));
return colors;
} else {
return null;
}
}
const settings = definePluginSettings({
enableProfileEffects: {
description: "Allows you to use profile effects",
type: OptionType.BOOLEAN,
default: false
},
enableProfileThemes: {
description: "Allows you to use profile themes",
type: OptionType.BOOLEAN,
default: false
},
enableCustomBadges: {
description: "Allows you to use custom badges",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true
},
enableAvatarDecorations: {
description: "Allows you to use discord avatar decorations",
type: OptionType.BOOLEAN,
default: false
},
showCustomBadgesinmessage: {
description: "Show custom badges in message",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true
},
nitroFirst: {
description: "Banner/Avatar to use if both Nitro and fakeProfile Banner/Avatar are present",
type: OptionType.SELECT,
options: [
{ label: "Nitro", value: true, default: true },
{ label: "fakeProfile", value: false },
]
},
voiceBackground: {
description: "Use fakeProfile banners as voice chat backgrounds",
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
}
});
function fakeProfileSection({ hideTitle = false, hideDivider = false, noMargin = false }: fakeProfileSectionProps) {
return <CustomizationSection
title={!hideTitle && "fakeProfile"}
hasBackground={true}
hideDivider={hideDivider}
className={noMargin && cl("section-remove-margin")}
>
<Flex>
<Button
onClick={async () => {
await loadfakeProfile(true);
updateBadgesForAllUsers();
Toasts.show({
message: "Successfully refetched fakeProfile!",
id: Toasts.genId(),
type: Toasts.Type.SUCCESS
});
}}
size={Button.Sizes.SMALL}
>
Refetch fakeProfile
</Button>
</Flex>
</CustomizationSection>;
}
const openModalOnClick = () => {
const modalKey = openModal(props => (
<ErrorBoundary noop onError={() => {
closeModal(modalKey);
VencordNative.native.openExternal("https://github.com/sampathgujarathi/fakeProfile");
}}>
<Modals.ModalRoot {...props}>
<Modals.ModalHeader>
<Flex style={{ width: "100%", justifyContent: "center" }}>
<Forms.FormTitle
tag="h2"
style={{
width: "100%",
textAlign: "center",
margin: 0
}}
>
fakeProfile
</Forms.FormTitle>
</Flex>
</Modals.ModalHeader>
<Modals.ModalContent>
<div style={{ textAlign: "center" }}>
<img
role="presentation"
src="https://cdn.discordapp.com/emojis/1217777696650563614.webp"
alt=""
style={{ margin: "auto", display: "block" }}
/>
</div>
<div style={{ padding: "0.5em", textAlign: "center" }}>
<Forms.FormText>
Disclaimer: This badge is generated by the fakeProfile plugin. Please be aware that it may not represent genuine credentials or affiliations. Thank you for your understanding.
</Forms.FormText>
</div>
</Modals.ModalContent>
</Modals.ModalRoot>
</ErrorBoundary>
));
};
function ImageIcon(path: string) {
return ({ tooltip }: { tooltip: string; }) => (
<Tooltip text={tooltip} >
{(tooltipProps: any) => (
<img {...tooltipProps} src={path} height={20} width={20} />
)}
</Tooltip>
);
}
const BadgeIcon = ({ user, badgeImg, badgeText }: { user: User, badgeImg: string, badgeText: string; }) => {
if (UsersData[user.id]?.badges) {
const Icon = ImageIcon(badgeImg);
const tooltip = badgeText;
return <Icon tooltip={tooltip} />;
} else {
return null;
}
};
const BadgeMain = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => {
const validBadges = UsersData[user.id]?.badges;
if (!validBadges || validBadges.length === 0) return null;
const icons = validBadges.map((badge, index) => (
<div onClick={openModalOnClick} >
<BadgeIcon
key={index}
user={user}
badgeImg={badge.icon}
badgeText={badge.description}
/>
</div>
));
return (
<span
className="custom-badge"
style={{
display: "inline-flex",
justifyContent: "center",
alignItems: "center",
marginLeft: wantMargin ? 4 : 0,
verticalAlign: "top",
position: "relative",
top: wantTopMargin ? 2 : 0,
padding: !wantMargin ? 1 : 0,
gap: 2
}}
>
{icons}
</span>
);
};
export default definePlugin({
name: "fakeProfile",
description: "Unlock Discord profile effects, themes, avatar decorations, and custom badges without the need for Nitro.",
authors: [EquicordDevs.Sampath, Devs.Alyxia, Devs.Remty, Devs.AutumnVN, Devs.pylix, Devs.TheKodeToad],
dependencies: ["MessageDecorationsAPI"],
start: async () => {
enableStyle(style);
await loadfakeProfile();
if (settings.store.enableCustomBadges) {
updateBadgesForAllUsers();
}
if (settings.store.showCustomBadgesinmessage) {
addDecoration("custom-badge", props =>
<ErrorBoundary noop>
<BadgeMain user={props.message?.author} wantTopMargin={true} />
</ErrorBoundary>
);
}
const response = await fetch(BASE_URL + "/fakeProfile");
const data = await response.json();
if (data.version !== VERSION) {
Toasts.show({
message: "There is an update available for the fakeProfile plugin.",
id: Toasts.genId(),
type: Toasts.Type.MESSAGE,
options: {
position: Toasts.Position.BOTTOM
}
});
}
setInterval(async () => {
await loadfakeProfile(true);
if (settings.store.enableCustomBadges) {
updateBadgesForAllUsers();
}
}, data.reloadInterval);
},
stop: () => {
if (settings.store.showCustomBadgesinmessage) {
removeDecoration("custom-badge");
}
},
patches: [
{
find: "UserProfileStore",
replacement: {
match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/,
replace: "$self.profileDecodeHook($1)"
}
},
{
find: "getAvatarDecorationURL:",
replacement: {
match: /(?<=function \i\(\i\){)(?=let{avatarDecoration)/,
replace: "const vcDecoration = (() => { return $self.getAvatarDecorationURL(arguments[0]); })(); if (vcDecoration) return vcDecoration;"
}
},
{
find: ".USER_SETTINGS_RESET_PROFILE_THEME",
replacement: {
match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
}
},
{
find: "DefaultCustomizationSections",
replacement: {
match: /(?<=USER_SETTINGS_AVATAR_DECORATION},"decoration"\),)/,
replace: "$self.fakeProfileSection(),"
}
},
{
find: ".NITRO_BANNER,",
replacement: {
match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/,
replace: "&&$self.shouldShowBadge(arguments[0])$&"
}
},
{
find: "=!1,canUsePremiumCustomization:",
replacement: {
match: /(\i)\.premiumType/,
replace: "$self.premiumHook($1)||$&"
}
},
{
find: ".banner)==null",
replacement: {
match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/,
replace: "$self.useBannerHook(arguments[0])||$&"
}
},
{
find: "\"data-selenium-video-tile\":",
predicate: () => settings.store.voiceBackground,
replacement: [
{
match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/,
replace: "$1.style=$self.voiceBackgroundHook($1);"
}
]
},
{
find: "getUserAvatarURL:",
replacement: [
{
match: /(getUserAvatarURL:)(\i),/,
replace: "$1$self.getAvatarHook($2),"
},
{
match: /(getUserAvatarURL:\i\(\){return )(\i)}/,
replace: "$1$self.getAvatarHook($2)}"
}
]
},
{
find: "isAvatarDecorationAnimating:",
group: true,
replacement: [
{
match: /(?<=TryItOut:\i,guildId:\i}\),)(?<=user:(\i).+?)/,
replace: "vcAvatarDecoration=$self.useUserAvatarDecoration($1),"
},
{
match: /(?<={avatarDecoration:).{1,20}?(?=,)(?<=avatarDecorationOverride:(\i).+?)/,
replace: "$1??vcAvatarDecoration??($&)"
},
{
match: /(?<=size:\i}\),\[)/,
replace: "vcAvatarDecoration,"
}
]
},
{
find: "renderAvatarWithPopout(){",
replacement: [
{
match: /(?<=\)\({(?:(?:.(?!\)}))*,)?avatarDecoration:)(\i)\.avatarDecoration(?=,|}\))/,
replace: "$self.useUserAvatarDecoration($1)??$&"
}
]
}
],
settingsAboutComponent: () => (
<Forms.FormSection>
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
<Link href="https://github.com/sampathgujarathi/fakeProfile">CLICK HERE TO GET PROFILE EFFECTS, CUSTOM BADGES, BANNER OR ANIMATED PFP</Link>
<Forms.FormText>
Enable Profile Themes to use fake profile themes. <br />
To set your own colors:
<ul>
<li> go to your profile settings</li>
<li> choose your own colors in the Nitro preview</li>
<li> click the "Copy 3y3" button</li>
<li> paste the invisible text anywhere in your bio</li>
</ul><br />
</Forms.FormText>
</Forms.FormSection>
),
settings,
profileDecodeHook(user: UserProfile) {
if (user) {
if (settings.store.enableProfileEffects || settings.store.enableProfileThemes) {
let mergeData: Partial<UserProfile> = {};
const profileEffect = getUserEffect(user.userId);
const colors = decode(user.bio);
if (settings.store.enableProfileEffects && profileEffect) {
mergeData = {
...mergeData,
profileEffectId: profileEffect
};
}
if (settings.store.enableProfileThemes && colors) {
mergeData = {
...mergeData,
premiumType: 2,
themeColors: colors
};
}
return virtualMerge(user, mergeData as UserProfile);
}
return user;
}
return user;
},
SKU_ID_DISCORD,
SKU_ID,
useUserAvatarDecoration(user?: User): { asset: string; skuId: string; animated: boolean; } | null {
const [avatarDecoration, setAvatarDecoration] = useState<{ asset: string; skuId: string; animated: boolean; } | null>(null);
useEffect(() => {
const fetchUserAssets = async () => {
try {
if (user?.id) {
const userAssetsData = UsersData[user.id];
if (userAssetsData?.decoration) {
setAvatarDecoration({
asset: userAssetsData.decoration?.asset,
skuId: userAssetsData.decoration?.skuId,
animated: userAssetsData.decoration?.animated
});
}
}
} catch (error) {
console.error("Error fetching user assets:", error);
}
};
fetchUserAssets();
}, [user, UsersData]);
if (!user || !settings.store.enableAvatarDecorations) {
return null;
}
return avatarDecoration ? {
asset: avatarDecoration.asset,
skuId: avatarDecoration.skuId,
animated: avatarDecoration.animated
} : null;
},
voiceBackgroundHook({ className, participantUserId }: any) {
if (className.includes("tile_")) {
if (UsersData[participantUserId]) {
return {
backgroundImage: `url(${UsersData[participantUserId].banner})`,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat"
};
}
}
},
useBannerHook({ displayProfile }: any) {
if (displayProfile?.banner && settings.store.nitroFirst) return;
if (UsersData[displayProfile?.userId] && UsersData[displayProfile?.userId].banner) return UsersData[displayProfile?.userId].banner;
},
premiumHook({ userId }: any) {
if (UsersData[userId]) return 2;
},
shouldShowBadge({ displayProfile, user }: any) {
return displayProfile?.banner && (UsersData[user.id] || settings.store.nitroFirst);
},
getAvatarHook: (original: any) => (user: User, animated: boolean, size: number) => {
if (!settings.store.nitroFirst && user.avatar?.startsWith("a_")) return original(user, animated, size);
return UsersData[user.id]?.avatar ?? original(user, animated, size);
},
getAvatarDecorationURL({ avatarDecoration, canAnimate }: { avatarDecoration: AvatarDecoration | null; canAnimate?: boolean; }) {
if (!avatarDecoration || !settings.store.enableAvatarDecorations) return;
if (canAnimate && avatarDecoration?.animated !== false) {
if (avatarDecoration?.skuId === SKU_ID) {
const url = new URL(`https://i.sampath.tech/avatar-decoration-presets/a_${avatarDecoration?.asset}.png`);
return url.toString();
} else {
const url = new URL(`https://cdn.discordapp.com/avatar-decoration-presets/${avatarDecoration?.asset}.png`);
return url.toString();
}
} else {
if (avatarDecoration?.skuId === SKU_ID) {
const url = new URL(`https://i.sampath.tech/avatar-decoration-presets/${avatarDecoration?.asset}.png`);
return url.toString();
} else {
const url = new URL(`https://cdn.discordapp.com/avatar-decoration-presets/${avatarDecoration?.asset}.png?passthrough=false`);
return url.toString();
}
}
},
fakeProfileSection: ErrorBoundary.wrap(fakeProfileSection),
toolboxActions: {
async "Refetch fakeProfile"() {
await loadfakeProfile(true);
updateBadgesForAllUsers();
Toasts.show({
message: "Successfully refetched fakeProfile!",
id: Toasts.genId(),
type: Toasts.Type.SUCCESS
});
}
},
addCopy3y3Button: ErrorBoundary.wrap(function ({ primary, accent }: Colors) {
return <Button
onClick={() => {
const colorString = encode(primary, accent);
copyWithToast(colorString);
}}
color={Button.Colors.PRIMARY}
size={Button.Sizes.XLARGE}
className={Margins.left16}
>Copy 3y3
</Button >;
}, { noop: true }),
});

View file

@ -1,12 +0,0 @@
:is([class*="userProfile"], [class*="userPopout"]) [class*="bannerPremium"] {
background: center / cover no-repeat;
}
[class*="NonPremium"]:has([class*="bannerPremium"]) [class*="avatarPositionNormal"],
[class*="PremiumWithoutBanner"]:has([class*="bannerPremium"]) [class*="avatarPositionPremiumNoBanner"] {
top: 76px;
}
[style*="background-image"] [class*="background_"] {
background-color: transparent !important;
}

View file

@ -1,49 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { User } from "discord-types/general";
export interface Badge {
asset: string;
description: string;
icon: string;
link?: string;
}
export interface DecorationData {
asset: string;
skuId: string;
animated: boolean;
}
export interface AvatarDecoration {
asset: string;
skuId: string;
animated: boolean;
}
export interface UserProfile extends User {
profileEffectId: string;
userId: string;
themeColors?: Array<number>;
}
export interface UserProfileData {
profile_effect: string;
banner: string;
avatar: string;
badges: Badge[];
decoration: DecorationData;
}
export interface Colors {
primary: number;
accent: number;
}
export interface fakeProfileSectionProps {
hideTitle?: boolean;
hideDivider?: boolean;
noMargin?: boolean;
}

View file

@ -7,21 +7,21 @@
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Margins } from "@utils/margins";
import { identity } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { findByCodeLazy } from "@webpack";
import { Card, Forms, React, Select, SnowflakeUtils, Switch } from "@webpack/common";
import { makeEmptyAppId } from ".";
import { ActivityType, RpcApp, SettingsProps } from "./types";
const RPCUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL");
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
const cachedApps: any = {};
async function lookupApp(appId: string): Promise<RpcApp | null> {
if (cachedApps[appId]) return cachedApps[appId];
const socket: any = {};
try {
await RPCUtils.fetchApplicationsRPC(socket, appId);
await fetchApplicationsRPC(socket, appId);
console.log(`Lookup finished for ${socket.application.name}`);
cachedApps[appId] = socket.application;
return socket.application;