diff --git a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
index 09d3d9a13..679dcb754 100644
--- a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
+++ b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
@@ -1,588 +1,457 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.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 s_userRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordUserConverter()
- {
- s_userRegex = DiscordRegEx.User;
- }
-
///
/// 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);
return Optional.FromNullable(result);
}
- var m = s_userRegex.Match(value);
+ var m = DiscordRegEx.User.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);
return Optional.FromNullable(result);
}
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var di = value.IndexOf('#');
var un = di != -1 ? value[..di] : value;
var dv = di != -1 ? value[(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 Optional.FromNullable(usr);
}
}
///
/// Represents a discord member converter.
///
public class DiscordMemberConverter : IArgumentConverter
{
- ///
- /// Gets the user regex.
- ///
- private static Regex s_userRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordMemberConverter()
- {
- s_userRegex = DiscordRegEx.User;
- }
-
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (ctx.Guild == null)
return Optional.None;
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
{
var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
return Optional.FromNullable(result);
}
- var m = s_userRegex.Match(value);
+ var m = DiscordRegEx.User.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);
return Optional.FromNullable(result);
}
var searchResult = await ctx.Guild.SearchMembersAsync(value).ConfigureAwait(false);
if (searchResult.Any())
return Optional.Some(searchResult.First());
var cs = ctx.Config.CaseSensitive;
if (!cs)
value = value.ToLowerInvariant();
var di = value.IndexOf('#');
var un = di != -1 ? value[..di] : value;
var dv = di != -1 ? value[(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);
return Optional.FromNullable(us.FirstOrDefault());
}
}
///
/// Represents a discord channel converter.
///
public class DiscordChannelConverter : IArgumentConverter
{
- ///
- /// Gets the channel regex.
- ///
- private static Regex s_channelRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordChannelConverter()
- {
- s_channelRegex = DiscordRegEx.Channel;
- }
-
///
/// 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);
return Optional.FromNullable(result);
}
- var m = s_channelRegex.Match(value);
+ var m = DiscordRegEx.Channel.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);
return Optional.FromNullable(result);
}
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 Optional.FromNullable(chn);
}
}
///
/// Represents a discord thread channel converter.
///
public class DiscordThreadChannelConverter : IArgumentConverter
{
- ///
- /// Gets the channel regex.
- ///
- private static Regex s_channelRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordThreadChannelConverter()
- {
- s_channelRegex = DiscordRegEx.Channel;
- }
-
///
/// 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);
return Optional.FromNullable(result);
}
- var m = s_channelRegex.Match(value);
+ var m = DiscordRegEx.Channel.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);
return Optional.FromNullable(result);
}
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 Optional.FromNullable(tchn);
}
}
///
/// Represents a discord role converter.
///
public class DiscordRoleConverter : IArgumentConverter
{
- ///
- /// Gets the role regex.
- ///
- private static Regex s_roleRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordRoleConverter()
- {
- s_roleRegex = DiscordRegEx.Role;
- }
-
///
/// 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.None);
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rid))
{
var result = ctx.Guild.GetRole(rid);
return Task.FromResult(Optional.FromNullable(result));
}
- var m = s_roleRegex.Match(value);
+ var m = DiscordRegEx.Role.Match(value);
if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out rid))
{
var result = ctx.Guild.GetRole(rid);
return Task.FromResult(Optional.FromNullable(result));
}
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(Optional.FromNullable(rol));
}
}
///
/// 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.Some(result))
: Task.FromResult(Optional.None);
}
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(Optional.FromNullable(gld));
}
}
///
/// Represents a discord invite converter.
///
public class DiscordInviteConverter : IArgumentConverter
{
- ///
- /// Gets the invite regex.
- ///
- private static Regex s_inviteRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordInviteConverter()
- {
- s_inviteRegex = DiscordRegEx.Invite;
- }
-
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
- var m = s_inviteRegex.Match(value);
+ var m = DiscordRegEx.Invite.Match(value);
if (m.Success)
{
- var result = await ctx.Client.GetInviteByCodeAsync(m.Groups[5].Value).ConfigureAwait(false);
+ ulong? eventId = ulong.TryParse(m.Groups["eventId"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out var eid) ? eid : null;
+ var result = await ctx.Client.GetInviteByCodeAsync(m.Groups["code"].Value, scheduledEventId: eventId).ConfigureAwait(false);
return Optional.FromNullable(result);
}
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value?.ToLowerInvariant();
-
var inv = await ctx.Client.GetInviteByCodeAsync(value);
return Optional.FromNullable(inv);
}
}
///
/// Represents a discord message converter.
///
public class DiscordMessageConverter : IArgumentConverter
{
- ///
- /// Gets the message path regex.
- ///
- private static Regex s_messagePathRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordMessageConverter()
- {
- s_messagePathRegex = DiscordRegEx.MessageLink;
- }
-
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (string.IsNullOrWhiteSpace(value))
return Optional.None;
var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : 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.None;
-
- var uripath = s_messagePathRegex.Match(uri.AbsolutePath);
+ var uripath = DiscordRegEx.MessageLink.Match(uri.AbsoluteUri);
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.None;
var chn = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
if (chn == null)
return Optional.None;
var msg = await chn.GetMessageAsync(mid).ConfigureAwait(false);
return Optional.FromNullable(msg);
}
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
{
var result = await ctx.Channel.GetMessageAsync(mid).ConfigureAwait(false);
return Optional.FromNullable(result);
}
return Optional.None;
}
}
///
/// Represents a discord scheduled event converter.
///
public class DiscordScheduledEventConverter : IArgumentConverter
{
- ///
- /// Gets the event regex.
- ///
- private static Regex s_eventRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordScheduledEventConverter()
- {
- s_eventRegex = DiscordRegEx.Event;
- }
-
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
async Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
if (string.IsNullOrWhiteSpace(value))
return Optional.None;
var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : value;
ulong seid;
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"))
+ var uripath = DiscordRegEx.Event.Match(uri.AbsoluteUri);
+ if (uripath.Success
+ && ulong.TryParse(uripath.Groups["guild"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out var gid)
+ && ulong.TryParse(uripath.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out seid))
+ {
+ var guild = await ctx.Client.GetGuildAsync(gid).ConfigureAwait(false);
+ if (guild == null)
+ return Optional.None;
+
+ var ev = await guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
+ return Optional.FromNullable(ev);
+ }
+
+ try
+ {
+ var invite = await ctx.CommandsNext.ConvertArgument(value, ctx).ConfigureAwait(false);
+ return Optional.FromNullable(invite.GuildScheduledEvent);
+ }
+ catch
+ {
return Optional.None;
-
- var uripath = s_eventRegex.Match(uri.AbsolutePath);
- if (!uripath.Success
- || !ulong.TryParse(uripath.Groups["guild"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var gid)
- || !ulong.TryParse(uripath.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out seid))
- return Optional.None;
-
- var guild = await ctx.Client.GetGuildAsync(gid).ConfigureAwait(false);
- if (guild == null)
- return Optional.None;
-
- var ev = await guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
- return Optional.FromNullable(ev);
+ }
}
if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out seid))
{
var result = await ctx.Guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
return Optional.FromNullable(result);
}
return Optional.None;
}
}
///
/// Represents a discord emoji converter.
///
public class DiscordEmojiConverter : IArgumentConverter
{
- ///
- /// Gets the emote regex.
- ///
- private static Regex s_emoteRegex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordEmojiConverter()
- {
- s_emoteRegex = DiscordRegEx.Emoji;
- }
-
///
/// 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;
return Task.FromResult(Optional.Some(result));
}
- var m = s_emoteRegex.Match(value);
+ var m = DiscordRegEx.Emoji.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.None)
: DiscordEmoji.TryFromGuildEmote(ctx.Client, id, out emoji)
? Task.FromResult(Optional.Some(emoji))
: Task.FromResult(Optional.Some(new DiscordEmoji
{
Discord = ctx.Client,
Id = id,
Name = name,
IsAnimated = anim,
RequiresColons = true,
IsManaged = false
}));
}
return Task.FromResult(Optional.None);
}
}
///
/// Represents a discord color converter.
///
public class DiscordColorConverter : IArgumentConverter
{
- ///
- /// Gets the color regex hex.
- ///
- private static Regex s_colorRegexHex { get; }
- ///
- /// Gets the color regex rgb.
- ///
- private static Regex s_colorRegexRgb { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- static DiscordColorConverter()
- {
- s_colorRegexHex = CommonRegEx.HexColorString;
- s_colorRegexRgb = CommonRegEx.RgbColorString;
- }
-
///
/// Converts a string.
///
/// The string to convert.
/// The command context.
Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx)
{
- var m = s_colorRegexHex.Match(value);
+ var m = CommonRegEx.HexColorString.Match(value);
if (m.Success && int.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clr))
return Task.FromResult(Optional.Some(clr));
- m = s_colorRegexRgb.Match(value);
+ m = CommonRegEx.RgbColorString.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.None)
: Task.FromResult(Optional.Some(new DiscordColor(r, g, b)));
}
return Task.FromResult(Optional.None);
}
}
}
diff --git a/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
index 7c2bde47a..0475999c1 100644
--- a/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
+++ b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
@@ -1,122 +1,124 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Text.RegularExpressions;
namespace DisCatSharp.Common.RegularExpressions
{
///
/// Provides common regex for discord related things.
///
public static class DiscordRegEx
{
+ private const string WEBSITE = @"(https?:\/\/)?(www\.|canary\.|ptb\.)?(discord)\.(com|net)\/";
+
///
/// Represents a invite.
///
- public static Regex Invite
- => new(@"^(https?:\/\/)?(www\.)?(discord\.(gg|io|me|li)|discordapp\.(com|net)\/invite)\/(.+[a-z])$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static readonly Regex Invite
+ = new(@"^(https?:\/\/)?(www\.)?(discord\.(gg|io|me|li)|discordapp\.(com|net)\/invite)\/(.*\/)*(?[a-zA-Z0-9]*)(\?event=(?\d+))?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
///
/// Represents a message link.
///
- public static Regex MessageLink
- => new(@"(https?:\/\/)?(www\.|canary\.|ptb\.)?(discord)\.(com|net)\/channels\/(?(?:\d+|@me))\/(?\d+)\/(?\d+)\/?", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static readonly Regex MessageLink
+ = new($@"^{WEBSITE}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);
+ public static readonly Regex Emoji
+ = new(@"^<(?a)?:(?[a-zA-Z0-9_]+?):(?\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
///
/// Represents a animated emoji.
///
- public static Regex AnimatedEmoji
- => new(@"^<(?a):(?\w{2,32}):(?\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static readonly Regex AnimatedEmoji
+ = new(@"^<(?a):(?\w{2,32}):(?\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
///
/// Represents a non-animated emoji.
///
- public static Regex StaticEmoji
- => new(@"^<:(?\w{2,32}):(?\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static readonly Regex StaticEmoji
+ = new(@"^<:(?\w{2,32}):(?\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
///
/// Represents a timestamp.
///
- public static Regex Timestamp
- => new(@"^-?\d{1,13})(:(?