RoleModifyContext.cs
1 using Discord.WebSocket; 2 using Discord; 3 using DiscordRewards; 4 using Nethereum.Model; 5 using Logging; 6 7 namespace BiblioTech.Rewards 8 { 9 public class RoleModifyContext 10 { 11 private Dictionary<ulong, IGuildUser> users = new(); 12 private Dictionary<ulong, SocketRole> roles = new(); 13 private DateTime lastLoad = DateTime.MinValue; 14 private readonly object _lock = new object(); 15 16 private readonly SocketGuild guild; 17 private readonly UserRepo userRepo; 18 private readonly ILog log; 19 private readonly SocketTextChannel? rewardsChannel; 20 21 public RoleModifyContext(SocketGuild guild, UserRepo userRepo, ILog log, SocketTextChannel? rewardsChannel) 22 { 23 this.guild = guild; 24 this.userRepo = userRepo; 25 this.log = log; 26 this.rewardsChannel = rewardsChannel; 27 } 28 29 public void Initialize() 30 { 31 lock (_lock) 32 { 33 var span = DateTime.UtcNow - lastLoad; 34 if (span > TimeSpan.FromMinutes(10)) 35 { 36 lastLoad = DateTime.UtcNow; 37 log.Log("Loading all users and roles..."); 38 var task = LoadAllUsers(guild); 39 task.Wait(); 40 this.users = task.Result; 41 this.roles = LoadAllRoles(guild); 42 } 43 } 44 } 45 46 public IGuildUser[] Users => users.Values.ToArray(); 47 48 public async Task GiveRole(ulong userId, ulong roleId) 49 { 50 Log($"Giving role {roleId} to user {userId}"); 51 var role = GetRole(roleId); 52 var guildUser = GetUser(userId); 53 if (role == null) return; 54 if (guildUser == null) return; 55 56 await guildUser.AddRoleAsync(role); 57 await Program.AdminChecker.SendInAdminChannel($"Added role '{role.Name}' for user <@{userId}>."); 58 59 await SendNotification(guildUser, role); 60 } 61 62 public async Task RemoveRole(ulong userId, ulong roleId) 63 { 64 Log($"Removing role {roleId} from user {userId}"); 65 var role = GetRole(roleId); 66 var guildUser = GetUser(userId); 67 if (role == null) return; 68 if (guildUser == null) return; 69 70 await guildUser.RemoveRoleAsync(role); 71 await Program.AdminChecker.SendInAdminChannel($"Removed role '{role.Name}' for user <@{userId}>."); 72 } 73 74 private SocketRole? GetRole(ulong roleId) 75 { 76 if (roles.ContainsKey(roleId)) return roles[roleId]; 77 return null; 78 } 79 80 private IGuildUser? GetUser(ulong userId) 81 { 82 if (users.ContainsKey(userId)) return users[userId]; 83 return null; 84 } 85 86 private void Log(string msg) 87 { 88 log.Log(msg); 89 } 90 91 private async Task<Dictionary<ulong, IGuildUser>> LoadAllUsers(SocketGuild guild) 92 { 93 var result = new Dictionary<ulong, IGuildUser>(); 94 var users = guild.GetUsersAsync(); 95 await foreach (var ulist in users) 96 { 97 foreach (var u in ulist) 98 { 99 result.Add(u.Id, u); 100 } 101 } 102 return result; 103 } 104 105 private Dictionary<ulong, SocketRole> LoadAllRoles(SocketGuild guild) 106 { 107 var result = new Dictionary<ulong, SocketRole>(); 108 var roles = guild.Roles.ToArray(); 109 foreach (var role in roles) 110 { 111 result.Add(role.Id, role); 112 } 113 return result; 114 } 115 116 private async Task SendNotification(IGuildUser user, SocketRole role) 117 { 118 try 119 { 120 var userData = userRepo.GetUser(user); 121 if (userData == null) return; 122 123 if (userData.NotificationsEnabled && rewardsChannel != null) 124 { 125 var msg = $"<@{user.Id}> has received '{role.Name}'."; 126 await rewardsChannel.SendMessageAsync(msg); 127 } 128 } 129 catch (Exception ex) 130 { 131 log.Error($"Failed to notify user '{user.DisplayName}' about role '{role.Name}': {ex}"); 132 } 133 } 134 } 135 }