DeukBot4/DeukBot4/MessageHandlers/ReminderHandler.cs

131 lines
5.4 KiB
C#

using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DeukBot4.MessageHandlers.CommandHandler;
using DeukBot4.Utilities;
namespace DeukBot4.MessageHandlers
{
public static class ReminderHandler
{
private static readonly Regex ReminderMatcher =
new Regex(
@".*(remind\s*((?<recipient>me)|<@!*(?<recipient>\d*)>)\s*)(\s+to(?<action>.+)|\s+in\s+(?<time>((?!to).)*)){2}",
RegexOptions.IgnoreCase);
/*
* Regex explained:
* .*
* grab any token preceding
* (remind\s*
* look for the word "remind", followed by a whitespace
* ((?<recipient>me)|<@!*(?<recipient>\d*)>)\s*)
* look for either "me", or a discord ping or id, and save it as "recipient" group
* (\s+to(?<action>.+)
* look for the word "to", then followed by a string of what we should remember to do
* \s+in\s+(?<time>((?!to).)*))
* in a specified time. We do a negative lookup for the word "to" here, so that they don't get eaten
* (\s+to(?<action>.+)|\s+in\s+(?<time>[^to]*)){2}
* The action and time can be in either order.
*/
public static async Task HandleReminder(ReceivedMessage receivedMessage)
{
var match = ReminderMatcher.Match(receivedMessage.Message.Content);
if (!match.Success)
{
return;
}
if (receivedMessage.IsHandled)
return;
receivedMessage.IsHandled = true;
var message = receivedMessage.Message;
var recipient = match.Groups["recipient"].Captures[0].Value;
var action = match.Groups["action"]?.Value.Trim();
if (string.IsNullOrWhiteSpace(action))
return;
var time = match.Groups["time"]?.Value;
if (string.IsNullOrWhiteSpace(time))
return;
var timespan = ParseTime(time);
if (!timespan.HasValue)
return;
var embed = EmbedFactory.GetStandardEmbedBuilder();
embed.Title = "Reminder";
if (timespan.Value.TotalMinutes < 5)
{
embed.Description = "A reminder should be at least 5 minutes in the future";
message.Channel.SendMessageAsync("", embed: embed.Build());
return;
}
if (!ulong.TryParse(recipient, out var recip))
{
recip = message.Author.Id;
}
embed.Description = message.Author.Id.Equals(recip)
? $"Reminder set! I will remind you in {timespan.Value.ToPrettyFormat()} to {action}"
: $"Reminder set! I will remind <@!{recip}> in {timespan.Value.ToPrettyFormat()} to {action}";
Database.ReminderHandler.Main.AddReminder(timespan.Value, action, message.Channel.Id, message.Author.Id, recip);
message.Channel.SendMessageAsync(embed: embed.Build());
}
private static readonly Regex TimespanMatcher =
new Regex(@"(?<timeNumber>\d+.?\d*|[\w\s-]+)\s*(?<timeId>minutes*|hours*|days*|weeks*|months*|years*)\W*(and )*",
RegexOptions.IgnoreCase);
private static TimeSpan? ParseTime(string message)
{
var matches = TimespanMatcher.Matches(message);
if (matches.Count == 0)
return null;
var timespan = new TimeSpan();
foreach (Match match in matches)
{
if (!match.Success)
continue;
var timeId = match.Groups["timeId"].Value.ToLowerInvariant();
var amountString = match.Groups["timeNumber"].Value;
if (!double.TryParse(amountString, out var timeAmount))
{
if (!EnglishNumberParser.ConvertNumberString(amountString, out timeAmount))
{
return null;
}
}
if (timeId.StartsWith("minute"))
timespan += TimeSpan.FromMinutes(timeAmount);
else if (timeId.StartsWith("hour"))
timespan += TimeSpan.FromHours(timeAmount);
else if (timeId.StartsWith("day"))
timespan += TimeSpan.FromDays(timeAmount);
else if (timeId.StartsWith("week"))
timespan += TimeSpan.FromDays(timeAmount * 7);
else if (timeId.StartsWith("month"))
{
var target = DateTime.UtcNow.AddMonths((int) timeAmount);
target = target.AddDays(timeAmount % 1 * 30);
timespan += (target - DateTime.UtcNow);
}
else if (timeId.StartsWith("month"))
{
var target = DateTime.UtcNow.AddMonths((int) timeAmount);
target = target.AddDays(timeAmount % 1 * 30);
timespan += (target - DateTime.UtcNow);
}
else if (timeId.StartsWith("year"))
{
var target = DateTime.UtcNow.AddYears((int) timeAmount);
target = target.AddDays(timeAmount % 1 * 365);
timespan += (target - DateTime.UtcNow);
}
}
return timespan;
}
}
}