Added reminders
This commit is contained in:
parent
56f1e2740e
commit
b2e3b3dec8
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace DeukBot4.Database
|
||||
{
|
||||
public class ReminderHandler
|
||||
{
|
||||
public static ReminderHandler Main = new ReminderHandler();
|
||||
public static ConnectionMultiplexer Redis = ConnectionMultiplexer.Connect("127.0.0.1");
|
||||
|
||||
|
||||
public void AddReminder(TimeSpan time, string message, ulong channel, ulong author, ulong recipient)
|
||||
{
|
||||
try
|
||||
{
|
||||
var db = Redis.GetDatabase();
|
||||
var id = Guid.NewGuid().ToString();
|
||||
var expectedTime = DateTime.UtcNow.Add(time);
|
||||
db.SortedSetAdd("deukbot_reminders", (RedisValue)id, expectedTime.ToBinary());
|
||||
db.HashSet((RedisKey) id, new[]
|
||||
{
|
||||
new HashEntry("channel", channel),
|
||||
new HashEntry("message", message),
|
||||
new HashEntry("author", author),
|
||||
new HashEntry("recipient", recipient),
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Main.LogError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckReminders()
|
||||
{
|
||||
var checkTime = TimeSpan.FromSeconds(70);
|
||||
var startTime = DateTime.UtcNow;
|
||||
var desiredTopScore = (startTime + checkTime).ToBinary();
|
||||
var db = Redis.GetDatabase();
|
||||
var reminders = db.SortedSetRangeByScoreWithScores("deukbot_reminders", stop: desiredTopScore);
|
||||
foreach (var sortedSetEntry in reminders)
|
||||
{
|
||||
var val = sortedSetEntry.Element.ToString();
|
||||
var timeLong = sortedSetEntry.Score;
|
||||
var time = DateTime.FromBinary((long) timeLong);
|
||||
var data = db.HashGetAll(val);
|
||||
ulong channel = 0;
|
||||
ulong author = 0;
|
||||
ulong recipient = 0;
|
||||
string message = null;
|
||||
foreach (var hashEntry in data)
|
||||
{
|
||||
if (hashEntry.Name == "channel") channel = (ulong) hashEntry.Value;
|
||||
else if (hashEntry.Name == "message") message = hashEntry.Value;
|
||||
else if (hashEntry.Name == "author") author = (ulong) hashEntry.Value;
|
||||
else if (hashEntry.Name == "recipient") recipient = (ulong) hashEntry.Value;
|
||||
}
|
||||
var diff = time - DateTime.UtcNow;
|
||||
FireReminderAtTime((int) diff.TotalSeconds, channel, message, author, recipient);
|
||||
db.KeyDelete(val);
|
||||
}
|
||||
|
||||
db.SortedSetRemoveRangeByScore("deukbot_reminders", Double.MinValue, desiredTopScore);
|
||||
|
||||
await Task.Delay(checkTime);
|
||||
await CheckReminders();
|
||||
}
|
||||
|
||||
private async Task FireReminderAtTime(int seconds, ulong channelId, string message, ulong author, ulong recipient)
|
||||
{
|
||||
if (seconds > 0)
|
||||
await Task.Delay(TimeSpan.FromSeconds(seconds));
|
||||
await FireReminder(channelId, message, author, recipient);
|
||||
}
|
||||
|
||||
private async Task FireReminder(ulong channelId, string message, ulong author, ulong recipient)
|
||||
{
|
||||
if (Program.Client.GetChannel(channelId) is ITextChannel channel)
|
||||
{
|
||||
if (author == recipient)
|
||||
{
|
||||
channel.SendMessageAsync($"Hey <@{recipient}>, don't forget to {message}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.SendMessageAsync($"Hey <@{recipient}>, <@{author}> asked me to remind you to {message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,5 +9,6 @@
|
|||
<PackageReference Include="Microsoft.NETCore.App" Version="2.1.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.2" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="1.2.6" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -6,9 +6,12 @@ using Discord;
|
|||
|
||||
namespace DeukBot4
|
||||
{
|
||||
public static class Logger
|
||||
|
||||
public class Logger
|
||||
{
|
||||
private static readonly Dictionary<LogSeverity, ConsoleColor> Colors = new Dictionary<LogSeverity, ConsoleColor>
|
||||
public static Logger Main = new Logger();
|
||||
|
||||
private readonly Dictionary<LogSeverity, ConsoleColor> Colors = new Dictionary<LogSeverity, ConsoleColor>
|
||||
{
|
||||
{LogSeverity.Info, ConsoleColor.Black},
|
||||
{LogSeverity.Verbose, ConsoleColor.Black},
|
||||
|
@ -18,34 +21,33 @@ namespace DeukBot4
|
|||
{LogSeverity.Critical, ConsoleColor.Red}
|
||||
};
|
||||
|
||||
public static async Task Log(object o, LogSeverity severity)
|
||||
public async Task Log(object o, LogSeverity severity)
|
||||
{
|
||||
Console.ForegroundColor = Colors[severity];
|
||||
Console.WriteLine($"[{severity}] {DateTime.UtcNow:u}: {o.ToString()}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static async Task LogDiscord(LogMessage message)
|
||||
public async Task LogDiscord(LogMessage message)
|
||||
{
|
||||
Console.ForegroundColor = Colors[message.Severity];
|
||||
Console.WriteLine($"[{message.Severity}] {DateTime.UtcNow:u}: {message.Message}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static async Task Log(object o)
|
||||
public async Task Log(object o)
|
||||
{
|
||||
await Log(o, LogSeverity.Info);
|
||||
}
|
||||
|
||||
public static async Task LogWarning(object o)
|
||||
public async Task LogWarning(object o)
|
||||
{
|
||||
await Log(o, LogSeverity.Warning);
|
||||
}
|
||||
|
||||
public static async Task LogError(object o)
|
||||
public async Task LogError(object o)
|
||||
{
|
||||
await Log(o, LogSeverity.Error);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
}
|
||||
}
|
||||
|
||||
Logger.Log(
|
||||
Logger.Main.Log(
|
||||
$"Loaded following commands for container {obj.Name}: {commands.Select(x => x.Name).Join(", ")}");
|
||||
|
||||
}
|
||||
|
@ -57,19 +57,19 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
switch (resultCode)
|
||||
{
|
||||
case CommandRequest.RequestCode.Invalid:
|
||||
await Logger.LogError("Invalid content: " + message.Content);
|
||||
await Logger.Main.LogError("Invalid content: " + message.Content);
|
||||
return;
|
||||
case CommandRequest.RequestCode.InvalidParameters:
|
||||
await Logger.LogError("Invalid parameters: " + message.Content);
|
||||
await Logger.Main.LogError("Invalid parameters: " + message.Content);
|
||||
break;
|
||||
case CommandRequest.RequestCode.Forbidden:
|
||||
await Logger.Log(
|
||||
await Logger.Main.Log(
|
||||
$"Unauthorized user tried to run command: {message.Author.Username} -> {message.Content}");
|
||||
break;
|
||||
case CommandRequest.RequestCode.OK:
|
||||
if (!(message.Channel is IGuildChannel) && req.Item1.Command.ForbidInPm)
|
||||
{
|
||||
await Logger.Log(
|
||||
await Logger.Main.Log(
|
||||
$"User is trying to use blocked command in PM: {message.Author.Username}");
|
||||
return;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.Log("An error occured: \n" + e);
|
||||
await Logger.Main.Log("An error occured: \n" + e);
|
||||
}
|
||||
break;
|
||||
case CommandRequest.RequestCode.UnknownCommand:
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Net;
|
|||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using DeukBot4.APIHandlers;
|
||||
using DeukBot4.Database;
|
||||
using DeukBot4.MessageHandlers.CommandHandler.RequestStructure;
|
||||
using DeukBot4.MessageHandlers.Permissions;
|
||||
using DeukBot4.Utilities;
|
||||
|
@ -140,6 +141,5 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
};
|
||||
await request.SendMessageAsync("", embed: eb.Build());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -178,7 +178,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
span = TimeSpan.FromMinutes(minutes.Value);
|
||||
break;
|
||||
case ParameterMatcher.ParameterType.Timespan:
|
||||
var sp = TimespanParser.Parse(request.Parameters[1].AsString());
|
||||
var sp = TimespanHelper.Parse(request.Parameters[1].AsString());
|
||||
if (sp.HasValue)
|
||||
{
|
||||
span = sp.Value;
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler
|
|||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
await Logger.LogError(e.Message);
|
||||
await Logger.Main.LogError(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler.RequestStructure
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.LogError(e.Message);
|
||||
await Logger.Main.LogError(e.Message);
|
||||
return (null, RequestCode.Forbidden, null);
|
||||
}
|
||||
if (permission < command.Permission)
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler.RequestStructure
|
|||
case ParameterType.User:
|
||||
return $" *(?:<@!*(?<{index}>\\d+)>|(?<{index}>\\d+)(?:$| |\n))";
|
||||
case ParameterType.Timespan:
|
||||
return $" *(?<{index}>\\d+\\.*d*[smhd])";
|
||||
return $" *(?<{index}>\\d+\\.*\\d*[smhd])";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ namespace DeukBot4.MessageHandlers.CommandHandler.RequestStructure
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e.ToString());
|
||||
Logger.Main.LogError(e.ToString());
|
||||
return command.RequireParameterMatch ? null : new RequestParameter[0];
|
||||
}
|
||||
if (matches.Success)
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace DeukBot4.MessageHandlers
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.LogError(e);
|
||||
await Logger.Main.LogError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using DeukBot4.APIHandlers;
|
||||
using DeukBot4.Database;
|
||||
using DeukBot4.Utilities;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
@ -22,6 +24,7 @@ namespace DeukBot4.MessageHandlers
|
|||
#pragma warning disable 4014
|
||||
CommandHandler.CommandHandler.HandleMessage(message);
|
||||
HandlePrivateMessage(message);
|
||||
HandleReminder(message);
|
||||
ImageBackupHandler.Backup(message);
|
||||
JokeHandlers.DeltaHandler(message);
|
||||
JokeHandlers.DadJokeHandler(message);
|
||||
|
@ -30,7 +33,7 @@ namespace DeukBot4.MessageHandlers
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.LogError(e.ToString());
|
||||
await Logger.Main.LogError(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +42,7 @@ namespace DeukBot4.MessageHandlers
|
|||
{
|
||||
if (message.Channel is IPrivateChannel)
|
||||
{
|
||||
await Logger.Log(($"Private Message: {message.Author.Username}- {message.Content}"));
|
||||
await Logger.Main.Log(($"Private Message: {message.Author.Username}- {message.Content}"));
|
||||
if (_dmChannel == null)
|
||||
{
|
||||
_dmChannel = (ITextChannel) Program.Client.GetChannel(Program.Settings.DmChannel);
|
||||
|
@ -63,5 +66,70 @@ namespace DeukBot4.MessageHandlers
|
|||
}
|
||||
}
|
||||
|
||||
private static Regex ReminderMatcher =
|
||||
new Regex(
|
||||
@".*(remind\s*((?<recipient>me)|<@!*(?<recipient>\d*)>)\s*to)(?<action>.*)(in\s*)(?<timeNum>\d)\s*(?<timeId>\w*)",
|
||||
RegexOptions.IgnoreCase);
|
||||
private static async Task HandleReminder(SocketMessage message)
|
||||
{
|
||||
var match = ReminderMatcher.Match(message.Content);
|
||||
if (!match.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var recipient = match.Groups["recipient"].Captures[0].Value;
|
||||
var action = match.Groups["action"].Value.Trim();
|
||||
if (string.IsNullOrWhiteSpace(action))
|
||||
return;
|
||||
var timeNumber = double.Parse(match.Groups["timeNum"].Value);
|
||||
var timeIdentifier = match.Groups["timeId"].Value.Trim();
|
||||
TimeSpan timespan;
|
||||
if (timeIdentifier.ToLowerInvariant().StartsWith("minu"))
|
||||
{
|
||||
timespan = TimeSpan.FromMinutes(timeNumber);
|
||||
}
|
||||
else if (timeIdentifier.ToLowerInvariant().StartsWith("hour"))
|
||||
{
|
||||
timespan = TimeSpan.FromHours(timeNumber);
|
||||
}
|
||||
else if (timeIdentifier.ToLowerInvariant().StartsWith("day"))
|
||||
{
|
||||
timespan = TimeSpan.FromDays(timeNumber);
|
||||
}
|
||||
else if (timeIdentifier.ToLowerInvariant().StartsWith("month"))
|
||||
{
|
||||
var dest = DateTime.UtcNow.AddMonths((int) (timeNumber));
|
||||
dest = dest.AddDays(timeNumber % 1 * 30);
|
||||
timespan = dest - DateTime.UtcNow;
|
||||
}
|
||||
else if (timeIdentifier.ToLowerInvariant().StartsWith("year"))
|
||||
{
|
||||
var dest = DateTime.UtcNow.AddYears((int) (timeNumber));
|
||||
dest = dest.AddDays(timeNumber % 1 * 365);
|
||||
timespan = dest - DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Main.LogError("Unknown timespan identifier: " + timeIdentifier);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timespan.TotalMinutes < 5)
|
||||
{
|
||||
message.Channel.SendMessageAsync("A reminder should be at least 5 minutes in the future");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ulong.TryParse(recipient, out var recip))
|
||||
{
|
||||
recip = message.Author.Id;
|
||||
}
|
||||
|
||||
ReminderHandler.Main.AddReminder(timespan, action, message.Channel.Id, message.Author.Id, recip);
|
||||
message.Channel.SendMessageAsync(
|
||||
message.Author.Id == recip
|
||||
? $"Reminder set! I will remind you in {timespan.ToPrettyFormat()} to {action}"
|
||||
: $"Reminder set! I will remind <@!{recip}> in {timespan.ToPrettyFormat()} to {action}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ namespace DeukBot4.MessageHandlers.Permissions
|
|||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
await Logger.LogError(e.ToString());
|
||||
await Logger.Main.LogError(e.ToString());
|
||||
return PermissionLevel.Everyone;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using DeukBot4.MessageHandlers.CommandHandler;
|
|||
using Discord;
|
||||
using Discord.Commands.Builders;
|
||||
using Discord.WebSocket;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace DeukBot4
|
||||
{
|
||||
|
@ -21,10 +22,17 @@ namespace DeukBot4
|
|||
MainAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private static async Task SetupScheduler()
|
||||
{
|
||||
ReminderHandler.Main.CheckReminders();
|
||||
}
|
||||
|
||||
private static async Task MainAsync()
|
||||
{
|
||||
|
||||
Settings = Settings.FromJsonFile("settings.json");
|
||||
DatabaseConnection.ConnectionString = Settings.DatabaseConnectionString;
|
||||
await SetupScheduler();
|
||||
|
||||
DatabaseInitializer.Initialize();
|
||||
ServerSettingHandler.OnBotStartUp();
|
||||
|
@ -35,7 +43,7 @@ namespace DeukBot4
|
|||
|
||||
Client = new DiscordSocketClient();
|
||||
|
||||
Client.Log += Logger.LogDiscord;
|
||||
Client.Log += Logger.Main.LogDiscord;
|
||||
Client.Ready += OnReady;
|
||||
Client.MessageReceived += MainHandler.HandleMessage;
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DeukBot4.Utilities
|
||||
{
|
||||
public static class TimespanParser
|
||||
public static class TimespanHelper
|
||||
{
|
||||
public static TimeSpan? Parse(string s)
|
||||
{
|
||||
|
@ -27,6 +28,19 @@ namespace DeukBot4.Utilities
|
|||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static string ToPrettyFormat(this TimeSpan span) {
|
||||
|
||||
if (span == TimeSpan.Zero) return "0 minutes";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
if (span.Days > 0)
|
||||
sb.AppendFormat("{0} day{1} ", span.Days, span.Days > 1 ? "s" : String.Empty);
|
||||
if (span.Hours > 0)
|
||||
sb.AppendFormat("{0} hour{1} ", span.Hours, span.Hours > 1 ? "s" : String.Empty);
|
||||
if (span.Minutes > 0)
|
||||
sb.AppendFormat("{0} minute{1} ", span.Minutes, span.Minutes > 1 ? "s" : String.Empty);
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue