public async Task Command(int users = 0, int seconds = 0, string action = null) { Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); AntiRaidConfig antiraid = server.AntiRaid; if (users == 0 && seconds == 0 && action == null) { if (antiraid != null) { await DatabaseQueries.DeleteAllForServerAsync <AntiRaidConfig>(server.ServerId); await SendBasicSuccessEmbedAsync("Successfully disabled this server's antiraid protection."); return; } await SendBasicErrorEmbedAsync("This server has not setup the antiraid service, therefore " + "there is nothing to disable."); return; } if (users < 1 || seconds < 5 && action != null) { if (users < 3) { throw new ArgumentOutOfRangeException(nameof(users), "There must be at least `3` users to action before " + "it is classified as a raid."); } if (seconds < 5) { throw new ArgumentOutOfRangeException(nameof(seconds), "The seconds parameter must be at least `5`."); } } if (users > 200) { throw new ArgumentOutOfRangeException(nameof(users), "The users count must not be greater than `200`."); } if (seconds > 999) { throw new ArgumentOutOfRangeException(nameof(seconds), "The `seconds` duration must not be greater than `999`."); } if (action == null) { throw new ArgumentNullException(nameof(action), "The action must not be null and must be either " + "`mute`, `kick`, `shadowban`, or `ban`."); } string[] actions = { "mute", "kick", "shadowban", "ban" }; if (!actions.Any(x => x.Equals(action.ToLower()))) { throw new ArgumentOutOfRangeException(nameof(action), "The action must be either `mute`, `kick`, `shadowban`, " + "or `ban`."); } var ar = new AntiRaidConfig { ServerId = server.ServerId, Users = users, Seconds = seconds, Action = action.ToLower(), Server = server }; await SendBasicSuccessEmbedAsync( $"Successfully enabled the antiraid service for `{Context.Guild.Name}`.\n\n" + $"I will `{action.ToUpper()}` anyone part of a raid. A raid is now defined as " + $"`{users.ToWords()}` users joining within `{seconds.ToWords()}` seconds " + $"of each other."); if (antiraid != null) { await DatabaseQueries.InsertOrReplaceAsync(ar); return; } await DatabaseQueries.InsertOrReplaceAsync(ar); }
public static async Task Initialize() => await Task.Run(() => { DiscordShardedClient client = ConfigProperties.Client; client.UserJoined += async u => { SocketGuild guild = u.Guild; Server server = await DatabaseQueries.GetOrCreateServerAsync(guild.Id); if (server.AntiRaid == null) { return; } AntiRaidConfig ar = server.AntiRaid; if (!ActiveAntiraids.Raids.Any(x => x.ServerId == server.ServerId)) { var newRaidData = new RaidData { ServerId = server.ServerId, UserIds = new HashSet <ulong> { u.Id } }; ActiveAntiraids.SafeAdd(newRaidData); } else { var newIds = new HashSet <ulong>(); HashSet <ulong> existingIds = ActiveAntiraids.Raids.First(x => x.ServerId == server.ServerId).UserIds; // Copies existing IDs from the known / active raid into a new HashSet<ulong>. foreach (ulong id in existingIds) { newIds.Add(id); } // Finally, add the current user's ID to the HashSet. newIds.Add(u.Id); // Replace the object in memory. ActiveAntiraids.ReplaceRaidData(new RaidData { ServerId = server.ServerId, UserIds = newIds }); } var timer = new Timer(ar.Seconds * 1000); timer.Enabled = true; timer.AutoReset = false; timer.Elapsed += async(sender, args) => { RaidData existingObj = ActiveAntiraids.Raids.FirstOrDefault(x => x.ServerId == server.ServerId); if (existingObj == null) { return; } // Filter by distinct to fix the duplicate users issue. existingObj.UserIds = existingObj.UserIds.Distinct().ToHashSet(); // This must be above ActionUsers() as it takes a few seconds for users to be actioned // resulting in duplicate log messages and ban attempts. ActiveAntiraids.RemoveAll(existingObj); if (existingObj.UserIds.Count >= ar.Users) { await ActionUsers(existingObj.UserIds, server.ServerId, ar.Action); } }; }; });