Fixed some crashes in response strings source generator, reorganized more submodules into their folders

This commit is contained in:
Kwoth
2022-01-02 03:49:54 +01:00
parent 9c590668df
commit 4b6af0e4ef
191 changed files with 120 additions and 80 deletions

View File

@@ -0,0 +1,224 @@
#nullable disable
using Humanizer.Localisation;
using NadekoBot.Db;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Utility.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Utility;
public partial class Utility
{
[Group]
public partial class RemindCommands : NadekoSubmodule<RemindService>
{
public enum MeOrHere
{
Me,
Here
}
public enum Server
{
Server = int.MinValue,
Srvr = int.MinValue,
Serv = int.MinValue,
S = int.MinValue
}
private readonly DbService _db;
private readonly GuildTimezoneService _tz;
public RemindCommands(DbService db, GuildTimezoneService tz)
{
_db = db;
_tz = tz;
}
[Cmd]
[Priority(1)]
public async partial Task Remind(MeOrHere meorhere, [Leftover] string remindString)
{
if (!_service.TryParseRemindMessage(remindString, out var remindData))
{
await ReplyErrorLocalizedAsync(strs.remind_invalid);
return;
}
ulong target;
target = meorhere == MeOrHere.Me ? ctx.User.Id : ctx.Channel.Id;
if (!await RemindInternal(target,
meorhere == MeOrHere.Me || ctx.Guild is null,
remindData.Time,
remindData.What)) await ReplyErrorLocalizedAsync(strs.remind_too_long);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[Priority(0)]
public async partial Task Remind(ITextChannel channel, [Leftover] string remindString)
{
var perms = ((IGuildUser)ctx.User).GetPermissions(channel);
if (!perms.SendMessages || !perms.ViewChannel)
{
await ReplyErrorLocalizedAsync(strs.cant_read_or_send);
return;
}
if (!_service.TryParseRemindMessage(remindString, out var remindData))
{
await ReplyErrorLocalizedAsync(strs.remind_invalid);
return;
}
if (!await RemindInternal(channel.Id, false, remindData.Time, remindData.What))
await ReplyErrorLocalizedAsync(strs.remind_too_long);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(0)]
public partial Task RemindList(Server _, int page = 1)
=> RemindListInternal(page, true);
[Cmd]
[Priority(1)]
public partial Task RemindList(int page = 1)
=> RemindListInternal(page, false);
private async Task RemindListInternal(int page, bool isServer)
{
if (--page < 0)
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle(GetText(isServer ? strs.reminder_server_list : strs.reminder_list));
List<Reminder> rems;
await using (var uow = _db.GetDbContext())
{
if (isServer)
rems = uow.Reminders.RemindersForServer(ctx.Guild.Id, page).ToList();
else
rems = uow.Reminders.RemindersFor(ctx.User.Id, page).ToList();
}
if (rems.Any())
{
var i = 0;
foreach (var rem in rems)
{
var when = rem.When;
var diff = when - DateTime.UtcNow;
embed.AddField(
$"#{++i + (page * 10)} {rem.When:HH:mm yyyy-MM-dd} UTC "
+ $"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: Culture)})",
$@"`Target:` {(rem.IsPrivate ? "DM" : "Channel")}
`TargetId:` {rem.ChannelId}
`Message:` {rem.Message?.TrimTo(50)}");
}
}
else
{
embed.WithDescription(GetText(strs.reminders_none));
}
embed.AddPaginatedFooter(page + 1, null);
await ctx.Channel.EmbedAsync(embed);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(0)]
public partial Task RemindDelete(Server _, int index)
=> RemindDelete(index, true);
[Cmd]
[Priority(1)]
public partial Task RemindDelete(int index)
=> RemindDelete(index, false);
private async Task RemindDelete(int index, bool isServer)
{
if (--index < 0)
return;
Reminder rem = null;
await using (var uow = _db.GetDbContext())
{
var rems = isServer
? uow.Reminders.RemindersForServer(ctx.Guild.Id, index / 10).ToList()
: uow.Reminders.RemindersFor(ctx.User.Id, index / 10).ToList();
var pageIndex = index % 10;
if (rems.Count > pageIndex)
{
rem = rems[pageIndex];
uow.Reminders.Remove(rem);
uow.SaveChanges();
}
}
if (rem is null)
await ReplyErrorLocalizedAsync(strs.reminder_not_exist);
else
await ReplyConfirmLocalizedAsync(strs.reminder_deleted(index + 1));
}
private async Task<bool> RemindInternal(
ulong targetId,
bool isPrivate,
TimeSpan ts,
string message)
{
var time = DateTime.UtcNow + ts;
if (ts > TimeSpan.FromDays(60))
return false;
if (ctx.Guild is not null)
{
var perms = ((IGuildUser)ctx.User).GetPermissions((IGuildChannel)ctx.Channel);
if (!perms.MentionEveryone) message = message.SanitizeAllMentions();
}
var rem = new Reminder
{
ChannelId = targetId,
IsPrivate = isPrivate,
When = time,
Message = message,
UserId = ctx.User.Id,
ServerId = ctx.Guild?.Id ?? 0
};
await using (var uow = _db.GetDbContext())
{
uow.Reminders.Add(rem);
await uow.SaveChangesAsync();
}
var gTime = ctx.Guild is null ? time : TimeZoneInfo.ConvertTime(time, _tz.GetTimeZoneOrUtc(ctx.Guild.Id));
try
{
await SendConfirmAsync("⏰ "
+ GetText(strs.remind(
Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),
Format.Bold(message),
ts.Humanize(3, minUnit: TimeUnit.Second, culture: Culture),
gTime,
gTime)));
}
catch
{
}
return true;
}
}
}

View File

@@ -0,0 +1,166 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.Utility.Services;
public class RemindService : INService
{
private readonly Regex _regex =
new(
@"^(?:in\s?)?\s*(?:(?<mo>\d+)(?:\s?(?:months?|mos?),?))?(?:(?:\sand\s|\s*)?(?<w>\d+)(?:\s?(?:weeks?|w),?))?(?:(?:\sand\s|\s*)?(?<d>\d+)(?:\s?(?:days?|d),?))?(?:(?:\sand\s|\s*)?(?<h>\d+)(?:\s?(?:hours?|h),?))?(?:(?:\sand\s|\s*)?(?<m>\d+)(?:\s?(?:minutes?|mins?|m),?))?\s+(?:to:?\s+)?(?<what>(?:\r\n|[\r\n]|.)+)",
RegexOptions.Compiled | RegexOptions.Multiline);
private readonly DiscordSocketClient _client;
private readonly DbService _db;
private readonly IBotCredentials _creds;
private readonly IEmbedBuilderService _eb;
public RemindService(
DiscordSocketClient client,
DbService db,
IBotCredentials creds,
IEmbedBuilderService eb)
{
_client = client;
_db = db;
_creds = creds;
_eb = eb;
_ = StartReminderLoop();
}
private async Task StartReminderLoop()
{
while (true)
{
await Task.Delay(15000);
try
{
var now = DateTime.UtcNow;
var reminders = await GetRemindersBeforeAsync(now);
if (reminders.Count == 0)
continue;
Log.Information($"Executing {reminders.Count} reminders.");
// make groups of 5, with 1.5 second inbetween each one to ensure against ratelimits
foreach (var group in reminders.Chunk(5))
{
var executedReminders = group.ToList();
await executedReminders.Select(ReminderTimerAction).WhenAll();
await RemoveReminders(executedReminders);
await Task.Delay(1500);
}
}
catch (Exception ex)
{
Log.Warning($"Error in reminder loop: {ex.Message}");
Log.Warning(ex.ToString());
}
}
}
private async Task RemoveReminders(List<Reminder> reminders)
{
await using var uow = _db.GetDbContext();
uow.Set<Reminder>().RemoveRange(reminders);
await uow.SaveChangesAsync();
}
private Task<List<Reminder>> GetRemindersBeforeAsync(DateTime now)
{
using var uow = _db.GetDbContext();
return uow.Reminders
.FromSqlInterpolated(
$"select * from reminders where ((serverid >> 22) % {_creds.TotalShards}) == {_client.ShardId} and \"when\" < {now};")
.ToListAsync();
}
public bool TryParseRemindMessage(string input, out RemindObject obj)
{
var m = _regex.Match(input);
obj = default;
if (m.Length == 0) return false;
var values = new Dictionary<string, int>();
var what = m.Groups["what"].Value;
if (string.IsNullOrWhiteSpace(what))
{
Log.Warning("No message provided for the reminder.");
return false;
}
foreach (var groupName in _regex.GetGroupNames())
{
if (groupName is "0" or "what") continue;
if (string.IsNullOrWhiteSpace(m.Groups[groupName].Value))
{
values[groupName] = 0;
continue;
}
if (!int.TryParse(m.Groups[groupName].Value, out var value))
{
Log.Warning($"Reminder regex group {groupName} has invalid value.");
return false;
}
if (value < 1)
{
Log.Warning("Reminder time value has to be an integer greater than 0.");
return false;
}
values[groupName] = value;
}
var ts = new TimeSpan((30 * values["mo"]) + (7 * values["w"]) + values["d"], values["h"], values["m"], 0);
obj = new() { Time = ts, What = what };
return true;
}
private async Task ReminderTimerAction(Reminder r)
{
try
{
IMessageChannel ch;
if (r.IsPrivate)
{
var user = _client.GetUser(r.ChannelId);
if (user is null)
return;
ch = await user.CreateDMChannelAsync();
}
else
{
ch = _client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId);
}
if (ch is null)
return;
await ch.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("Reminder")
.AddField("Created At",
r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
.AddField("By",
(await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()),
r.Message);
}
catch (Exception ex) { Log.Information(ex.Message + $"({r.Id})"); }
}
public struct RemindObject
{
public string What { get; set; }
public TimeSpan Time { get; set; }
}
}