diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs
index ee50710e8e..fa122f267e 100644
--- a/src/Discord.Net.Core/Entities/Roles/Color.cs
+++ b/src/Discord.Net.Core/Entities/Roles/Color.cs
@@ -200,6 +200,94 @@ public static implicit operator Color(uint rawValue)
public static implicit operator uint(Color color)
=> color.RawValue;
+ ///
+ /// Converts the string representation of a color to a Color object.
+ ///
+ /// String to be parsed to a color
+ /// Color format of the string
+ /// A Color object with a color value parsed from a string
+ /// The argument is not a number or of incorrect length
+ public static Color Parse(string rawValue, ColorType colorType = ColorType.CssHexColor)
+ {
+ if (TryParse(rawValue, out var color, colorType))
+ return color;
+
+ throw new ArgumentOutOfRangeException(nameof(rawValue), "Value must be a number of valid length - matching the specified ColorType");
+ }
+
+ ///
+ /// Converts the string representation of a color to a Color object. The return value indicates whether the conversion succeeded.
+ ///
+ /// String to be parsed to a color
+ /// When this method returns true, contains a Color that represents the parsed string.
+ /// Color format of the string
+ /// true if the conversion succeeded; false otherwise.
+ public static bool TryParse(string rawValue, out Color color, ColorType colorType = ColorType.CssHexColor)
+ {
+ color = new Color();
+
+ if (string.IsNullOrWhiteSpace(rawValue))
+ return false;
+
+ rawValue = rawValue.TrimStart('#');
+
+ if (rawValue.StartsWith("0x"))
+ rawValue = rawValue.Substring(2);
+
+ if (!uint.TryParse(rawValue, System.Globalization.NumberStyles.HexNumber, null, out var parsedValue))
+ return false;
+
+ uint r;
+ uint g;
+ uint b;
+ uint fullHex;
+
+ switch (rawValue.Length, colorType)
+ {
+ case (3, ColorType.CssHexColor):
+ r = parsedValue >> 8;
+ g = (parsedValue & 0xF0) >> 4;
+ b = parsedValue & 0xF;
+
+ r = (r << 4) | r;
+ g = (g << 4) | g;
+ b = (b << 4) | b;
+
+ fullHex = (r << 16) | (g << 8) | b;
+
+ break;
+
+ case (4, ColorType.CssHexColor):
+ r = (parsedValue & 0xF00) >> 8;
+ g = (parsedValue & 0xF0) >> 4;
+ b = parsedValue & 0xF;
+
+ r = (r << 4) | r;
+ g = (g << 4) | g;
+ b = (b << 4) | b;
+
+ fullHex = (r << 16) | (g << 8) | b;
+
+ break;
+
+ case (6, ColorType.CssHexColor):
+ color = new Color(parsedValue);
+ return true;
+
+ case (8, ColorType.CssHexColor):
+ parsedValue &= 0xFFFFFF;
+ color = new Color(parsedValue);
+ return true;
+
+ default:
+ return false;
+ }
+
+ color = new Color(fullHex);
+
+ return true;
+ }
+
public override bool Equals(object obj)
=> obj is Color c && RawValue == c.RawValue;
diff --git a/src/Discord.Net.Core/Entities/Roles/ColorType.cs b/src/Discord.Net.Core/Entities/Roles/ColorType.cs
new file mode 100644
index 0000000000..a0b594ec82
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Roles/ColorType.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Discord;
+
+public enum ColorType
+{
+ ///
+ /// Color in one of the following formats.
+ ///
+ /// #RGB The three-value syntax
+ ///
+ ///
+ /// #RGBA The four-value syntax
+ ///
+ ///
+ /// #RRGGBB The six-value syntax
+ ///
+ /// #RRGGBBAA The eight-value syntax
+ ///
+ CssHexColor = 0
+}
diff --git a/test/Discord.Net.Tests.Unit/ColorTests.cs b/test/Discord.Net.Tests.Unit/ColorTests.cs
index 48a6041e5b..6259238e97 100644
--- a/test/Discord.Net.Tests.Unit/ColorTests.cs
+++ b/test/Discord.Net.Tests.Unit/ColorTests.cs
@@ -74,6 +74,26 @@ public void Color_FromRgb_Float_OutOfRange()
Assert.Throws(() => new Color(2.0f, 2.0f, 2.0f));
}
[Fact]
+ public void Color_FromRgb_String_CssHexColor()
+ {
+ Assert.Equal(0xFF0000u, Color.Parse("#F00", ColorType.CssHexColor).RawValue);
+ Assert.Equal(0x22BB44u, Color.Parse("#2B4", ColorType.CssHexColor).RawValue);
+ Assert.Equal(0xAABBAAu, Color.Parse("FABA", ColorType.CssHexColor).RawValue);
+ Assert.Equal(0x00F672u, Color.Parse("00F672", ColorType.CssHexColor).RawValue);
+ Assert.Equal(0x257777u, Color.Parse("0xFF257777", ColorType.CssHexColor).RawValue);
+ }
+ [Fact]
+ public void Color_FromRgb_String_Invalid()
+ {
+ Assert.Throws(() => Color.Parse(" ", ColorType.CssHexColor));
+ Assert.Throws(() => Color.Parse(null, ColorType.CssHexColor));
+ Assert.Throws(() => Color.Parse("#F"));
+ Assert.Throws(() => Color.Parse("F0", ColorType.CssHexColor));
+ Assert.Throws(() => Color.Parse("FF000"));
+ Assert.Throws(() => Color.Parse("FF00000"));
+ Assert.Throws(() => Color.Parse("FF0000000"));
+ }
+ [Fact]
public void Color_Red()
{
Assert.Equal(0xAF, new Color(0xAF1390).R);