public async Task ExecuteGroupAsync(CommandContext ctx,
                                                [Description("desc-enable")] bool enable,
                                                [Description("desc-sens")] short sens,
                                                [Description("desc-punish-action")] PunishmentAction action = PunishmentAction.TemporaryMute)
            {
                if (sens is < RatelimitSettings.MinSensitivity or > RatelimitSettings.MaxSensitivity)
                {
                    throw new CommandFailedException(ctx, "cmd-err-range-sens", RatelimitSettings.MinSensitivity, RatelimitSettings.MaxSensitivity);
                }

                var settings = new RatelimitSettings {
                    Action      = action,
                    Enabled     = enable,
                    Sensitivity = sens
                };

                await ctx.Services.GetRequiredService <GuildConfigService>().ModifyConfigAsync(ctx.Guild.Id, gcfg => gcfg.RatelimitSettings = settings);

                await ctx.GuildLogAsync(emb => {
                    emb.WithLocalizedTitle("evt-cfg-upd");
                    emb.WithColor(this.ModuleColor);
                    if (enable)
                    {
                        emb.WithLocalizedDescription("evt-rl-enabled");
                        emb.AddLocalizedTitleField("str-sensitivity", settings.Sensitivity, inline: true);
                        emb.AddLocalizedTitleField("str-punish-action", settings.Action.Humanize(), inline: true);
                    }
                    else
                    {
                        emb.WithLocalizedDescription("evt-rl-disabled");
                    }
                });

                await ctx.InfoAsync(this.ModuleColor, enable? "evt-rl-enabled" : "evt-rl-disabled");
            }
        private static bool HaveSamePropertyValues(CachedGuildConfig?first, CachedGuildConfig?second)
        {
            if (first is null || second is null)
            {
                return(false);
            }

            if (first.Currency != second.Currency)
            {
                return(false);
            }

            if (first.LoggingEnabled != second.LoggingEnabled || first.LogChannelId != second.LogChannelId)
            {
                return(false);
            }

            if (first.Prefix != second.Prefix)
            {
                return(false);
            }

            if (first.Locale != second.Locale || first.TimezoneId != second.TimezoneId)
            {
                return(false);
            }

            if (first.ReactionResponse != second.ReactionResponse || first.SuggestionsEnabled != second.SuggestionsEnabled)
            {
                return(false);
            }

            AntispamSettings as1 = first.AntispamSettings;
            AntispamSettings as2 = second.AntispamSettings;

            if (as1.Action != as2.Action || as1.Enabled != as2.Enabled || as1.Sensitivity != as2.Sensitivity)
            {
                return(false);
            }

            LinkfilterSettings ls1 = first.LinkfilterSettings;
            LinkfilterSettings ls2 = second.LinkfilterSettings;

            if (ls1.BlockBooterWebsites != ls2.BlockBooterWebsites || ls1.BlockDiscordInvites != ls2.BlockDiscordInvites ||
                ls1.BlockDisturbingWebsites != ls2.BlockDisturbingWebsites || ls1.BlockIpLoggingWebsites != ls2.BlockIpLoggingWebsites ||
                ls1.BlockUrlShorteners != ls2.BlockUrlShorteners || ls1.Enabled != ls2.Enabled)
            {
                return(false);
            }

            RatelimitSettings rs1 = first.RatelimitSettings;
            RatelimitSettings rs2 = second.RatelimitSettings;

            return(rs1.Action == rs2.Action && rs1.Enabled == rs2.Enabled && rs1.Sensitivity == rs2.Sensitivity);
        }
        public async Task HandleNewMessageAsync(MessageCreateEventArgs e, RatelimitSettings settings)
        {
            if (!this.guildRatelimitInfo.ContainsKey(e.Guild.Id))
            {
                if (!this.TryAddGuildToWatch(e.Guild.Id))
                {
                    throw new ConcurrentOperationException("Failed to add guild to ratelimit watch list!");
                }
                this.UpdateExemptsForGuildAsync(e.Guild.Id);
            }

            var member = e.Author as DiscordMember;

            if (this.guildExempts.TryGetValue(e.Guild.Id, out ConcurrentHashSet <ExemptedEntity> exempts))
            {
                if (exempts.Any(ee => ee.Type == ExemptedEntityType.Channel && ee.Id == e.Channel.Id))
                {
                    return;
                }
                if (exempts.Any(ee => ee.Type == ExemptedEntityType.Member && ee.Id == e.Author.Id))
                {
                    return;
                }
                if (exempts.Any(ee => ee.Type == ExemptedEntityType.Role && member.Roles.Any(r => r.Id == ee.Id)))
                {
                    return;
                }
            }

            ConcurrentDictionary <ulong, UserRatelimitInfo> gRateInfo = this.guildRatelimitInfo[e.Guild.Id];

            if (!gRateInfo.ContainsKey(e.Author.Id))
            {
                if (!gRateInfo.TryAdd(e.Author.Id, new UserRatelimitInfo(settings.Sensitivity)))
                {
                    throw new ConcurrentOperationException("Failed to add member to ratelimit watch list!");
                }
                return;
            }

            if (gRateInfo.TryGetValue(e.Author.Id, out UserRatelimitInfo rateInfo) && !rateInfo.TryDecrementAllowedMessageCount())
            {
                await this.PunishMemberAsync(e.Guild, member, settings.Action);

                rateInfo.Reset();
            }
        }
        public async Task HandleNewMessageAsync(MessageCreateEventArgs e, RatelimitSettings settings)
        {
            if (!this.guildSpamInfo.ContainsKey(e.Guild.Id) && !this.TryAddGuildToWatch(e.Guild.Id))
            {
                throw new ConcurrentOperationException("Failed to add guild to ratelimit watch list!");
            }

            if (!this.guildSpamInfo[e.Guild.Id].ContainsKey(e.Author.Id))
            {
                if (!this.guildSpamInfo[e.Guild.Id].TryAdd(e.Author.Id, new UserRatelimitInfo(settings.Sensitivity - 1)))
                {
                    throw new ConcurrentOperationException("Failed to add member to ratelimit watch list!");
                }
                return;
            }

            if (!this.guildSpamInfo[e.Guild.Id][e.Author.Id].TryDecrementAllowedMessageCount())
            {
                await this.PunishMemberAsync(e.Guild, e.Author as DiscordMember, settings.Action);
            }
        }