Implements giving and removing of activehost and activeclient roles

This commit is contained in:
Ben 2025-04-17 09:35:13 +02:00
parent a2c8c18c5c
commit 0eaaa625f1
No known key found for this signature in database
GPG Key ID: 0F16E812E736C24B
8 changed files with 197 additions and 37 deletions

View File

@ -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)

View File

@ -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 =>

View File

@ -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!");
}

View File

@ -21,7 +21,12 @@ namespace BiblioTech
await action(new LoggingRoleGiver(log));
}
public async Task IterateRemoveActiveP2pParticipants(Func<IUser, bool> predicate)
public async Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, params ulong[] rolesToIterate)
{
await Task.CompletedTask;
}
public async Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, Func<IRoleGiver, Task> 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;
}
}

View File

@ -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<ulong> activeUsers, Func<ulong, Task> removeActiveRole)
{
if (ShouldUserHaveRole(user, activeUsers))
{
activeUsers.Remove(user.Id);
}
else
{
await removeActiveRole(user.Id);
}
}
private bool ShouldUserHaveRole(IUser user, List<ulong> 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<ulong> a, IEnumerable<ulong> 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<ulong>();
public ulong[] Clients { get; set; } = Array.Empty<ulong>();
public ActiveUserIds(IEnumerable<ulong> hosts, IEnumerable<ulong> clients)
{
Hosts = hosts.ToList();
Clients = clients.ToList();
}
public List<ulong> Hosts { get; }
public List<ulong> Clients { get; }
public bool HasAny()
{

View File

@ -11,13 +11,19 @@ namespace BiblioTech.Rewards
public interface IDiscordRoleDriver
{
Task RunRoleGiver(Func<IRoleGiver, Task> action);
Task IterateRemoveActiveP2pParticipants(Func<IUser, bool> predicate);
Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, params ulong[] rolesToIterate);
Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, Func<IRoleGiver, Task> 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]")]

View File

@ -30,20 +30,26 @@ namespace BiblioTech.Rewards
await action(mapper);
}
public async Task IterateRemoveActiveP2pParticipants(Func<IUser, bool> shouldRemove)
public async Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, params ulong[] rolesToIterate)
{
await IterateUsersWithRoles(onUserWithRole, g => Task.CompletedTask, rolesToIterate);
}
public async Task IterateUsersWithRoles(Func<IRoleGiver, IUser, ulong, Task> onUserWithRole, Func<IRoleGiver, Task> 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<RoleModifyContext> 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);
}
}
}

View File

@ -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)