diff --git a/src/api/Badges.ts b/src/api/Badges.ts
new file mode 100644
index 00000000..542f78d8
--- /dev/null
+++ b/src/api/Badges.ts
@@ -0,0 +1,100 @@
+/*
+ * 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 { User } from "discord-types/general";
+import { HTMLProps } from "react";
+
+export enum BadgePosition {
+ START,
+ END
+}
+
+export interface ProfileBadge {
+ /** The tooltip to show on hover */
+ tooltip: string;
+ /** The custom image to use */
+ image?: string;
+ /** Action to perform when you click the badge */
+ onClick?(): void;
+ /** Should the user display this badge? */
+ shouldShow?(userInfo: BadgeUserArgs): boolean;
+ /** Optional props (e.g. style) for the badge */
+ props?: HTMLProps;
+ /** Insert at start or end? */
+ position?: BadgePosition;
+
+ /** The badge name to display. Discord uses this, but we don't. */
+ key?: string;
+}
+
+const Badges = new Set();
+
+/**
+ * Register a new badge with the Badges API
+ * @param badge The badge to register
+ */
+export function addBadge(badge: ProfileBadge) {
+ Badges.add(badge);
+}
+
+/**
+ * Unregister a badge from the Badges API
+ * @param badge The badge to remove
+ */
+export function removeBadge(badge: ProfileBadge) {
+ return Badges.delete(badge);
+}
+
+/**
+ * Inject badges into the profile badges array.
+ * You probably don't need to use this.
+ */
+export function inject(badgeArray: ProfileBadge[], args: BadgeUserArgs) {
+ for (const badge of Badges) {
+ if (!badge.shouldShow || badge.shouldShow(args)) {
+ badge.position === BadgePosition.START
+ ? badgeArray.unshift(badge)
+ : badgeArray.push(badge);
+ }
+ }
+ return badgeArray;
+}
+
+export interface BadgeUserArgs {
+ user: User;
+ profile: Profile;
+ premiumSince: Date;
+ premiumGuildSince?: Date;
+}
+
+interface ConnectedAccount {
+ type: string;
+ id: string;
+ name: string;
+ verified: boolean;
+}
+
+interface Profile {
+ connectedAccounts: ConnectedAccount[];
+ premiumType: number;
+ premiumSince: string;
+ premiumGuildSince?: any;
+ lastFetched: number;
+ profileFetchFailed: boolean;
+ application?: any;
+}
diff --git a/src/api/index.ts b/src/api/index.ts
index 73dafc93..bde4b3e7 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+import * as $Badges from "./Badges";
import * as $Commands from "./Commands";
import * as $DataStore from "./DataStore";
import * as $MessageAccessories from "./MessageAccessories";
@@ -57,5 +58,9 @@ const DataStore = $DataStore;
* An API allowing you to add custom components as message accessories
*/
const MessageAccessories = $MessageAccessories;
+/**
+ * An API allowing you to add badges to user profiles
+ */
+const Badges = $Badges;
-export { Commands,DataStore, MessageAccessories, MessageEvents, Notices };
+export { Badges, Commands, DataStore, MessageAccessories, MessageEvents, Notices };
diff --git a/src/plugins/apiBadges.tsx b/src/plugins/apiBadges.tsx
new file mode 100644
index 00000000..e26a7a1b
--- /dev/null
+++ b/src/plugins/apiBadges.tsx
@@ -0,0 +1,70 @@
+/*
+ * 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 { BadgePosition, ProfileBadge } from "../api/Badges";
+import { Devs } from "../utils/constants";
+import IpcEvents from "../utils/IpcEvents";
+import definePlugin from "../utils/types";
+
+const CONTRIBUTOR_BADGE = "https://media.discordapp.net/stickers/1026517526106087454.webp";
+
+/** List of vencord contributor IDs */
+const contributorIds: string[] = Object.values(Devs).map(d => d.id.toString());
+
+const ContributorBadge: ProfileBadge = {
+ tooltip: "Vencord Contributor",
+ image: CONTRIBUTOR_BADGE,
+ position: BadgePosition.START,
+ props: {
+ style: {
+ borderRadius: "50%",
+ transform: "scale(0.9)" // The image is a bit too big compared to default badges
+ }
+ },
+ shouldShow: ({ user }) => contributorIds.includes(user.id),
+ onClick: () => VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, "https://github.com/Vendicated/Vencord")
+};
+
+export default definePlugin({
+ name: "BadgeAPI",
+ description: "API to add badges to users.",
+ authors: [Devs.Megu],
+ required: true,
+ patches: [
+ /* Patch the badges array */
+ {
+ find: "PREMIUM_GUILD_SUBSCRIPTION_TOOLTIP.format({date:",
+ replacement: {
+ match: /&&((\w{1,3})\.push\({tooltip:\w{1,3}\.\w{1,3}\.Messages\.PREMIUM_GUILD_SUBSCRIPTION_TOOLTIP\.format.+?;)(?:return\s\w{1,3};?})/,
+ replace: (_, m, badgeArray) => `&&${m} return Vencord.Api.Badges.inject(${badgeArray}, arguments[0]);}`,
+ }
+ },
+ /* Patch the badge list component on user profiles */
+ {
+ find: "Messages.PROFILE_USER_BADGES,role:",
+ replacement: {
+ match: /src:(\w{1,3})\[(\w{1,3})\.key\],/,
+ //
+ replace: (_, imageMap, badge) => `src: ${badge}.image ?? ${imageMap}[${badge}.key], ...${badge}.props,`
+ }
+ }
+ ],
+ start() {
+ Vencord.Api.Badges.addBadge(ContributorBadge);
+ },
+});
diff --git a/src/plugins/banger.ts b/src/plugins/banger.ts
index 59d9b421..62632248 100644
--- a/src/plugins/banger.ts
+++ b/src/plugins/banger.ts
@@ -22,13 +22,7 @@ import definePlugin, { OptionType } from "../utils/types";
export default definePlugin({
name: "BANger",
description: "Replaces the GIF in the ban dialogue with a custom one.",
- authors: [
- {
- name: "Xinto",
- id: 423915768191647755n
- },
- Devs.Glitch
- ],
+ authors: [Devs.Xinto, Devs.Glitch],
patches: [
{
find: "BAN_CONFIRM_TITLE.",
diff --git a/src/plugins/clearURLs/index.ts b/src/plugins/clearURLs/index.ts
index be0b8029..b01faedb 100644
--- a/src/plugins/clearURLs/index.ts
+++ b/src/plugins/clearURLs/index.ts
@@ -23,6 +23,7 @@ import {
removePreEditListener,
removePreSendListener,
} from "../../api/MessageEvents";
+import { Devs } from "../../utils/constants";
import definePlugin from "../../utils/types";
import { defaultRules } from "./defaultRules";
@@ -33,12 +34,7 @@ const reHasRegExpChar = RegExp(reRegExpChar.source);
export default definePlugin({
name: "clearURLs",
description: "Removes tracking garbage from URLs",
- authors: [
- {
- name: "adryd",
- id: 0n,
- },
- ],
+ authors: [Devs.adryd],
dependencies: ["MessageEventsAPI"],
escapeRegExp(str: string) {
diff --git a/src/plugins/experiments.tsx b/src/plugins/experiments.tsx
index 861bb051..4f83c5a2 100644
--- a/src/plugins/experiments.tsx
+++ b/src/plugins/experiments.tsx
@@ -31,7 +31,7 @@ export default definePlugin({
Devs.Megu,
Devs.Ven,
Devs.Nickyux,
- { name: "BanTheNons", id: 460478012794863637n },
+ Devs.BanTheNons
],
description: "Enable Access to Experiments in Discord!",
patches: [{
diff --git a/src/plugins/iLoveSpam.ts b/src/plugins/iLoveSpam.ts
index 17753269..7f390f34 100644
--- a/src/plugins/iLoveSpam.ts
+++ b/src/plugins/iLoveSpam.ts
@@ -22,10 +22,7 @@ import definePlugin from "../utils/types";
export default definePlugin({
name: "iLoveSpam",
description: "Do not hide messages from 'likely spammers'",
- authors: [
- Devs.botato,
- Devs.Animal,
- ],
+ authors: [Devs.botato, Devs.Animal],
patches: [
{
find: "),{hasFlag:",
diff --git a/src/plugins/moarKaomojis.ts b/src/plugins/moarKaomojis.ts
index 839be6a9..8b3c0c58 100644
--- a/src/plugins/moarKaomojis.ts
+++ b/src/plugins/moarKaomojis.ts
@@ -16,18 +16,14 @@
* along with this program. If not, see .
*/
-import { findOption,OptionalMessageOption } from "../api/Commands";
+import { findOption, OptionalMessageOption } from "../api/Commands";
+import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
export default definePlugin({
name: "moarKaomojis",
description: "Adds more Kaomojis to discord. ヽ(´▽`)/",
- authors: [
- {
- name: "Jacob.Tm",
- id: 302872992097107991n
- }
- ],
+ authors: [Devs.JacobTm],
dependencies: ["CommandsAPI"],
commands: [
{ name: "dissatisfaction", description: " >﹏<" },
diff --git a/src/plugins/moreCommands.ts b/src/plugins/moreCommands.ts
index 074e7159..0a23aafb 100644
--- a/src/plugins/moreCommands.ts
+++ b/src/plugins/moreCommands.ts
@@ -32,14 +32,7 @@ function mock(input: string): string {
export default definePlugin({
name: "MoreCommands",
description: "echo, lenny, mock",
- authors: [
- Devs.Arjix,
- Devs.echo,
- {
- name: "ICodeInAssembly",
- id: 702973430449832038n
- }
- ],
+ authors: [Devs.Arjix, Devs.echo, Devs.Samu],
dependencies: ["CommandsAPI"],
commands: [
{
diff --git a/src/plugins/nitroBypass.ts b/src/plugins/nitroBypass.ts
index 937b8d05..dfdfe9d2 100644
--- a/src/plugins/nitroBypass.ts
+++ b/src/plugins/nitroBypass.ts
@@ -52,11 +52,7 @@ interface StickerPack {
export default definePlugin({
name: "NitroBypass",
- authors: [
- Devs.Arjix,
- Devs.D3SOX,
- Devs.Ven
- ],
+ authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven],
description: "Allows you to stream in nitro quality and send fake emojis/stickers.",
dependencies: ["MessageEventsAPI"],
diff --git a/src/plugins/noCanaryMessageLinks.ts b/src/plugins/noCanaryMessageLinks.ts
index 807c8643..67c91762 100644
--- a/src/plugins/noCanaryMessageLinks.ts
+++ b/src/plugins/noCanaryMessageLinks.ts
@@ -24,10 +24,7 @@ import { Settings } from "../Vencord";
export default definePlugin({
name: "NoCanaryMessageLinks",
description: "Allows you to change/remove the subdomain of discord message and channel links",
- authors: [
- Devs.Samu,
- Devs.nea,
- ],
+ authors: [Devs.Samu, Devs.nea],
options: {
linkPrefix: {
description: "The subdomain for your discord message links",
diff --git a/src/plugins/noReplyMention.ts b/src/plugins/noReplyMention.ts
index d516450a..2e5e9695 100644
--- a/src/plugins/noReplyMention.ts
+++ b/src/plugins/noReplyMention.ts
@@ -16,15 +16,13 @@
* along with this program. If not, see .
*/
+import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
export default definePlugin({
name: "NoReplyMention",
description: "Disables reply pings by default",
- authors: [{
- name: "DustyAngel47",
- id: 714583473804935238n
- }],
+ authors: [Devs.DustyAngel47],
patches: [
{
find: "CREATE_PENDING_REPLY:function",
diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts
index 802ede9c..39637e2e 100644
--- a/src/plugins/pronoundb/index.ts
+++ b/src/plugins/pronoundb/index.ts
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+import { Devs } from "../../utils/constants";
import definePlugin, { OptionType } from "../../utils/types";
import PronounsAboutComponent from "./components/PronounsAboutComponent";
import PronounsChatComponent from "./components/PronounsChatComponent";
@@ -28,10 +29,7 @@ export enum PronounsFormat {
export default definePlugin({
name: "PronounDB",
- authors: [{
- name: "Tyman",
- id: 487443883127472129n
- }],
+ authors: [Devs.Tyman],
description: "Adds pronouns to user messages using pronoundb",
patches: [
// Patch the chat timestamp element
diff --git a/src/plugins/showHiddenChannels.tsx b/src/plugins/showHiddenChannels.tsx
index 7f2c4b80..8dc92bfe 100644
--- a/src/plugins/showHiddenChannels.tsx
+++ b/src/plugins/showHiddenChannels.tsx
@@ -34,17 +34,7 @@ waitFor(m => m.can && m.initialize, m => ({ can } = m));
export default definePlugin({
name: "ShowHiddenChannels",
description: "Show hidden channels",
- authors: [
- {
- name: "BigDuck",
- id: 1024588272623681609n
- },
- {
- name: "Average React Enjoyer",
- id: 1004904120056029256n
- },
- Devs.D3SOX,
- ],
+ authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX],
options: {
hideUnreads: {
description: "Hide unreads",
diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx
index 6bf66253..e8b53702 100644
--- a/src/plugins/spotifyControls/index.tsx
+++ b/src/plugins/spotifyControls/index.tsx
@@ -23,17 +23,7 @@ import { Player } from "./PlayerComponent";
export default definePlugin({
name: "SpotifyControls",
description: "Spotify Controls",
- authors: [
- Devs.Ven,
- {
- id: 420043923822608384n,
- name: "afn",
- },
- {
- id: 379304073515499530n,
- name: "KraXen72"
- }
- ],
+ authors: [Devs.Ven, Devs.afn, Devs.KraXen72],
dependencies: ["MenuItemDeobfuscatorApi"],
patches: [
{
diff --git a/src/plugins/uwuify.ts b/src/plugins/uwuify.ts
index 9f4ac7e7..804c7417 100644
--- a/src/plugins/uwuify.ts
+++ b/src/plugins/uwuify.ts
@@ -17,6 +17,7 @@
*/
import { findOption, RequiredMessageOption } from "../api/Commands";
+import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
const endings = [
@@ -109,10 +110,7 @@ function uwuify(message: string): string {
export default definePlugin({
name: "UwUifier",
description: "Simply uwuify commands",
- authors: [{
- name: "ECHO",
- id: 712639419785412668n
- }],
+ authors: [Devs.echo],
dependencies: ["CommandsAPI"],
commands: [
diff --git a/src/plugins/vcDoubleClick.ts b/src/plugins/vcDoubleClick.ts
index 554b31a2..b1a05581 100644
--- a/src/plugins/vcDoubleClick.ts
+++ b/src/plugins/vcDoubleClick.ts
@@ -28,10 +28,7 @@ const timers = {} as Record