From 0eaaa625f10bbbc5879401f536f41664ca2b63f8 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 17 Apr 2025 09:35:13 +0200 Subject: [PATCH] Implements giving and removing of activehost and activeclient roles --- .../CodexChecking/ActiveP2pRoleRemover.cs | 16 +++- Tools/BiblioTech/CommandHandler.cs | 2 +- .../Commands/CheckResponseHandler.cs | 4 +- Tools/BiblioTech/LoggingRoleDriver.cs | 45 ++++++++- .../Rewards/ChainActivityHandler.cs | 94 +++++++++++++++++-- Tools/BiblioTech/Rewards/RewardController.cs | 12 ++- Tools/BiblioTech/Rewards/RoleDriver.cs | 49 ++++++++-- Tools/BiblioTech/Rewards/RoleModifyContext.cs | 12 +-- 8 files changed, 197 insertions(+), 37 deletions(-) diff --git a/Tools/BiblioTech/CodexChecking/ActiveP2pRoleRemover.cs b/Tools/BiblioTech/CodexChecking/ActiveP2pRoleRemover.cs index 4a448792..2d8ec602 100644 --- a/Tools/BiblioTech/CodexChecking/ActiveP2pRoleRemover.cs +++ b/Tools/BiblioTech/CodexChecking/ActiveP2pRoleRemover.cs @@ -1,4 +1,5 @@ -using Discord; +using BiblioTech.Rewards; +using Discord; using Logging; using System.Threading.Tasks; @@ -50,7 +51,18 @@ namespace BiblioTech.CodexChecking { var expiryMoment = DateTime.UtcNow - TimeSpan.FromMinutes(config.ActiveP2pRoleDurationMinutes); - Program.RoleDriver.IterateRemoveActiveP2pParticipants(p => ShouldRemoveRole(p, expiryMoment)); + Program.RoleDriver.IterateUsersWithRoles( + (g, u, r) => OnUserWithRole(g, u, r, expiryMoment), + Program.Config.ActiveP2pParticipantRoleId); + } + + private async Task OnUserWithRole(IRoleGiver giver, IUser user, ulong roleId, DateTime expiryMoment) + { + var report = repo.GetOrCreate(user.Id); + if (report.UploadCheck.CompletedUtc > expiryMoment) return; + if (report.DownloadCheck.CompletedUtc > expiryMoment) return; + + await giver.RemoveActiveP2pParticipant(user.Id); } private bool ShouldRemoveRole(IUser user, DateTime expiryMoment) diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs index 5a7a83e7..822d4446 100644 --- a/Tools/BiblioTech/CommandHandler.cs +++ b/Tools/BiblioTech/CommandHandler.cs @@ -42,7 +42,7 @@ namespace BiblioTech Program.AdminChecker.SetAdminChannel(adminChannel); Program.RoleDriver = new RoleDriver(client, Program.UserRepo, log, rewardsChannel); - Program.ChainActivityHandler = new ChainActivityHandler(log); + Program.ChainActivityHandler = new ChainActivityHandler(log, Program.UserRepo); Program.EventsSender = new ChainEventsSender(log, replacement, chainEventsChannel); var builders = commands.Select(c => diff --git a/Tools/BiblioTech/Commands/CheckResponseHandler.cs b/Tools/BiblioTech/Commands/CheckResponseHandler.cs index ca9dedd4..5428e86b 100644 --- a/Tools/BiblioTech/Commands/CheckResponseHandler.cs +++ b/Tools/BiblioTech/Commands/CheckResponseHandler.cs @@ -58,8 +58,8 @@ namespace BiblioTech.Commands { await Program.RoleDriver.RunRoleGiver(async r => { - await r.GiveAltruisticRole(user); - await r.GiveActiveP2pParticipant(user); + await r.GiveAltruisticRole(user.Id); + await r.GiveActiveP2pParticipant(user.Id); }); await context.Followup($"Congratulations! You've been granted the Altruistic Mode role!"); } diff --git a/Tools/BiblioTech/LoggingRoleDriver.cs b/Tools/BiblioTech/LoggingRoleDriver.cs index a5631271..b1759af9 100644 --- a/Tools/BiblioTech/LoggingRoleDriver.cs +++ b/Tools/BiblioTech/LoggingRoleDriver.cs @@ -21,7 +21,12 @@ namespace BiblioTech await action(new LoggingRoleGiver(log)); } - public async Task IterateRemoveActiveP2pParticipants(Func predicate) + public async Task IterateUsersWithRoles(Func onUserWithRole, params ulong[] rolesToIterate) + { + await Task.CompletedTask; + } + + public async Task IterateUsersWithRoles(Func onUserWithRole, Func whenDone, params ulong[] rolesToIterate) { await Task.CompletedTask; } @@ -35,15 +40,45 @@ namespace BiblioTech this.log = log; } - public async Task GiveActiveP2pParticipant(IUser user) + public async Task GiveActiveClient(ulong userId) { - log.Log($"Giving ActiveP2p role to " + user.Id); + log.Log($"Giving ActiveClient role to " + userId); await Task.CompletedTask; } - public async Task GiveAltruisticRole(IUser user) + public async Task GiveActiveHost(ulong userId) { - log.Log($"Giving Altruistic role to " + user.Id); + log.Log($"Giving ActiveHost role to " + userId); + await Task.CompletedTask; + } + + public async Task GiveActiveP2pParticipant(ulong userId) + { + log.Log($"Giving ActiveP2p role to " + userId); + await Task.CompletedTask; + } + + public async Task RemoveActiveP2pParticipant(ulong userId) + { + log.Log($"Removing ActiveP2p role from " + userId); + await Task.CompletedTask; + } + + public async Task GiveAltruisticRole(ulong userId) + { + log.Log($"Giving Altruistic role to " + userId); + await Task.CompletedTask; + } + + public async Task RemoveActiveClient(ulong userId) + { + log.Log($"Removing ActiveClient role from " + userId); + await Task.CompletedTask; + } + + public async Task RemoveActiveHost(ulong userId) + { + log.Log($"Removing ActiveHost role from " + userId); await Task.CompletedTask; } } diff --git a/Tools/BiblioTech/Rewards/ChainActivityHandler.cs b/Tools/BiblioTech/Rewards/ChainActivityHandler.cs index 2025725a..a3bb17a8 100644 --- a/Tools/BiblioTech/Rewards/ChainActivityHandler.cs +++ b/Tools/BiblioTech/Rewards/ChainActivityHandler.cs @@ -1,4 +1,5 @@ -using DiscordRewards; +using Discord; +using DiscordRewards; using Logging; namespace BiblioTech.Rewards @@ -7,6 +8,7 @@ namespace BiblioTech.Rewards { private readonly ILog log; private readonly UserRepo repo; + private ActiveUserIds? previousIds = null; public ChainActivityHandler(ILog log, UserRepo repo) { @@ -18,17 +20,85 @@ namespace BiblioTech.Rewards { var activeUserIds = ConvertToUserIds(activeChainAddresses); if (!activeUserIds.HasAny()) return; + + if (!HasChanged(activeUserIds)) return; - todo call role driver to add roles to new activeIds or remove them. + await GiveAndRemoveRoles(activeUserIds); + } + + private async Task GiveAndRemoveRoles(ActiveUserIds activeUserIds) + { + await Program.RoleDriver.IterateUsersWithRoles( + (g, u, r) => OnUserWithRole(g, u, r, activeUserIds), + whenDone: g => GiveRolesToRemaining(g, activeUserIds), + Program.Config.ActiveClientRoleId, + Program.Config.ActiveHostRoleId); + } + + private async Task OnUserWithRole(IRoleGiver giver, IUser user, ulong roleId, ActiveUserIds activeIds) + { + if (roleId == Program.Config.ActiveClientRoleId) + { + await CheckUserWithRole(user, activeIds.Clients, giver.RemoveActiveClient); + } + else if (roleId == Program.Config.ActiveHostRoleId) + { + await CheckUserWithRole(user, activeIds.Hosts, giver.RemoveActiveHost); + } + else + { + throw new Exception("Unknown roleId received!"); + } + } + + private async Task CheckUserWithRole(IUser user, List activeUsers, Func removeActiveRole) + { + if (ShouldUserHaveRole(user, activeUsers)) + { + activeUsers.Remove(user.Id); + } + else + { + await removeActiveRole(user.Id); + } + } + + private bool ShouldUserHaveRole(IUser user, List activeUsers) + { + return activeUsers.Any(id => id == user.Id); + } + + private async Task GiveRolesToRemaining(IRoleGiver giver, ActiveUserIds ids) + { + foreach (var client in ids.Clients) await giver.GiveActiveClient(client); + foreach (var host in ids.Hosts) await giver.GiveActiveHost(host); + } + + private bool HasChanged(ActiveUserIds activeUserIds) + { + if (previousIds == null) + { + previousIds = activeUserIds; + return true; + } + + if (!IsEquivalent(previousIds.Hosts, activeUserIds.Hosts)) return true; + if (!IsEquivalent(previousIds.Clients, activeUserIds.Clients)) return true; + return false; + } + + private static bool IsEquivalent(IEnumerable a, IEnumerable b) + { + return a.SequenceEqual(b); } private ActiveUserIds ConvertToUserIds(ActiveChainAddresses activeChainAddresses) { return new ActiveUserIds - { - Hosts = Map(activeChainAddresses.Hosts), - Clients = Map(activeChainAddresses.Clients) - }; + ( + hosts: Map(activeChainAddresses.Hosts), + clients: Map(activeChainAddresses.Clients) + ); } private ulong[] Map(string[] ethAddresses) @@ -43,13 +113,19 @@ namespace BiblioTech.Rewards } } - return result.ToArray(); + return result.Order().ToArray(); } private class ActiveUserIds { - public ulong[] Hosts { get; set; } = Array.Empty(); - public ulong[] Clients { get; set; } = Array.Empty(); + public ActiveUserIds(IEnumerable hosts, IEnumerable clients) + { + Hosts = hosts.ToList(); + Clients = clients.ToList(); + } + + public List Hosts { get; } + public List Clients { get; } public bool HasAny() { diff --git a/Tools/BiblioTech/Rewards/RewardController.cs b/Tools/BiblioTech/Rewards/RewardController.cs index 7cbed562..7a4b8aa3 100644 --- a/Tools/BiblioTech/Rewards/RewardController.cs +++ b/Tools/BiblioTech/Rewards/RewardController.cs @@ -11,13 +11,19 @@ namespace BiblioTech.Rewards public interface IDiscordRoleDriver { Task RunRoleGiver(Func action); - Task IterateRemoveActiveP2pParticipants(Func predicate); + Task IterateUsersWithRoles(Func onUserWithRole, params ulong[] rolesToIterate); + Task IterateUsersWithRoles(Func onUserWithRole, Func whenDone, params ulong[] rolesToIterate); } public interface IRoleGiver { - Task GiveAltruisticRole(IUser user); - Task GiveActiveP2pParticipant(IUser user); + Task GiveAltruisticRole(ulong userId); + Task GiveActiveP2pParticipant(ulong userId); + Task RemoveActiveP2pParticipant(ulong userId); + Task GiveActiveHost(ulong userId); + Task RemoveActiveHost(ulong userId); + Task GiveActiveClient(ulong userId); + Task RemoveActiveClient(ulong userId); } [Route("api/[controller]")] diff --git a/Tools/BiblioTech/Rewards/RoleDriver.cs b/Tools/BiblioTech/Rewards/RoleDriver.cs index c716c011..41b0e44c 100644 --- a/Tools/BiblioTech/Rewards/RoleDriver.cs +++ b/Tools/BiblioTech/Rewards/RoleDriver.cs @@ -30,20 +30,26 @@ namespace BiblioTech.Rewards await action(mapper); } - public async Task IterateRemoveActiveP2pParticipants(Func shouldRemove) + public async Task IterateUsersWithRoles(Func onUserWithRole, params ulong[] rolesToIterate) + { + await IterateUsersWithRoles(onUserWithRole, g => Task.CompletedTask, rolesToIterate); + } + + public async Task IterateUsersWithRoles(Func onUserWithRole, Func whenDone, params ulong[] rolesToIterate) { var context = await OpenRoleModifyContext(); + var mapper = new RoleMapper(context); foreach (var user in context.Users) { - if (user.RoleIds.Any(r => r == Program.Config.ActiveP2pParticipantRoleId)) + foreach (var role in rolesToIterate) { - // This user has the role. Should it be removed? - if (shouldRemove(user)) + if (user.RoleIds.Contains(role)) { - await context.RemoveRole(user, Program.Config.ActiveP2pParticipantRoleId); + await onUserWithRole(mapper, user, role); } } } + await whenDone(mapper); } private async Task OpenRoleModifyContext() @@ -74,14 +80,39 @@ namespace BiblioTech.Rewards this.context = context; } - public async Task GiveActiveP2pParticipant(IUser user) + public async Task GiveActiveClient(ulong userId) { - await context.GiveRole(user, Program.Config.ActiveP2pParticipantRoleId); + await context.GiveRole(userId, Program.Config.ActiveClientRoleId); } - public async Task GiveAltruisticRole(IUser user) + public async Task GiveActiveHost(ulong userId) { - await context.GiveRole(user, Program.Config.AltruisticRoleId); + await context.GiveRole(userId, Program.Config.ActiveHostRoleId); + } + + public async Task GiveActiveP2pParticipant(ulong userId) + { + await context.GiveRole(userId, Program.Config.ActiveP2pParticipantRoleId); + } + + public async Task RemoveActiveP2pParticipant(ulong userId) + { + await context.RemoveRole(userId, Program.Config.ActiveP2pParticipantRoleId); + } + + public async Task GiveAltruisticRole(ulong userId) + { + await context.GiveRole(userId, Program.Config.AltruisticRoleId); + } + + public async Task RemoveActiveClient(ulong userId) + { + await context.RemoveRole(userId, Program.Config.ActiveClientRoleId); + } + + public async Task RemoveActiveHost(ulong userId) + { + await context.RemoveRole(userId, Program.Config.ActiveHostRoleId); } } } diff --git a/Tools/BiblioTech/Rewards/RoleModifyContext.cs b/Tools/BiblioTech/Rewards/RoleModifyContext.cs index f65d4cf2..58914f5e 100644 --- a/Tools/BiblioTech/Rewards/RoleModifyContext.cs +++ b/Tools/BiblioTech/Rewards/RoleModifyContext.cs @@ -31,28 +31,28 @@ namespace BiblioTech.Rewards public IGuildUser[] Users => users.Values.ToArray(); - public async Task GiveRole(IUser user, ulong roleId) + public async Task GiveRole(ulong userId, ulong roleId) { var role = GetRole(roleId); - var guildUser = GetUser(user.Id); + var guildUser = GetUser(userId); if (role == null) return; if (guildUser == null) return; await guildUser.AddRoleAsync(role); - await Program.AdminChecker.SendInAdminChannel($"Added role '{role.Name}' for user <@{user.Id}>."); + await Program.AdminChecker.SendInAdminChannel($"Added role '{role.Name}' for user <@{userId}>."); await SendNotification(guildUser, role); } - public async Task RemoveRole(IUser user, ulong roleId) + public async Task RemoveRole(ulong userId, ulong roleId) { var role = GetRole(roleId); - var guildUser = GetUser(user.Id); + var guildUser = GetUser(userId); if (role == null) return; if (guildUser == null) return; await guildUser.RemoveRoleAsync(role); - await Program.AdminChecker.SendInAdminChannel($"Removed role '{role.Name}' for user <@{user.Id}>."); + await Program.AdminChecker.SendInAdminChannel($"Removed role '{role.Name}' for user <@{userId}>."); } private SocketRole? GetRole(ulong roleId)