Plugin
IrcColors
Makes username colors in chat unique, like in IRC clients
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 { hash as h64 } from "@intrnl/xxhash64";9
import { Devs } from "@utils/constants";10
import definePlugin, { OptionType } from "@utils/types";11
import { useMemo } from "@webpack/common";12
13
// Calculate a CSS color string based on the user ID14
function 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
21
const 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: true32
},33
applyColorOnlyToUsersWithoutColor: {34
description: "Apply colors only to users who don039;t have a predefined color",35
restartNeeded: false,36
type: OptionType.BOOLEAN,37
default: false38
},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: false45
}46
});47
48
export 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: 039;="SYSTEM_TAG"039;,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.memberListColors71
}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: undefined88
}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 settings102
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
? color111
: 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
? color126
: colorString;127
} catch (e) {128
console.error("Failed to calculate name color for list context:", e);129
}130
}131
});132