diff --git a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
index 669686b2a..fb4a2e434 100644
--- a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
+++ b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
@@ -1,578 +1,578 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using DisCatSharp.Common.RegularExpressions;
using DisCatSharp.Entities;
namespace DisCatSharp.CommandsNext.Converters
{
///
/// Represents a discord user converter.
///
public class DiscordUserConverter : IArgumentConverter
{
///
/// Gets the user regex.
///
private static Regex UserRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordUserConverter()
{
#if NETSTANDARD1_3
UserRegex = new Regex(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript);
#else
- UserRegex = new Regex(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ UserRegex = DiscordRegEx.User;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
{
var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var m = UserRegex.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
{
var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var di = value.IndexOf('#');
var un = di != -1 ? value.Substring(0, di) : value;
var dv = di != -1 ? value.Substring(di + 1) : null;
var us = ctx.Client.Guilds.Values
.SelectMany(xkvp => xkvp.Members.Values)
.Where(xm => (cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null));
var usr = us.FirstOrDefault();
return usr != null ? Optional.FromValue(usr) : Optional.FromNoValue();
}
}
///
/// Represents a discord member converter.
///
public class DiscordMemberConverter : IArgumentConverter
{
///
/// Gets the user regex.
///
private static Regex UserRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordMemberConverter()
{
#if NETSTANDARD1_3
UserRegex = new Regex(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript);
#else
- UserRegex = new Regex(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ UserRegex = DiscordRegEx.User;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ctx.Guild == null)
return Optional.FromNoValue();
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
{
var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var m = UserRegex.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
{
var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var searchResult = await ctx.Guild.SearchMembersAsync(value).ConfigureAwait(false);
if (searchResult.Any())
return Optional.FromValue(searchResult.First());
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var di = value.IndexOf('#');
var un = di != -1 ? value.Substring(0, di) : value;
var dv = di != -1 ? value.Substring(di + 1) : null;
var us = ctx.Guild.Members.Values
.Where(xm => ((cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null))
|| (cs ? xm.Nickname : xm.Nickname?.ToLowerInvariant()) == value);
var mbr = us.FirstOrDefault();
return mbr != null ? Optional.FromValue(mbr) : Optional.FromNoValue();
}
}
///
/// Represents a discord channel converter.
///
public class DiscordChannelConverter : IArgumentConverter
{
///
/// Gets the channel regex.
///
private static Regex ChannelRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordChannelConverter()
{
#if NETSTANDARD1_3
ChannelRegex = new Regex(@"^<#(\d+)>$", RegexOptions.ECMAScript);
#else
- ChannelRegex = new Regex(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ ChannelRegex = DiscordRegEx.Channel;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid))
{
var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var m = ChannelRegex.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out cid))
{
var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var chn = ctx.Guild?.Channels.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
return chn != null ? Optional.FromValue(chn) : Optional.FromNoValue();
}
}
///
/// Represents a discord thread channel converter.
///
public class DiscordThreadChannelConverter : IArgumentConverter
{
///
/// Gets the channel regex.
///
private static Regex ChannelRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordThreadChannelConverter()
{
#if NETSTANDARD1_3
ChannelRegex = new Regex(@"^<#(\d+)>$", RegexOptions.ECMAScript);
#else
- ChannelRegex = new Regex(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ ChannelRegex = DiscordRegEx.Channel;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var tid))
{
var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var m = ChannelRegex.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid))
{
var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var tchn = ctx.Guild?.Threads.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
return tchn != null ? Optional.FromValue(tchn) : Optional.FromNoValue();
}
}
///
/// Represents a discord role converter.
///
public class DiscordRoleConverter : IArgumentConverter
{
///
/// Gets the role regex.
///
private static Regex RoleRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordRoleConverter()
{
#if NETSTANDARD1_3
RoleRegex = new Regex(@"^<@&(\d+?)>$", RegexOptions.ECMAScript);
#else
- RoleRegex = new Regex(@"^<@&(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ RoleRegex = DiscordRegEx.Role;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ctx.Guild == null)
return Task.FromResult(Optional.FromNoValue());
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rid))
{
var result = ctx.Guild.GetRole(rid);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return Task.FromResult(ret);
}
var m = RoleRegex.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out rid))
{
var result = ctx.Guild.GetRole(rid);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return Task.FromResult(ret);
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var rol = ctx.Guild.Roles.Values.FirstOrDefault(xr => (cs ? xr.Name : xr.Name.ToLowerInvariant()) == value);
return Task.FromResult(rol != null ? Optional.FromValue(rol) : Optional.FromNoValue());
}
}
///
/// Represents a discord guild converter.
///
public class DiscordGuildConverter : IArgumentConverter
{
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var gid))
{
return ctx.Client.Guilds.TryGetValue(gid, out var result)
? Task.FromResult(Optional.FromValue(result))
: Task.FromResult(Optional.FromNoValue());
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value?.ToLowerInvariant();
var gld = ctx.Client.Guilds.Values.FirstOrDefault(xg => (cs ? xg.Name : xg.Name.ToLowerInvariant()) == value);
return Task.FromResult(gld != null ? Optional.FromValue(gld) : Optional.FromNoValue());
}
}
///
/// Represents a discord invite converter.
///
public class DiscordInviteConverter : IArgumentConverter
{
///
/// Gets the invite regex.
///
private static Regex InviteRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordInviteConverter()
{
#if NETSTANDARD1_3
InviteRegex = new Regex(@"^(https?:\/\/)?(www\.)?(discord\.(gg|io|me|li)|discordapp\.com\/invite)\/(.+[a-z])$", RegexOptions.ECMAScript);
#else
- InviteRegex = new Regex(@"^(https?:\/\/)?(www\.)?(discord\.(gg|io|me|li)|discordapp\.com\/invite)\/(.+[a-z])$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ InviteRegex = DiscordRegEx.Invite;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
var m = InviteRegex.Match(value);
if (m.Success)
{
var result = await ctx.Client.GetInviteByCodeAsync(m.Groups[5].Value).ConfigureAwait(false);
var ret = result != null ? Optional.FromValue(result) : Optional.FromNoValue();
return ret;
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value?.ToLowerInvariant();
var inv = await ctx.Client.GetInviteByCodeAsync(value);
return inv != null ? Optional.FromValue(inv) : Optional.FromNoValue();
}
}
///
/// Represents a discord message converter.
///
public class DiscordMessageConverter : IArgumentConverter
{
///
/// Gets the message path regex.
///
private static Regex MessagePathRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordMessageConverter()
{
#if NETSTANDARD1_3
MessagePathRegex = new Regex(@"^\/channels\/(?(?:\d+|@me))\/(?\d+)\/(?\d+)\/?$", RegexOptions.ECMAScript);
#else
- MessagePathRegex = new Regex(@"^\/channels\/(?(?:\d+|@me))\/(?\d+)\/(?\d+)\/?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ MessagePathRegex = DiscordRegEx.MessageLink;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (string.IsNullOrWhiteSpace(value))
return Optional.FromNoValue();
var msguri = value.StartsWith("<") && value.EndsWith(">") ? value.Substring(1, value.Length - 2) : value;
ulong mid;
if (Uri.TryCreate(msguri, UriKind.Absolute, out var uri))
{
if (uri.Host != "discordapp.com" && uri.Host != "discord.com" && !uri.Host.EndsWith(".discordapp.com") && !uri.Host.EndsWith(".discord.com"))
return Optional.FromNoValue();
var uripath = MessagePathRegex.Match(uri.AbsolutePath);
if (!uripath.Success
|| !ulong.TryParse(uripath.Groups["channel"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid)
|| !ulong.TryParse(uripath.Groups["message"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
return Optional.FromNoValue();
var chn = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
if (chn == null)
return Optional.FromNoValue();
var msg = await chn.GetMessageAsync(mid).ConfigureAwait(false);
return msg != null ? Optional.FromValue(msg) : Optional.FromNoValue();
}
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
{
var result = await ctx.Channel.GetMessageAsync(mid).ConfigureAwait(false);
return result != null ? Optional.FromValue(result) : Optional.FromNoValue();
}
return Optional.FromNoValue();
}
}
///
/// Represents a discord emoji converter.
///
public class DiscordEmojiConverter : IArgumentConverter
{
///
/// Gets the emote regex.
///
private static Regex EmoteRegex { get; }
///
/// Initializes a new instance of the class.
///
static DiscordEmojiConverter()
{
#if NETSTANDARD1_3
EmoteRegex = new Regex(@"^$", RegexOptions.ECMAScript);
#else
- EmoteRegex = new Regex(@"^<(?a)?:(?[a-zA-Z0-9_]+?):(?\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ EmoteRegex = DiscordRegEx.Emoji;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (DiscordEmoji.TryFromUnicode(ctx.Client, value, out var emoji))
{
var result = emoji;
var ret = Optional.FromValue(result);
return Task.FromResult(ret);
}
var m = EmoteRegex.Match(value);
if (m.Success)
{
var sid = m.Groups["id"].Value;
var name = m.Groups["name"].Value;
var anim = m.Groups["animated"].Success;
return !ulong.TryParse(sid, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id)
? Task.FromResult(Optional.FromNoValue())
: DiscordEmoji.TryFromGuildEmote(ctx.Client, id, out emoji)
? Task.FromResult(Optional.FromValue(emoji))
: Task.FromResult(Optional.FromValue(new DiscordEmoji
{
Discord = ctx.Client,
Id = id,
Name = name,
IsAnimated = anim,
RequiresColons = true,
IsManaged = false
}));
}
return Task.FromResult(Optional.FromNoValue());
}
}
///
/// Represents a discord color converter.
///
public class DiscordColorConverter : IArgumentConverter
{
///
/// Gets the color regex hex.
///
private static Regex ColorRegexHex { get; }
///
/// Gets the color regex rgb.
///
private static Regex ColorRegexRgb { get; }
///
/// Initializes a new instance of the class.
///
static DiscordColorConverter()
{
#if NETSTANDARD1_3
ColorRegexHex = new Regex(@"^#?([a-fA-F0-9]{6})$", RegexOptions.ECMAScript);
ColorRegexRgb = new Regex(@"^(\d{1,3})\s*?,\s*?(\d{1,3}),\s*?(\d{1,3})$", RegexOptions.ECMAScript);
#else
- ColorRegexHex = new Regex(@"^#?([a-fA-F0-9]{6})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
- ColorRegexRgb = new Regex(@"^(\d{1,3})\s*?,\s*?(\d{1,3}),\s*?(\d{1,3})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ ColorRegexHex = DiscordRegEx.HexColorString;
+ ColorRegexRgb = DiscordRegEx.RgbColorString;
#endif
}
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
var m = ColorRegexHex.Match(value);
if (m.Success && int.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clr))
return Task.FromResult(Optional.FromValue(clr));
m = ColorRegexRgb.Match(value);
if (m.Success)
{
var p1 = byte.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var r);
var p2 = byte.TryParse(m.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var g);
var p3 = byte.TryParse(m.Groups[3].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var b);
return !(p1 && p2 && p3)
? Task.FromResult(Optional.FromNoValue())
: Task.FromResult(Optional.FromValue(new DiscordColor(r, g, b)));
}
return Task.FromResult(Optional.FromNoValue());
}
}
}
diff --git a/DisCatSharp.Common/DiscordRegEx.cs b/DisCatSharp.Common/DiscordRegEx.cs
deleted file mode 100644
index cb74d4dae..000000000
--- a/DisCatSharp.Common/DiscordRegEx.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// This file is part of the DisCatSharp project, a fork of DSharpPlus.
-//
-// Copyright (c) 2021 AITSYS
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace DisCatSharp.Common
-{
- ///
- /// Provides common regex for discord related things.
- ///
- public static class DiscordRegEx
- {
- }
-}
diff --git a/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
new file mode 100644
index 000000000..1e0111ef6
--- /dev/null
+++ b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
@@ -0,0 +1,83 @@
+// This file is part of the DisCatSharp project, a fork of DSharpPlus.
+//
+// Copyright (c) 2021 AITSYS
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace DisCatSharp.Common.RegularExpressions
+{
+ ///
+ /// Provides common regex for discord related things.
+ ///
+ public static class DiscordRegEx
+ {
+ ///
+ /// Represents a invite.
+ ///
+ public static Regex Invite
+ => new(@"^(https?:\/\/)?(www\.)?(discord\.(gg|io|me|li)|discordapp\.com\/invite)\/(.+[a-z])$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a message link.
+ ///
+ public static Regex MessageLink
+ => new(@"^\/channels\/(?(?:\d+|@me))\/(?\d+)\/(?\d+)\/?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a emoji.
+ ///
+ public static Regex Emoji
+ => new(@"^<(?a)?:(?[a-zA-Z0-9_]+?):(?\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a hex color string.
+ ///
+ public static Regex HexColorString
+ => new(@"^#?([a-fA-F0-9]{6})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a rgp color string.
+ ///
+ public static Regex RgbColorString
+ => new(@"^(\d{1,3})\s*?,\s*?(\d{1,3}),\s*?(\d{1,3})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a role.
+ ///
+ public static Regex Role
+ => new(@"^<@&(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a channel.
+ ///
+ public static Regex Channel
+ => new(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ ///
+ /// Represents a user.
+ ///
+ public static Regex User
+ => new(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ }
+}