Plugin

IrcColors

Makes username colors in chat unique, like in IRC clients

Appearance Customisation
index.ts
Download

Source

src/plugins/ircColors/index.ts
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { definePluginSettings } from "@api/Settings";
8import { hash as h64 } from "@intrnl/xxhash64";
9import { Devs } from "@utils/constants";
10import definePlugin, { OptionType } from "@utils/types";
11import { useMemo } from "@webpack/common";
12
13// Calculate a CSS color string based on the user ID
14function calculateNameColorForUser(id?: string) {
15 const { lightness } = settings.use(["lightness"]);
16 const idHash = useMemo(() => id ? h64(id) : null, [id]);
17
18 return idHash && `hsl(${idHash % 360n}, 100%, ${lightness}%)`;
19}
20
21const settings = definePluginSettings({
22 lightness: {
23 description: "Lightness, in %. Change if the colors are too light or too dark",
24 type: OptionType.NUMBER,
25 default: 70,
26 },
27 memberListColors: {
28 description: "Replace role colors in the member list",
29 restartNeeded: true,
30 type: OptionType.BOOLEAN,
31 default: true
32 },
33 applyColorOnlyToUsersWithoutColor: {
34 description: "Apply colors only to users who don't have a predefined color",
35 restartNeeded: false,
36 type: OptionType.BOOLEAN,
37 default: false
38 },
39 applyColorOnlyInDms: {
40 displayName: "Apply Color Only In DMs",
41 description: "Apply colors only in direct messages; do not apply colors in servers.",
42 restartNeeded: false,
43 type: OptionType.BOOLEAN,
44 default: false
45 }
46});
47
48export default definePlugin({
49 name: "IrcColors",
50 description: "Makes username colors in chat unique, like in IRC clients",
51 tags: ["Appearance", "Customisation"],
52 authors: [Devs.Grzesiek11, Devs.jamesbt365],
53 settings,
54
55 patches: [
56 {
57 find: '="SYSTEM_TAG"',
58 replacement: {
59 // Override colorString with our custom color and disable gradients if applying the custom color.
60 match: /(?<=colorString:\i,colorStrings:\i,colorRoleName:\i.*?}=)(\i),/,
61 replace: "$self.wrapMessageColorProps($1, arguments[0]),"
62 }
63 },
64 {
65 find: "#{intl::GUILD_OWNER}),children:",
66 replacement: {
67 match: /(?<=roleName:\i,)colorString:/,
68 replace: "colorString:$self.calculateNameColorForListContext(arguments[0]),originalColor:"
69 },
70 predicate: () => settings.store.memberListColors
71 }
72 ],
73
74 wrapMessageColorProps(colorProps: { colorString: string, colorStrings?: Record<"primaryColor" | "secondaryColor" | "tertiaryColor", string>; }, context: any) {
75 try {
76 const colorString = this.calculateNameColorForMessageContext(context);
77 if (colorString === colorProps.colorString) {
78 return colorProps;
79 }
80
81 return {
82 ...colorProps,
83 colorString,
84 colorStrings: colorProps.colorStrings && {
85 primaryColor: colorString,
86 secondaryColor: undefined,
87 tertiaryColor: undefined
88 }
89 };
90 } catch (e) {
91 console.error("Failed to calculate message color strings:", e);
92 return colorProps;
93 }
94 },
95
96 calculateNameColorForMessageContext(context: any) {
97 const userId: string | undefined = context?.message?.author?.id;
98 const colorString = context?.author?.colorString;
99 const color = calculateNameColorForUser(userId);
100
101 // Color preview in role settings
102 if (context?.message?.channel_id === "1337" && userId === "313337")
103 return colorString;
104
105 if (settings.store.applyColorOnlyInDms && !context?.channel?.isPrivate()) {
106 return colorString;
107 }
108
109 return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
110 ? color
111 : colorString;
112 },
113
114 calculateNameColorForListContext(context: any) {
115 try {
116 const id = context?.user?.id;
117 const colorString = context?.colorString;
118 const color = calculateNameColorForUser(id);
119
120 if (settings.store.applyColorOnlyInDms && context?.guildId !== undefined) {
121 return colorString;
122 }
123
124 return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
125 ? color
126 : colorString;
127 } catch (e) {
128 console.error("Failed to calculate name color for list context:", e);
129 }
130 }
131});
132