Plugin
USRBG
Displays user banners from USRBG, allowing anyone to get a banner without Nitro
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import { definePluginSettings } from "@api/Settings";8
import { LinkButton } from "@components/Button";9
import { Devs } from "@utils/constants";10
import definePlugin, { OptionType } from "@utils/types";11
12
const API_URL = "https:class="ts-cmt">//usrbg.is-hardly.online/users";13
14
interface UsrbgApiReturn {15
endpoint: string;16
bucket: string;17
prefix: string;18
users: Record<string, string>;19
}20
21
const settings = definePluginSettings({22
nitroFirst: {23
description: "Banner to use if both Nitro and USRBG banners are present",24
type: OptionType.SELECT,25
options: [26
{ label: "Nitro banner", value: true, default: true },27
{ label: "USRBG banner", value: false },28
]29
},30
voiceBackground: {31
description: "Use USRBG banners as voice chat backgrounds",32
type: OptionType.BOOLEAN,33
default: true,34
restartNeeded: true35
}36
});37
38
export default definePlugin({39
name: "USRBG",40
description: "Displays user banners from USRBG, allowing anyone to get a banner without Nitro",41
tags: ["Appearance", "Customisation"],42
authors: [Devs.AutumnVN, Devs.katlyn, Devs.pylix, Devs.TheKodeToad],43
settings,44
patches: [45
{46
find: 039;:"SHOULD_LOAD");039;,47
replacement: {48
match: /\i(?:\?)?.getPreviewBanner\(\i,\i,\i\)(?=.{0,100}"COMPLETE")/,49
replace: "$self.patchBannerUrl(arguments[0])||$&"50
51
}52
},53
{54
find: "\"data-selenium-video-tile\":",55
predicate: () => settings.store.voiceBackground,56
replacement: [57
{58
match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/,59
replace: "$1.style=$self.getVoiceBackgroundStyles($1);"60
}61
]62
},63
{64
find: 039;"VideoBackground-web"039;,65
predicate: () => settings.store.voiceBackground,66
replacement: {67
match: /backgroundColor:.{0,25},\{style:(?=\i\?)/,68
replace: "$&$self.userHasBackground(arguments[0]?.userId)?null:",69
}70
}71
],72
73
data: null as UsrbgApiReturn | null,74
75
settingsAboutComponent: () => {76
return (77
<LinkButton href="https:class="ts-cmt">//github.com/AutumnVN/usrbg#how-to-request-your-own-usrbg-banner" variant="primary">78
Get your own USRBG banner79
</LinkButton>80
);81
},82
83
getVoiceBackgroundStyles({ className, participantUserId }: any) {84
if (className.includes("tile")) {85
if (this.userHasBackground(participantUserId)) {86
return {87
backgroundImage: `url(${this.getImageUrl(participantUserId)})`,88
backgroundSize: "cover",89
backgroundPosition: "center",90
backgroundRepeat: "no-repeat"91
};92
}93
}94
},95
96
patchBannerUrl({ displayProfile }: any) {97
if (displayProfile?.banner && settings.store.nitroFirst) return;98
if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId);99
},100
101
userHasBackground(userId: string) {102
return !!this.data?.users[userId];103
},104
105
getImageUrl(userId: string): string | null {106
if (!this.userHasBackground(userId)) return null;107
108
// We can assert that data exists because userHasBackground returned true109
const { endpoint, bucket, prefix, users: { [userId]: etag } } = this.data!;110
return `${endpoint}/${bucket}/${prefix}${userId}?${etag}`;111
},112
113
async start() {114
const res = await fetch(API_URL);115
if (res.ok) {116
this.data = await res.json();117
}118
}119
});120