add: notify, minesweeper, migrations

dev: renames, refactors
change: remind optimized wait
This commit is contained in:
Kwoth
2024-12-03 14:13:31 +00:00
parent d583e2b99a
commit 0f240925e8
32 changed files with 8073 additions and 222 deletions

View File

@@ -9,6 +9,7 @@ public sealed class AfkService : INService, IReadyExecutor
private readonly MessageSenderService _mss;
private static readonly TimeSpan _maxAfkDuration = 8.Hours();
public AfkService(IBotCache cache, DiscordSocketClient client, MessageSenderService mss)
{
_cache = cache;
@@ -19,6 +20,9 @@ public sealed class AfkService : INService, IReadyExecutor
private static TypedKey<string> GetKey(ulong userId)
=> new($"afk:msg:{userId}");
private static TypedKey<bool> GetRecentlySentKey(ulong userId, ulong channelId)
=> new($"afk:recent:{userId}:{channelId}");
public async Task<bool> SetAfkAsync(ulong userId, string text)
{
var added = await _cache.AddAsync(GetKey(userId), text, _maxAfkDuration, overwrite: true);
@@ -43,9 +47,7 @@ public sealed class AfkService : INService, IReadyExecutor
msg.DeleteAfter(5);
});
}
}
}
catch (Exception ex)
{
@@ -61,7 +63,7 @@ public sealed class AfkService : INService, IReadyExecutor
await Task.Delay(_maxAfkDuration);
_client.MessageReceived -= StopAfk;
});
return added;
}
@@ -72,36 +74,29 @@ public sealed class AfkService : INService, IReadyExecutor
return Task.CompletedTask;
}
private Task TryTriggerAfkMessage(SocketMessage arg)
private Task TryTriggerAfkMessage(SocketMessage sm)
{
if (arg.Author.IsBot || arg.Author.IsWebhook)
if (sm.Author.IsBot || sm.Author.IsWebhook)
return Task.CompletedTask;
if (arg is not IUserMessage uMsg || uMsg.Channel is not ITextChannel tc)
if (sm is not IUserMessage uMsg || uMsg.Channel is not ITextChannel tc)
return Task.CompletedTask;
if ((arg.MentionedUsers.Count is 0 or > 3) && uMsg.ReferencedMessage is null)
if ((sm.MentionedUsers.Count is 0 or > 3) && uMsg.ReferencedMessage is null)
return Task.CompletedTask;
_ = Task.Run(async () =>
{
var botUser = await tc.Guild.GetCurrentUserAsync();
var perms = botUser.GetPermissions(tc);
if (!perms.SendMessages)
return;
ulong mentionedUserId = 0;
if (arg.MentionedUsers.Count <= 3)
if (sm.MentionedUsers.Count <= 3)
{
foreach (var uid in uMsg.MentionedUserIds)
{
if (uid == arg.Author.Id)
if (uid == sm.Author.Id)
continue;
if (arg.Content.StartsWith($"<@{uid}>") || arg.Content.StartsWith($"<@!{uid}>"))
if (sm.Content.StartsWith($"<@{uid}>") || sm.Content.StartsWith($"<@!{uid}>"))
{
mentionedUserId = uid;
break;
@@ -115,26 +110,46 @@ public sealed class AfkService : INService, IReadyExecutor
{
return;
}
mentionedUserId = repliedUserId;
}
try
{
var result = await _cache.GetAsync(GetKey(mentionedUserId));
if (result.TryPickT0(out var msg, out _))
{
var st = SmartText.CreateFrom(msg);
st = $"The user you've pinged (<#{mentionedUserId}>) is AFK: " + st;
var toDelete = await _mss.Response(arg.Channel)
.User(arg.Author)
var toDelete = await _mss.Response(sm.Channel)
.User(sm.Author)
.Message(uMsg)
.Text(st)
.SendAsync();
toDelete.DeleteAfter(30);
var botUser = await tc.Guild.GetCurrentUserAsync();
var perms = botUser.GetPermissions(tc);
if (!perms.SendMessages)
return;
var key = GetRecentlySentKey(mentionedUserId, sm.Channel.Id);
var recent = await _cache.GetAsync(key);
if (!recent.TryPickT0(out _, out _))
{
var chMsg = await _mss.Response(sm.Channel)
.Message(uMsg)
.Pending(strs.user_afk($"<@{mentionedUserId}>"))
.SendAsync();
chMsg.DeleteAfter(5);
await _cache.AddAsync(key, true, expiry: TimeSpan.FromMinutes(5));
}
}
}
catch (HttpException ex)

View File

@@ -98,10 +98,10 @@ public partial class Utility
return;
var embed = CreateEmbed()
.WithOkColor()
.WithTitle(GetText(guildId is not null
? strs.reminder_server_list
: strs.reminder_list));
.WithOkColor()
.WithTitle(GetText(guildId is not null
? strs.reminder_server_list
: strs.reminder_list));
List<Reminder> rems;
if (guildId is { } gid)
@@ -193,23 +193,14 @@ public partial class Utility
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 _service.AddReminderAsync(ctx.User.Id,
targetId,
ctx.Guild?.Id,
isPrivate,
time,
message,
ReminderType.User);
await using (var uow = _db.GetDbContext())
{
uow.Set<Reminder>().Add(rem);
await uow.SaveChangesAsync();
}
// var gTime = ctx.Guild is null ? time : TimeZoneInfo.ConvertTime(time, _tz.GetTimeZoneOrUtc(ctx.Guild.Id));
await Response()
.Confirm($"\u23f0 {GetText(strs.remind2(
Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),

View File

@@ -21,6 +21,8 @@ public class RemindService : INService, IReadyExecutor, IRemindService
private readonly IMessageSenderService _sender;
private readonly CultureInfo _culture;
private TaskCompletionSource<bool> _tcs;
public RemindService(
DiscordSocketClient client,
DbService db,
@@ -44,8 +46,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
public async Task OnReadyAsync()
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(15));
while (await timer.WaitForNextTickAsync())
while (true)
{
await OnReminderLoopTickInternalAsync();
}
@@ -55,8 +56,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
{
try
{
var now = DateTime.UtcNow;
var reminders = await GetRemindersBeforeAsync(now);
var reminders = await GetRemindersBeforeAsync();
if (reminders.Count == 0)
return;
@@ -67,7 +67,6 @@ public class RemindService : INService, IReadyExecutor, IRemindService
{
var executedReminders = group.ToList();
await executedReminders.Select(ReminderTimerAction).WhenAll();
await RemoveReminders(executedReminders.Select(x => x.Id));
await Task.Delay(1500);
}
}
@@ -80,21 +79,51 @@ public class RemindService : INService, IReadyExecutor, IRemindService
private async Task RemoveReminders(IEnumerable<int> reminders)
{
await using var uow = _db.GetDbContext();
await uow.Set<Reminder>()
.ToLinqToDBTable()
await uow.GetTable<Reminder>()
.DeleteAsync(x => reminders.Contains(x.Id));
await uow.SaveChangesAsync();
}
private async Task<List<Reminder>> GetRemindersBeforeAsync(DateTime now)
private async Task<IReadOnlyList<Reminder>> GetRemindersBeforeAsync()
{
await using var uow = _db.GetDbContext();
return await uow.Set<Reminder>()
.ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId, _creds.TotalShards, _client.ShardId)
&& x.When < now)
.ToListAsyncLinqToDB();
while (true)
{
_tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
await using var uow = _db.GetDbContext();
var earliest = await uow.Set<Reminder>()
.ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId,
_creds.TotalShards,
_client.ShardId))
.OrderBy(x => x.When)
.FirstOrDefaultAsyncLinqToDB();
if (earliest == default)
{
await _tcs.Task;
continue;
}
var now = DateTime.UtcNow;
if (earliest.When > now)
{
var diff = earliest.When - now;
// Log.Information("Waiting for {Diff}", diff);
await Task.WhenAny(Task.Delay(diff), _tcs.Task);
continue;
}
var reminders = await uow.Set<Reminder>()
.ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId,
_creds.TotalShards,
_client.ShardId))
.Where(x => x.When <= now)
.DeleteWithOutputAsync();
return reminders;
}
}
public bool TryParseRemindMessage(string input, out RemindObject obj)
@@ -243,21 +272,24 @@ public class RemindService : INService, IReadyExecutor, IRemindService
string message,
ReminderType reminderType)
{
var rem = new Reminder
await using (var ctx = _db.GetDbContext())
{
UserId = userId,
ChannelId = targetId,
ServerId = guildId ?? 0,
IsPrivate = isPrivate,
When = time,
Message = message,
Type = reminderType
};
await ctx.GetTable<Reminder>()
.InsertAsync(() => new Reminder
{
UserId = userId,
ChannelId = targetId,
ServerId = guildId ?? 0,
IsPrivate = isPrivate,
When = time,
Message = message,
Type = reminderType,
DateAdded = DateTime.UtcNow
});
await ctx.SaveChangesAsync();
}
await using var ctx = _db.GetDbContext();
await ctx.Set<Reminder>()
.AddAsync(rem);
await ctx.SaveChangesAsync();
_tcs.SetResult(true);
}
public async Task<List<Reminder>> GetServerReminders(int page, ulong guildId)

View File

@@ -110,14 +110,14 @@ public partial class Utility
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[Priority(0)]
public Task Repeat(StoopidTime interval, [Leftover] string message)
public Task Repeat(ParsedTimespan interval, [Leftover] string message)
=> Repeat(ctx.Channel, null, interval, message);
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[Priority(0)]
public Task Repeat(ITextChannel channel, StoopidTime interval, [Leftover] string message)
public Task Repeat(ITextChannel channel, ParsedTimespan interval, [Leftover] string message)
=> Repeat(channel, null, interval, message);
[Cmd]
@@ -138,14 +138,14 @@ public partial class Utility
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[Priority(2)]
public Task Repeat(GuildDateTime? timeOfDay, StoopidTime? interval, [Leftover] string message)
public Task Repeat(GuildDateTime? timeOfDay, ParsedTimespan? interval, [Leftover] string message)
=> Repeat(ctx.Channel, timeOfDay, interval, message);
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[Priority(3)]
public async Task Repeat(IMessageChannel channel, GuildDateTime? timeOfDay, StoopidTime? interval,
public async Task Repeat(IMessageChannel channel, GuildDateTime? timeOfDay, ParsedTimespan? interval,
[Leftover] string message)
{
if (channel is not ITextChannel txtCh || txtCh.GuildId != ctx.Guild.Id)