Skip to content

Commit 62788b9

Browse files
committed
new enhanced timespan parser to accept other input formats
1 parent d29f780 commit 62788b9

File tree

3 files changed

+39
-37
lines changed

3 files changed

+39
-37
lines changed

CommandHandlers/WarningCommands.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public void CMD_Timeout(CommandData command)
138138
}
139139
WarnableUser warnable = WarningUtilities.GetWarnableUser(guild.Id, userID);
140140
string durationText = command.RawArguments[1];
141-
TimeSpan? realDuration = WarningUtilities.ParseShortDuration(durationText);
141+
TimeSpan? realDuration = WarningUtilities.ParseDuration(durationText, mForMinutes: true);
142142
if (!realDuration.HasValue)
143143
{
144144
SendErrorMessageReply(command.Message, "Invalid Input", "Duration must be formatted like '5m' (for 5 minutes). Allowed type: 'm' for minutes, 'h' for hours, 'd' for days. Or '0' to remove a timeout.");

WarningHandlers/WarningUtilities.cs

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using ModBot.Core;
1010
using FreneticUtilities.FreneticExtensions;
1111
using System.Collections.Concurrent;
12+
using FreneticUtilities.FreneticToolkit;
1213

1314
namespace ModBot.WarningHandlers
1415
{
@@ -94,56 +95,57 @@ public static WarnableUser GetWarnableUser(ulong guildId, ulong id)
9495
/// <summary>Words that mean "permanent" that a user might try.</summary>
9596
public static HashSet<string> PermanentWords = new() { "permanent", "permanently", "indefinite", "indefinitely", "forever" };
9697

97-
/// <summary>Parses duration text into a valid TimeSpan, or null.</summary>
98-
public static TimeSpan? ParseShortDuration(string durationText)
98+
/// <summary>Helper to separate digits from letters, for <see cref="ParseDuration(string, bool)"/>.</summary>
99+
public static AsciiMatcher DigitMatcher = new(AsciiMatcher.Digits);
100+
101+
/// <summary>Helper to identify timespan suffix keywords, for <see cref="ParseDuration(string, bool)"/>.</summary>
102+
public static Dictionary<string, string> TimespanSuffixRemapper = new();
103+
static WarningUtilities()
99104
{
100-
durationText = durationText.ToLowerFast();
101-
if (durationText == "0")
102-
{
103-
return TimeSpan.Zero;
104-
}
105-
if (durationText.EndsWith('m') && double.TryParse(durationText.Before('m'), out double minutes))
105+
static void Add(string realKey, params string[] keys)
106106
{
107-
return TimeSpan.FromMinutes(minutes);
108-
}
109-
else if (durationText.EndsWith('h') && double.TryParse(durationText.Before('h'), out double hours))
110-
{
111-
return TimeSpan.FromHours(hours);
112-
}
113-
else if (durationText.EndsWith('d') && double.TryParse(durationText.Before('d'), out double days))
114-
{
115-
return TimeSpan.FromDays(days);
107+
foreach (string key in keys)
108+
{
109+
TimespanSuffixRemapper.Add(key, realKey);
110+
}
111+
TimespanSuffixRemapper.Add(realKey, realKey);
116112
}
117-
return null;
113+
Add("seconds", "s", "sec", "secs", "second");
114+
Add("minutes", "min", "mins", "minute");
115+
Add("hours", "h", "hr", "hrs", "hour");
116+
Add("days", "d", "day");
117+
Add("weeks", "w", "week");
118+
Add("months", "mo", "mon", "mons", "month");
119+
Add("years", "y", "yr", "yrs", "year");
118120
}
119121

120122
/// <summary>Parses duration text into a valid TimeSpan, or null.</summary>
121-
public static TimeSpan? ParseDuration(string durationText)
123+
public static TimeSpan? ParseDuration(string durationText, bool mForMinutes = false)
122124
{
123125
durationText = durationText.ToLowerFast();
124126
if (PermanentWords.Contains(durationText))
125127
{
126128
return TimeSpan.FromDays(100 * 365);
127129
}
128-
if (durationText.EndsWith('h') && double.TryParse(durationText.Before('h'), out double hours))
129-
{
130-
return TimeSpan.FromHours(hours);
131-
}
132-
else if (durationText.EndsWith('d') && double.TryParse(durationText.Before('d'), out double days))
133-
{
134-
return TimeSpan.FromDays(days);
135-
}
136-
else if (durationText.EndsWith('w') && double.TryParse(durationText.Before('w'), out double weeks))
137-
{
138-
return TimeSpan.FromDays(weeks * 7);
139-
}
140-
else if (durationText.EndsWith('m') && double.TryParse(durationText.Before('m'), out double months))
130+
int endOfNumber = DigitMatcher.FirstNonMatchingIndex(durationText);
131+
string numberText = durationText[..endOfNumber];
132+
string suffixText = durationText[endOfNumber..].ToLowerFast();
133+
if (suffixText == "m")
141134
{
142-
return TimeSpan.FromDays(months * 31);
135+
suffixText = mForMinutes ? "minutes" : "months";
143136
}
144-
else if (durationText.EndsWith('y') && double.TryParse(durationText.Before('y'), out double years))
137+
if (TimespanSuffixRemapper.TryGetValue(suffixText, out string formatText) && double.TryParse(numberText, out double numVal))
145138
{
146-
return TimeSpan.FromDays(years * 365);
139+
switch (formatText)
140+
{
141+
case "seconds": return TimeSpan.FromSeconds(numVal);
142+
case "minutes": return TimeSpan.FromMinutes(numVal);
143+
case "hours": return TimeSpan.FromHours(numVal);
144+
case "days": return TimeSpan.FromDays(numVal);
145+
case "weeks": return TimeSpan.FromDays(numVal * 7);
146+
case "months": return TimeSpan.FromDays(numVal * 31);
147+
case "years": return TimeSpan.FromDays(numVal * 365);
148+
}
147149
}
148150
return null;
149151
}

0 commit comments

Comments
 (0)