private async Task AnnounceAutomoderationConfig(AutoModerationConfig config, IUser actor, RestAction action)
        {
            using var scope = _serviceProvider.CreateScope();

            _logger.LogInformation($"Announcing automod config {config.GuildId}/{config.AutoModerationType} ({config.Id}).");


            GuildConfig guildConfig = await GuildConfigRepository.CreateDefault(scope.ServiceProvider).GetGuildConfig(config.GuildId);

            if (!string.IsNullOrEmpty(guildConfig.ModInternalNotificationWebhook))
            {
                _logger.LogInformation($"Sending internal webhook for config {config.GuildId}/{config.AutoModerationType} ({config.Id}) to {guildConfig.ModInternalNotificationWebhook}.");

                try
                {
                    EmbedBuilder embed = await config.CreateAutomodConfigEmbed(actor, action, _serviceProvider);

                    await _discordAPI.ExecuteWebhook(guildConfig.ModInternalNotificationWebhook, embed.Build());
                }
                catch (Exception e)
                {
                    _logger.LogError(e, $"Error while announcing config  {config.GuildId}/{config.AutoModerationType} ({config.Id}) to {guildConfig.ModInternalNotificationWebhook}.");
                }
            }
        }
示例#2
0
        public static bool Check(IMessage message, AutoModerationConfig config, IDiscordClient _)
        {
            if (config.Limit == null)
            {
                return(false);
            }
            if (string.IsNullOrEmpty(config.CustomWordFilter))
            {
                return(false);
            }
            if (string.IsNullOrEmpty(message.Content))
            {
                return(false);
            }

            int matches = 0;

            foreach (string word in config.CustomWordFilter.Split('\n'))
            {
                if (string.IsNullOrWhiteSpace(word))
                {
                    continue;
                }
                try
                {
                    matches += Regex.Matches(message.Content, word, RegexOptions.IgnoreCase).Count;
                }
                catch { }
                if (matches > config.Limit)
                {
                    break;
                }
            }
            return(matches > config.Limit);
        }
示例#3
0
        private async Task ExecutePunishment(IMessage message, AutoModerationConfig autoModerationConfig)
        {
            AutoModerationEvent modEvent = new()
            {
                GuildId              = (message.Channel as ITextChannel).Guild.Id,
                AutoModerationType   = autoModerationConfig.AutoModerationType,
                AutoModerationAction = autoModerationConfig.AutoModerationAction,
                UserId         = message.Author.Id,
                MessageId      = message.Id,
                MessageContent = message.Content
            };

            await AutoModerationEventRepository.CreateDefault(_serviceProvider).RegisterEvent(modEvent, message.Channel as ITextChannel, message.Author);

            if (modEvent.AutoModerationAction == AutoModerationAction.ContentDeleted || modEvent.AutoModerationAction == AutoModerationAction.ContentDeletedAndCaseCreated)
            {
                try
                {
                    RequestOptions requestOptions = new();
                    requestOptions.RetryMode = RetryMode.RetryRatelimit;
                    await message.DeleteAsync(requestOptions);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error deleting message {message.Id}.");
                }
            }
        }
    }
示例#4
0
        private async Task <bool> IsProtectedByFilter(IMessage message, AutoModerationConfig autoModerationConfig)
        {
            if (_config.GetSiteAdmins().Contains(message.Author.Id))
            {
                return(true);
            }

            IGuild guild = await _client.GetGuildAsync((message.Channel as ITextChannel).Guild.Id);

            IGuildUser member = await guild.GetUserAsync(message.Author.Id);

            if (member == null)
            {
                return(false);
            }

            if (member.Guild.OwnerId == member.Id)
            {
                return(true);
            }

            if (member.RoleIds.Any(x => _guildConfig.ModRoles.Contains(x) ||
                                   _guildConfig.AdminRoles.Contains(x) ||
                                   autoModerationConfig.IgnoreRoles.Contains(x)))
            {
                return(true);
            }

            return(autoModerationConfig.IgnoreChannels.Contains((message.Channel as ITextChannel).Id));
        }
示例#5
0
        public async Task <IActionResult> DeleteItem([FromRoute] string guildid, [FromRoute] string type)
        {
            logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | Incoming request.");
            Identity currentIdentity = await identityManager.GetIdentity(HttpContext);

            User currentUser = await currentIdentity.GetCurrentDiscordUser();

            if (currentUser == null)
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Unauthorized.");
                return(Unauthorized());
            }
            if (!await currentIdentity.HasAdminRoleOnGuild(guildid, this.database) && !config.Value.SiteAdminDiscordUserIds.Contains(currentUser.Id))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Unauthorized.");
                return(Unauthorized());
            }
            // ========================================================

            GuildConfig guildConfig = await database.SelectSpecificGuildConfig(guildid);

            if (guildConfig == null)
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Guild not registered.");
                return(BadRequest("Guild not registered."));
            }

            int x = 0;

            try {
                x = Int32.Parse(type);
            } catch (Exception e) {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Invalid type.", e);
                return(BadRequest("Invalid type"));
            }

            if (!Enum.IsDefined(typeof(AutoModerationType), x))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Invalid type.");
                return(BadRequest("Invalid type"));
            }

            AutoModerationType   eType         = (AutoModerationType)x;
            AutoModerationConfig currentConfig = await database.SelectModerationConfigForGuildAndType(guildid, eType);

            if (currentConfig != null)
            {
                database.DeleteSpecificModerationConfig(currentConfig);
                await database.SaveChangesAsync();

                return(Ok(new { id = currentConfig.Id }));
            }
            else
            {
                return(NotFound());
            }
        }
示例#6
0
 public static bool Check(IMessage message, AutoModerationConfig config, IDiscordClient _)
 {
     if (config.Limit == null)
     {
         return(false);
     }
     if (message.Embeds == null)
     {
         return(false);
     }
     return(message.Embeds.Count > config.Limit);
 }
示例#7
0
        private async Task <bool> CheckMultipleEvents(IMessage message, AutoModerationConfig config)
        {
            if (config.Limit == null)
            {
                return(false);
            }
            if (config.TimeLimitMinutes == null)
            {
                return(false);
            }
            var existing = await AutoModerationEventRepository.CreateDefault(_serviceProvider).GetAllEventsForUserSinceMinutes(message.Author.Id, config.TimeLimitMinutes.Value);

            return(existing.Count > config.Limit.Value);
        }
        public static bool Check(IMessage message, AutoModerationConfig config, IDiscordClient _)
        {
            if (config.Limit == null)
            {
                return(false);
            }
            if (string.IsNullOrEmpty(message.Content))
            {
                return(false);
            }
            if (config.Limit <= 0)
            {
                return(false);
            }

            Regex regexPattern = new(@"([^0-9`])(?:\s*\1){" + config.Limit.ToString() + @",}");

            return(regexPattern.Match(message.Content).Success);
        }
示例#9
0
        private async Task <bool> CheckAutoMod(AutoModerationType autoModerationType, IMessage message, Func <IMessage, AutoModerationConfig, Task <bool> > predicate)
        {
            AutoModerationConfig autoModerationConfig = _autoModerationConfigs.FirstOrDefault(x => x.AutoModerationType == autoModerationType);

            if (autoModerationConfig != null)
            {
                if (await predicate(message, autoModerationConfig))
                {
                    if (!await IsProtectedByFilter(message, autoModerationConfig))
                    {
                        _logger.LogInformation($"U: {message.Author.Id} | C: {(message.Channel as ITextChannel).Id} | G: {(message.Channel as ITextChannel).Guild.Id} triggered {autoModerationConfig.AutoModerationType}.");
                        await ExecutePunishment(message, autoModerationConfig);

                        if (autoModerationConfig.AutoModerationType != AutoModerationType.TooManyAutoModerations)
                        {
                            await CheckAutoMod(AutoModerationType.TooManyAutoModerations, message, CheckMultipleEvents);
                        }
                        return(true);
                    }
                }
            }
            return(false);
        }
示例#10
0
        public static async Task <EmbedBuilder> CreateAutomodConfigEmbed(this AutoModerationConfig autoModerationConfig, IUser actor, RestAction action, IServiceProvider provider)
        {
            var translator = provider.GetService <Translator>();

            await translator.SetContext(autoModerationConfig.GuildId);

            EmbedBuilder embed = CreateBasicEmbed(action, provider, actor);

            if (actor != null)
            {
                embed.WithThumbnailUrl(actor.GetAvatarOrDefaultUrl());
            }

            embed.WithTitle(translator.T().Automoderation() + ": " + translator.T().Enum(autoModerationConfig.AutoModerationType));

            switch (action)
            {
            case RestAction.Created:
                embed.WithDescription(translator.T().NotificationAutomoderationConfigInternalCreate(translator.T().Enum(autoModerationConfig.AutoModerationType), actor));
                break;

            case RestAction.Updated:
                embed.WithDescription(translator.T().NotificationAutomoderationConfigInternalUpdate(translator.T().Enum(autoModerationConfig.AutoModerationType), actor));
                break;

            case RestAction.Deleted:
                return(embed.WithDescription(translator.T().NotificationAutomoderationConfigInternalDelete(translator.T().Enum(autoModerationConfig.AutoModerationType), actor)));
            }

            if (autoModerationConfig.Limit != null)
            {
                embed.AddField(translator.T().NotificationAutomoderationConfigLimit(), $"`{autoModerationConfig.Limit}`", true);
            }
            if (autoModerationConfig.TimeLimitMinutes != null)
            {
                embed.AddField(translator.T().NotificationAutomoderationConfigTimeLimit(), $"`{autoModerationConfig.TimeLimitMinutes}`", true);
            }
            if (autoModerationConfig.Limit != null || autoModerationConfig.TimeLimitMinutes != null)
            {
                embed.AddField("\u200b", "\u200b");
            }

            if (autoModerationConfig.IgnoreRoles.Length > 0)
            {
                embed.AddField(translator.T().NotificationAutomoderationConfigIgnoredRoles(), string.Join(" ", autoModerationConfig.IgnoreRoles.Select(x => $"<@&{x}>")), true);
            }
            if (autoModerationConfig.IgnoreChannels.Length > 0)
            {
                embed.AddField(translator.T().NotificationAutomoderationConfigIgnoredChannels(), string.Join(" ", autoModerationConfig.IgnoreChannels.Select(x => $"<#{x}>")), true);
            }
            if (autoModerationConfig.IgnoreRoles.Length > 0 || autoModerationConfig.IgnoreChannels.Length > 0)
            {
                embed.AddField("\u200b", "\u200b");
            }

            if (autoModerationConfig.PunishmentType != null && (autoModerationConfig.AutoModerationAction == AutoModerationAction.CaseCreated || autoModerationConfig.AutoModerationAction == AutoModerationAction.ContentDeletedAndCaseCreated))
            {
                embed.AddField($"{SCALES_EMOTE} {translator.T().Punishment()}", translator.T().Enum(autoModerationConfig.PunishmentType.Value), true);
                if (autoModerationConfig.PunishmentDurationMinutes > 0)
                {
                    embed.AddField($"{ALARM_CLOCK} {translator.T().NotificationAutomoderationConfigDuration()}", $"`{autoModerationConfig.PunishmentDurationMinutes}`", true);
                }
                embed.AddField(
                    translator.T().NotificationAutomoderationConfigSendPublic(),
                    autoModerationConfig.SendPublicNotification ? CHECK : X_CHECK,
                    true);
                embed.AddField(
                    translator.T().NotificationAutomoderationConfigSendDM(),
                    autoModerationConfig.SendDmNotification ? CHECK : X_CHECK,
                    true);
            }
            embed.AddField(
                translator.T().NotificationAutomoderationConfigDeleteMessage(),
                autoModerationConfig.AutoModerationAction == AutoModerationAction.ContentDeleted || autoModerationConfig.AutoModerationAction == AutoModerationAction.ContentDeletedAndCaseCreated ? CHECK : X_CHECK,
                true);


            return(embed);
        }
示例#11
0
        private async Task AnnounceAutomoderation(AutoModerationEvent modEvent, AutoModerationConfig moderationConfig, GuildConfig guildConfig, ITextChannel channel, IUser author)
        {
            using var scope = _serviceProvider.CreateScope();

            var translator = scope.ServiceProvider.GetRequiredService <Translator>();

            translator.SetContext(guildConfig);
            if (!string.IsNullOrEmpty(guildConfig.ModInternalNotificationWebhook))
            {
                _logger.LogInformation($"Sending internal webhook for automod event {modEvent.GuildId}/{modEvent.Id} to {guildConfig.ModInternalNotificationWebhook}.");

                try
                {
                    EmbedBuilder embed = await modEvent.CreateInternalAutomodEmbed(guildConfig, author, channel, scope.ServiceProvider, moderationConfig.PunishmentType);

                    await _discordAPI.ExecuteWebhook(guildConfig.ModInternalNotificationWebhook, embed.Build());
                }
                catch (Exception e)
                {
                    _logger.LogError(e, $"Error while announcing automod event {modEvent.GuildId}/{modEvent.Id} to {guildConfig.ModInternalNotificationWebhook}.");
                }
            }

            if (moderationConfig.SendDmNotification)
            {
                _logger.LogInformation($"Sending dm notification for autmod event {modEvent.GuildId}/{modEvent.Id} to {author.Id}.");

                try
                {
                    string reason = translator.T().Enum(modEvent.AutoModerationType);
                    string action = translator.T().Enum(modEvent.AutoModerationAction);
                    await _discordAPI.SendDmMessage(author.Id, translator.T().NotificationAutomoderationDM(author, channel, reason, action));
                }
                catch (Exception e)
                {
                    _logger.LogError(e, $"Error while announcing automod event {modEvent.GuildId}/{modEvent.Id} in dm to {author.Id}.");
                }
            }

            if ((modEvent.AutoModerationAction == AutoModerationAction.ContentDeleted || modEvent.AutoModerationAction == AutoModerationAction.ContentDeletedAndCaseCreated) && moderationConfig.ChannelNotificationBehavior != AutoModerationChannelNotificationBehavior.NoNotification)
            {
                _logger.LogInformation($"Sending channel notification to {modEvent.GuildId}/{modEvent.Id} {channel.GuildId}/{channel.Id}.");

                try
                {
                    string   reason = translator.T().Enum(modEvent.AutoModerationType);
                    IMessage msg    = await channel.SendMessageAsync(translator.T().NotificationAutomoderationChannel(author, reason));

                    if (moderationConfig.ChannelNotificationBehavior == AutoModerationChannelNotificationBehavior.SendNotificationAndDelete)
                    {
                        Task task = new(async() =>
                        {
                            await Task.Delay(TimeSpan.FromSeconds(5));
                            try
                            {
                                _logger.LogInformation($"Deleting channel automod event notification {channel.GuildId}/{channel.Id}/{msg.Id}.");
                                await msg.DeleteAsync();
                            }
                            catch (UnauthorizedException) { }
                            catch (Exception e)
                            {
                                _logger.LogError(e, $"Error while deleting message {channel.GuildId}/{channel.Id}/{msg.Id} for automod event {modEvent.GuildId}/{modEvent.Id}.");
                            }
                        });
                        task.Start();
                    }
                }
                catch (Exception e)
                {
                    _logger.LogError(e, $"Error while announcing automod event {modEvent.GuildId}/{modEvent.Id} in channel {channel.Id}.");
                }
            }
        }
示例#12
0
 public void DeleteSpecificModerationConfig(AutoModerationConfig modConfig)
 {
     context.AutoModerationConfigs.Remove(modConfig);
 }
示例#13
0
 public void PutModerationConfig(AutoModerationConfig modConfig)
 {
     context.AutoModerationConfigs.Update(modConfig);
 }
        public async Task <IActionResult> CreateItem([FromRoute] string guildid, [FromBody] AutoModerationEventForCreateDto dto)
        {
            logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | Incoming request.");
            if (string.IsNullOrEmpty(this.config.Value.DiscordBotToken))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Authorization header not defined.");
                return(Unauthorized());
            }

            string auth = String.Empty;

            try {
                auth = Request.Headers["Authorization"];
            } catch (Exception e) {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Authorization header not defined.", e);
                return(Unauthorized());
            }
            if (this.config.Value.DiscordBotToken == auth)
            {
                AutoModerationConfig modConfig = await this.database.SelectModerationConfigForGuildAndType(guildid, dto.AutoModerationType);

                if (modConfig == null)
                {
                    logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 No config found for this type.");
                    return(BadRequest("No config found for this type"));
                }

                AutoModerationEvent modEvent = new AutoModerationEvent();
                if (modConfig.AutoModerationAction == AutoModerationAction.CaseCreated || modConfig.AutoModerationAction == AutoModerationAction.ContentDeletedAndCaseCreated)
                {
                    ModCase newModCase = new ModCase();

                    User discordBot = await discord.FetchCurrentBotInfo();

                    newModCase.CaseId = await database.GetHighestCaseIdForGuild(guildid) + 1;

                    newModCase.GuildId           = guildid;
                    newModCase.UserId            = dto.UserId;
                    newModCase.Username          = dto.Username;
                    newModCase.Discriminator     = dto.Discriminator;
                    newModCase.Nickname          = dto.Nickname;
                    newModCase.ModId             = discordBot.Id;
                    newModCase.CreatedAt         = DateTime.UtcNow;
                    newModCase.LastEditedAt      = newModCase.CreatedAt;
                    newModCase.LastEditedByModId = discordBot.Id;
                    newModCase.OccuredAt         = newModCase.CreatedAt;
                    newModCase.Title             = $"AutoModeration: {dto.AutoModerationType.ToString()}";
                    newModCase.Description       = $"User triggered AutoModeration.\nEvent: {dto.AutoModerationType.ToString()}.\nAction: {modConfig.AutoModerationAction.ToString()}\nMessageId: {dto.MessageId}.\nMessage content: {dto.MessageContent}.";
                    newModCase.Labels            = new List <string>()
                    {
                        "automoderation", dto.AutoModerationType.ToString()
                    }.ToArray();
                    newModCase.Valid = true;

                    if (modConfig.PunishmentType != null && modConfig.PunishmentType != PunishmentType.None)
                    {
                        newModCase.PunishmentType   = modConfig.PunishmentType.Value;
                        newModCase.PunishmentActive = true;

                        if (modConfig.PunishmentDurationMinutes == null)
                        {
                            newModCase.Punishment    = newModCase.PunishmentType.ToString();
                            newModCase.PunishedUntil = null;
                        }
                        else
                        {
                            newModCase.Punishment    = "Temp" + newModCase.PunishmentType.ToString();
                            newModCase.PunishedUntil = DateTime.UtcNow.AddMinutes(modConfig.PunishmentDurationMinutes.Value);
                        }

                        try {
                            logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | Handling punishment.");
                            await punishmentHandler.ExecutePunishment(newModCase, database);
                        }
                        catch (Exception e) {
                            logger.LogError(e, "Failed to handle punishment for modcase.");
                        }
                    }
                    else
                    {
                        newModCase.Punishment       = "Warn";
                        newModCase.PunishmentType   = PunishmentType.None;
                        newModCase.PunishedUntil    = null;
                        newModCase.PunishmentActive = false;
                    }

                    await database.SaveModCase(newModCase);

                    await database.SaveChangesAsync();

                    modEvent.AssociatedCaseId = newModCase.CaseId;

                    logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | Sending notification.");
                    try {
                        await discordAnnouncer.AnnounceModCase(newModCase, RestAction.Created, discordBot, modConfig.SendPublicNotification);
                    }
                    catch (Exception e) {
                        logger.LogError(e, "Failed to announce modcase.");
                    }
                }

                modEvent.GuildId              = guildid;
                modEvent.CreatedAt            = DateTime.UtcNow;
                modEvent.UserId               = dto.UserId;
                modEvent.Username             = dto.Username;
                modEvent.Nickname             = dto.Nickname;
                modEvent.Discriminator        = dto.Discriminator;
                modEvent.MessageContent       = dto.MessageContent;
                modEvent.MessageId            = dto.MessageId;
                modEvent.AutoModerationType   = dto.AutoModerationType;
                modEvent.AutoModerationAction = modConfig.AutoModerationAction;

                await database.SaveModerationEvent(modEvent);

                await database.SaveChangesAsync();

                return(Ok(new { Id = modEvent.Id }));
            }
            else
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Unauthorized.");
                return(Unauthorized());
            }
        }
示例#15
0
        public async Task <IActionResult> SetItem([FromRoute] string guildid, [FromBody] AutoModerationConfigForPutDto dto)
        {
            logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | Incoming request.");
            Identity currentIdentity = await identityManager.GetIdentity(HttpContext);

            User currentUser = await currentIdentity.GetCurrentDiscordUser();

            if (currentUser == null)
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Unauthorized.");
                return(Unauthorized());
            }
            if (!await currentIdentity.HasAdminRoleOnGuild(guildid, this.database) && !config.Value.SiteAdminDiscordUserIds.Contains(currentUser.Id))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 401 Unauthorized.");
                return(Unauthorized());
            }
            // ========================================================

            GuildConfig guildConfig = await database.SelectSpecificGuildConfig(guildid);

            if (guildConfig == null)
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Guild not registered.");
                return(BadRequest("Guild not registered."));
            }

            AutoModerationConfig currentConfig = await database.SelectModerationConfigForGuildAndType(guildid, dto.AutoModerationType);

            if (currentConfig == null)
            {
                currentConfig = new AutoModerationConfig();
            }

            if (!Enum.IsDefined(typeof(AutoModerationType), dto.AutoModerationType))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Invalid moderation type.");
                return(BadRequest("Invalid moderation type"));
            }

            if (!Enum.IsDefined(typeof(AutoModerationAction), dto.AutoModerationAction))
            {
                logger.LogInformation($"{HttpContext.Request.Method} {HttpContext.Request.Path} | 400 Invalid action type.");
                return(BadRequest("Invalid action type"));
            }

            currentConfig.AutoModerationType   = dto.AutoModerationType;
            currentConfig.AutoModerationAction = dto.AutoModerationAction;
            currentConfig.GuildId                   = guildid;
            currentConfig.IgnoreChannels            = dto.IgnoreChannels.Distinct().ToArray();
            currentConfig.IgnoreRoles               = dto.IgnoreRoles.Distinct().ToArray();
            currentConfig.Limit                     = dto.Limit;
            currentConfig.TimeLimitMinutes          = dto.TimeLimitMinutes;
            currentConfig.PunishmentType            = dto.PunishmentType;
            currentConfig.PunishmentDurationMinutes = dto.PunishmentDurationMinutes;
            currentConfig.SendPublicNotification    = dto.SendPublicNotification;
            currentConfig.SendDmNotification        = dto.SendDmNotification;

            database.PutModerationConfig(currentConfig);
            await database.SaveChangesAsync();

            return(Ok(new { Id = currentConfig.Id, GuildId = currentConfig.GuildId, Type = currentConfig.AutoModerationType }));
        }