Plugin

GlobalBadges

Adds global badges from other client mods

index.tsx
Download

Source

src/plugins/globalBadges/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
8import { Devs } from "@utils/constants";
9import definePlugin, { OptionType } from "@utils/types";
10import { React, Tooltip } from "@webpack/common";
11import { User } from "discord-types/general";
12
13type CustomBadge = string | {
14 name: string;
15 badge: string;
16 custom?: boolean;
17};
18
19interface BadgeCache {
20 badges: { [mod: string]: CustomBadge[]; };
21 expires: number;
22}
23
24const API_URL = "https:class="ts-cmt">//clientmodbadges-api.herokuapp.com/";
25
26const cache = new Map<string, BadgeCache>();
27const EXPIRES = 1000 * 60 * 15;
28
29const fetchBadges = (id: string): BadgeCache["badges"] | undefined => {
30 const cachedValue = cache.get(id);
31 if (!cache.has(id) || (cachedValue && cachedValue.expires < Date.now())) {
32 fetch(`${API_URL}users/${id}`)
33 .then(res => res.json() as Promise<BadgeCache["badges"]>)
34 .then(body => {
35 cache.set(id, { badges: body, expires: Date.now() + EXPIRES });
36 return body;
37 });
38 } else if (cachedValue) {
39 return cachedValue.badges;
40 }
41};
42
43const BadgeComponent = ({ name, img }: { name: string, img: string; }) => {
44 return (
45 <Tooltip text={name} >
46 {(tooltipProps: any) => (
47 <img
48 {...tooltipProps}
49 src={img}
50 style={{ width: "22px", height: "22px", transform: name.includes("Replugged") ? "scale(0.9)" : null, margin: "0 2px" }}
51 />
52 )}
53 </Tooltip>
54 );
55};
56
57const GlobalBadges = ({ user }: { user: User; }) => {
58 const [badges, setBadges] = React.useState<BadgeCache["badges"]>({});
59 React.useEffect(() => setBadges(fetchBadges(user.id) ?? {}), [user.id]);
60
61 if (!badges) return null;
62 const globalBadges: JSX.Element[] = [];
63
64 Object.keys(badges).forEach(mod => {
65 if (mod.toLowerCase() === "vencord") return;
66 badges[mod].forEach(badge => {
67 if (typeof badge === "string") {
68 const fullNames = { "hunter": "Bug Hunter", "early": "Early User" };
69 badge = {
70 name: fullNames[badge as string] ? fullNames[badge as string] : badge,
71 badge: `${API_URL}badges/${mod}/${(badge as string).replace(mod, "").trim().split(" ")[0]}`
72 };
73 } else if (typeof badge === "object") badge.custom = true;
74 if (!showCustom() && badge.custom) return;
75 const cleanName = badge.name.replace(mod, "").trim();
76 const prefix = showPrefix() ? mod : "";
77 if (!badge.custom) badge.name = `${prefix} ${cleanName.charAt(0).toUpperCase() + cleanName.slice(1)}`;
78 globalBadges.push(<BadgeComponent name={badge.name} img={badge.badge} />);
79 });
80 });
81
82 return (
83 <div className="vc-global-badges" style={{ alignItems: "center", display: "flex" }}>
84 {globalBadges}
85 </div>
86 );
87};
88
89const Badge: ProfileBadge = {
90 component: b => <GlobalBadges {...b} />,
91 position: BadgePosition.START,
92 shouldShow: userInfo => !!Object.keys(fetchBadges(userInfo.user.id) ?? {}).length,
93 key: "GlobalBadges"
94};
95
96const showPrefix = () => FiveCord.Settings.plugins.GlobalBadges.showPrefix;
97const showCustom = () => FiveCord.Settings.plugins.GlobalBadges.showCustom;
98
99export default definePlugin({
100 name: "GlobalBadges",
101 description: "Adds global badges from other client mods",
102 authors: [Devs.FiveCord],
103
104 start: () => addBadge(Badge),
105 stop: () => removeBadge(Badge),
106
107 options: {
108 showPrefix: {
109 type: OptionType.BOOLEAN,
110 description: "Shows the Mod as Prefix",
111 default: true,
112 restartNeeded: false
113 },
114 showCustom: {
115 type: OptionType.BOOLEAN,
116 description: "Show Custom Badges",
117 default: true,
118 restartNeeded: false
119 }
120 }
121});
122