public async Task <IResult> ShowServerStatsAsync()
    {
        var getGuild = await _guildAPI.GetGuildAsync(_context.GuildID.Value, ct : this.CancellationToken);

        if (!getGuild.IsSuccess)
        {
            return(getGuild);
        }

        var guild = getGuild.Entity;

        var eb = CreateGuildInfoEmbed(guild);

        return(await _feedback.SendContextualEmbedAsync(eb, ct : this.CancellationToken));
    }
Exemple #2
0
        public async Task <IResult> ShowServerSettingsAsync()
        {
            var getGuild = await _guildAPI.GetGuildAsync(_context.GuildID.Value);

            if (!getGuild.IsSuccess)
            {
                return(getGuild);
            }

            var guild = getGuild.Entity;

            var getSettings = await _moderation.GetOrCreateServerSettingsAsync(guild.ID);

            if (!getSettings.IsSuccess)
            {
                return(getSettings);
            }

            var settings = getSettings.Entity;

            var getGuildIcon = CDN.GetGuildIconUrl(guild);

            var embedFields = new List <EmbedField>();
            var eb          = new Embed
            {
                Colour    = _feedback.Theme.Secondary,
                Title     = guild.Name,
                Thumbnail = getGuildIcon.IsSuccess
                    ? new EmbedThumbnail(getGuildIcon.Entity.ToString())
                    : default(Optional <IEmbedThumbnail>),
                Fields = embedFields
            };

            var moderationLogChannelName = settings.ModerationLogChannel.HasValue
                ? $"<#{settings.ModerationLogChannel}>"
                : "None";

            embedFields.Add(new EmbedField("Moderation Log Channel", moderationLogChannelName));

            var monitoringChannelName = settings.MonitoringChannel.HasValue
                ? $"<#{settings.MonitoringChannel}>"
                : "None";

            embedFields.Add(new EmbedField("Event Monitor Channel", monitoringChannelName));

            embedFields.Add(new EmbedField("Warning Threshold", settings.WarningThreshold.ToString()));

            return(await _feedback.SendContextualEmbedAsync(eb));
        }
        public async Task <IResult> ListAsync()
        {
            var canViewPatterns = _messageContentPatternService.CanViewPatterns();

            if (_context is not MessageContext)
            {
                return(Result.FromError(new InvalidOperationError("Not a message context")));
            }

            if (!canViewPatterns)
            {
                await _channelApi.CreateMessageAsync(_context.ChannelID, $"<@!{_context.User.ID}> does not have permission to view patterns blocked or allowed in guild {_context.GuildID.Value.Value}!", allowedMentions : new NoAllowedMentions());

                return(Result.FromError(new InvalidOperationError("You do not have permission to view patterns blocked or allowed in this guild!")));
            }

            var patterns = await _messageContentPatternService.GetPatterns(_context.GuildID.Value.Value);

            if (!patterns.Any())
            {
                await _channelApi.CreateMessageAsync(_context.ChannelID, $"Guild {_context.GuildID.Value.Value} does not have any patterns set up, get started with `!pattern block` or `!pattern allow`");

                return(Result.FromError(new InvalidOperationError("This guild does not have any patterns set up, get started with `!pattern block` or `!pattern allow`")));
            }

            var blocked = patterns.Any(x => x.Type == MessageContentPatternType.Blocked)
                ? string.Join(Environment.NewLine, patterns.Where(x => x.Type == MessageContentPatternType.Blocked).Select(x => $"- `{x.Pattern}`"))
                : "There are no blocked patterns";

            var allowed = patterns.Any(x => x.Type == MessageContentPatternType.Allowed)
                ? string.Join(Environment.NewLine, patterns.Where(x => x.Type == MessageContentPatternType.Allowed).Select(x => $"- `{x.Pattern}`"))
                : "There are no allowed patterns";
            var guild = await _guildApi.GetGuildAsync(_context.GuildID.Value);

            var embed = new Embed(
                Title: $"Message Patterns for {guild.Entity!.Name}",
                Description: new Optional <string>("Allowed patterns supersede those that are blocked."),
                Fields: Enumerable.Empty <IEmbedField>().Concat(blocked.EnumerateLongTextAsFieldBuilders("Blocked"))
                .Concat(allowed.EnumerateLongTextAsFieldBuilders("Allowed")).ToList()
                );

            await _channelApi.CreateMessageAsync(_context.ChannelID, embeds : new[] { embed });

            return(Result.FromSuccess());
        }
Exemple #4
0
    public async Task <IResult> ShowServerAsync()
    {
        var getServerResult = await _servers.GetOrRegisterServerAsync(_context.GuildID.Value);

        if (!getServerResult.IsSuccess)
        {
            return(getServerResult);
        }

        var getGuild = await _guildAPI.GetGuildAsync(_context.GuildID.Value, ct : this.CancellationToken);

        if (!getGuild.IsSuccess)
        {
            return(getGuild);
        }

        var guild = getGuild.Entity;

        var server = getServerResult.Entity;

        var fields = new[]
Exemple #5
0
            /// <inheritdoc />
            public async Task <Result <QueryResult> > Handle(Query request, CancellationToken cancellationToken)
            {
                var getGuildInfoResult = await _guildApi.GetGuildAsync(request.GuildId, ct : cancellationToken);

                if (!getGuildInfoResult.IsSuccess)
                {
                    return(Result <QueryResult> .FromError(getGuildInfoResult));
                }

                var guildInfo = getGuildInfoResult.Entity;

                if (guildInfo is null)
                {
                    return(new NotFoundError("Guild not found."));
                }

                Uri?iconUrl;

                if (guildInfo.Icon is not null)
                {
                    var getIconUrlResult = CDN.GetGuildIconUrl(request.GuildId, guildInfo.Icon, CDNImageFormat.PNG);
                    iconUrl = !getIconUrlResult.IsSuccess ? null : getIconUrlResult.Entity;
                }
                else
                {
                    iconUrl = null;
                }

                return(new QueryResult(
                           guildInfo.Name,
                           guildInfo.OwnerID,
                           guildInfo.MaxMembers.HasValue ? guildInfo.MaxMembers.Value : null,
                           guildInfo.Roles.Skip(1).ToList(),
                           iconUrl
                           ));
            }
Exemple #6
0
            /// <inheritdoc />
            public async Task <Result <ModerationAction> > Handle(Command request, CancellationToken cancellationToken)
            {
                var banModerationAction = new ModerationAction
                                          (
                    moderationActionType: ModerationActionType.Ban,
                    guildId: request.GuildId.Value,
                    isActive: true,
                    reason: request.Reason,
                    date: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    userDiscordId: request.UserDiscordId?.Value,
                    userIgn: request.UserIgn,
                    expiryDate: request.ExpiryDate
                                          );

                if (request.UserIgn is not null)
                {
                    var proto = new GenericCommand
                    {
                        DefaultCommand     = "ban",
                        DiscordCommandName = "ban",
                        DiscordChannelId   = request.ChannelId.Value.ToString(),
                        Args = { request.UserIgn }
                    };
                    await _ps.BroadcastMessage(proto);
                }

                if (request.UserDiscordId is not null)
                {
                    var guildResult = await _guildApi.GetGuildAsync(request.GuildId, ct : cancellationToken);

                    if (!guildResult.IsSuccess)
                    {
                        return(Result <ModerationAction> .FromError(guildResult.Error));
                    }

                    var banResult = await _guildApi.CreateGuildBanAsync(
                        request.GuildId,
                        request.UserDiscordId.Value,
                        reason : request.Reason,
                        ct : cancellationToken
                        );

                    if (!banResult.IsSuccess)
                    {
                        return(Result <ModerationAction> .FromError(banResult.Error));
                    }

                    var guildName = guildResult.Entity.Name;
                    var embed     = new Embed
                    {
                        Title     = $"You have been banned from {guildName}.",
                        Colour    = _colourPalette.Red,
                        Thumbnail = EmbedProperties.MmccLogoThumbnail,
                        Timestamp = DateTimeOffset.UtcNow,
                        Fields    = new List <EmbedField>
                        {
                            new("Reason", request.Reason, false),
                            new(
                                "Expires at",
                                request.ExpiryDate is null
                                    ? "Permanent"
                                    : $"{DateTimeOffset.FromUnixTimeMilliseconds(request.ExpiryDate.Value).UtcDateTime} UTC"
                                ,
                                false
                                ),
                            new(
                                "Appeals",
                                $"You can appeal this decision **[here]({_discordSettings.AppealsUrl})**."
                                )
                        }
                    };

                    var createDmResult = await _userApi.CreateDMAsync(request.UserDiscordId.Value, cancellationToken);

                    const string errMsg =
                        "Failed to send a DM notification to the user. It may be because they have blocked the bot. This error can in most cases be ignored.";
                    if (!createDmResult.IsSuccess || createDmResult.Entity is null)
                    {
                        _logger.LogWarning(errMsg);
                    }
                    else
                    {
                        var sendDmResult = await _channelApi.CreateMessageAsync(createDmResult.Entity.ID,
                                                                                embeds : new[] { embed },
                                                                                ct : cancellationToken);

                        if (!sendDmResult.IsSuccess)
                        {
                            _logger.LogWarning(errMsg);
                        }
                    }
                }

                try
                {
                    await _context.AddAsync(banModerationAction, cancellationToken);

                    await _context.SaveChangesAsync(cancellationToken);
                }
                catch (Exception e)
                {
                    return(e);
                }

                return(banModerationAction);
            }
Exemple #7
0
        /// <inheritdoc />
        public async Task <Result <ModerationAction> > Handle(Command request, CancellationToken cancellationToken)
        {
            var warnModerationAction = new ModerationAction
                                       (
                moderationActionType: ModerationActionType.Warn,
                guildId: request.GuildId.Value,
                isActive: true,
                reason: request.Reason,
                date: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                userDiscordId: request.UserDiscordId?.Value,
                userIgn: request.UserIgn,
                expiryDate: null
                                       );

            if (request.UserIgn is not null)
            {
                var protobufMessage = new ChatMessage
                {
                    ServerId      = "MMCC",
                    Message       = $"You have been warned, @{request.UserIgn}. Reason: {request.Reason}",
                    MessageOffset = 5
                };
                await _ps.BroadcastMessage(protobufMessage);
            }

            if (request.UserDiscordId is not null)
            {
                var guildResult = await _guildApi.GetGuildAsync(request.GuildId, ct : cancellationToken);

                if (!guildResult.IsSuccess)
                {
                    return(Result <ModerationAction> .FromError(guildResult.Error));
                }

                var guildName = guildResult.Entity.Name;
                var embed     = new Embed
                {
                    Title     = $"You have been warned in {guildName}.",
                    Colour    = _colourPalette.Yellow,
                    Thumbnail = EmbedProperties.MmccLogoThumbnail,
                    Timestamp = DateTimeOffset.UtcNow,
                    Fields    = new List <EmbedField>
                    {
                        new("Reason", request.Reason, false)
                    }
                };

                var createDmResult = await _userApi.CreateDMAsync(request.UserDiscordId.Value, cancellationToken);

                const string errMsg =
                    "Failed to send a DM notification to the user. It may be because they have blocked the bot. This error can in most cases be ignored.";
                if (!createDmResult.IsSuccess || createDmResult.Entity is null)
                {
                    _logger.LogWarning(errMsg);
                }
                else
                {
                    var sendDmResult = await _channelApi.CreateMessageAsync(createDmResult.Entity.ID,
                                                                            embeds : new[] { embed },
                                                                            ct : cancellationToken);

                    if (!sendDmResult.IsSuccess)
                    {
                        _logger.LogWarning(errMsg);
                    }
                }
            }

            try
            {
                await _context.AddAsync(warnModerationAction, cancellationToken);

                await _context.SaveChangesAsync(cancellationToken);
            }
            catch (Exception e)
            {
                return(e);
            }

            return(warnModerationAction);
        }
Exemple #8
0
    /// <inheritdoc />
    public async ValueTask <Result> CheckHasRequiredPermission(
        DiscordPermission permission,
        Snowflake channelId,
        IUser userToCheck,
        CancellationToken ct = default
        )
    {
        var getChannel = await _channelApi.GetChannelAsync(channelId, ct);

        if (!getChannel.IsSuccess)
        {
            return(Result.FromError(getChannel));
        }

        var channel = getChannel.Entity;

        if (!channel.GuildID.HasValue)
        {
            return(new ConditionNotSatisfiedError(
                       "Command requires a guild permission but was executed outside of a guild."));
        }

        var guildId = channel.GuildID.Value;

        var getGuildMember = await _guildApi.GetGuildMemberAsync(guildId, userToCheck.ID, ct);

        if (!getGuildMember.IsSuccess)
        {
            return(Result.FromError(getGuildMember));
        }

        var getGuildRoles = await _guildApi.GetGuildRolesAsync(guildId, ct);

        if (!getGuildRoles.IsSuccess)
        {
            return(Result.FromError(getGuildRoles));
        }

        var guildRoles   = getGuildRoles.Entity;
        var everyoneRole = guildRoles.FirstOrDefault(r => r.Name.Equals("@everyone"));

        if (everyoneRole is null)
        {
            return(new NotFoundError("No @everyone role found."));
        }

        var user = getGuildMember.Entity;

        if (user is null)
        {
            return(new NotFoundError("Executing user not found"));
        }

        var getGuild = await _guildApi.GetGuildAsync(guildId, ct : ct);

        if (!getGuild.IsSuccess)
        {
            return(Result.FromError(getGuild));
        }

        var guildOwnerId = getGuild.Entity.OwnerID;

        // succeed if the user is the Owner of the guild
        if (guildOwnerId.Equals(userToCheck.ID))
        {
            return(Result.FromSuccess());
        }

        var memberRoles         = guildRoles.Where(r => user.Roles.Contains(r.ID)).ToList();
        var computedPermissions = channel.PermissionOverwrites switch
        {
            { HasValue : true, Value : { } overwrites } => DiscordPermissionSet.ComputePermissions(
Exemple #9
0
    /// <inheritdoc />
    public async Task <Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default)
    {
        if (!gatewayEvent.User.IsDefined(out var user))
        {
            // We can't do anything about this
            return(Result.FromSuccess());
        }

        var getServerResult = await _servers.GetOrRegisterServerAsync(gatewayEvent.GuildID, ct);

        if (!getServerResult.IsSuccess)
        {
            return(Result.FromError(getServerResult));
        }

        var server = getServerResult.Entity;

        if (!server.SendJoinMessage)
        {
            return(Result.FromSuccess());
        }

        var getJoinMessageResult = ServerService.GetJoinMessage(server);

        if (!getJoinMessageResult.IsSuccess)
        {
            return(Result.FromError(getJoinMessageResult));
        }

        var openDM = await _userAPI.CreateDMAsync(user.ID, ct);

        if (!openDM.IsSuccess)
        {
            return(Result.FromError(openDM));
        }

        var userChannel = openDM.Entity;

        var embed = new Embed
        {
            Colour      = _feedback.Theme.Secondary,
            Description = $"Welcome, <@{user.ID}>!\n" +
                          "\n" +
                          $"{getJoinMessageResult.Entity}"
        };

        var sendEmbed = await _channelAPI.CreateMessageAsync(userChannel.ID, embeds : new[] { embed }, ct : ct);

        if (sendEmbed.IsSuccess)
        {
            return(Result.FromSuccess());
        }

        if (sendEmbed.Error is not RestResultError <RestError> re)
        {
            return(Result.FromError(sendEmbed));
        }

        if (re.Error.Code is not DiscordError.CannotSendMessageToUser)
        {
            return(Result.FromError(sendEmbed));
        }

        var getGuild = await _guildAPI.GetGuildAsync(gatewayEvent.GuildID, ct : ct);

        if (!getGuild.IsSuccess)
        {
            return(Result.FromError(getGuild));
        }

        var guild = getGuild.Entity;

        if (!guild.SystemChannelID.HasValue)
        {
            // man, we tried
            return(Result.FromSuccess());
        }

        var content = $"Welcome, <@{user.ID}>! You have DMs disabled, so I couldn't send you " +
                      "the first-join message. To see it, type \"!server join-message\".";

        var sendNotification = await _feedback.SendWarningAsync
                               (
            guild.SystemChannelID.Value,
            content,
            user.ID,
            ct : ct
                               );

        return(sendNotification.IsSuccess
            ? Result.FromSuccess()
            : Result.FromError(sendNotification));
    }
Exemple #10
0
        /// <inheritdoc />
        public async ValueTask <Result> CheckHasRequiredPermission(
            DiscordPermission permission,
            Snowflake channelId,
            IUser userToCheck,
            CancellationToken ct = default
            )
        {
            var getChannel = await _channelApi.GetChannelAsync(channelId, ct);

            if (!getChannel.IsSuccess)
            {
                return(Result.FromError(getChannel));
            }

            var channel = getChannel.Entity;

            if (!channel.GuildID.HasValue)
            {
                return(new ConditionNotSatisfiedError(
                           "Command requires a guild permission but was executed outside of a guild."));
            }

            var guildId = channel.GuildID.Value;

            var getGuildMember = await _guildApi.GetGuildMemberAsync(guildId, userToCheck.ID, ct);

            if (!getGuildMember.IsSuccess)
            {
                return(Result.FromError(getGuildMember));
            }

            var getGuildRoles = await _guildApi.GetGuildRolesAsync(guildId, ct);

            if (!getGuildRoles.IsSuccess)
            {
                return(Result.FromError(getGuildRoles));
            }

            var guildRoles   = getGuildRoles.Entity;
            var everyoneRole = guildRoles.FirstOrDefault(r => r.Name.Equals("@everyone"));

            if (everyoneRole is null)
            {
                return(new NotFoundError("No @everyone role found."));
            }

            var user = getGuildMember.Entity;

            if (user is null)
            {
                return(new NotFoundError("Executing user not found"));
            }

            var getGuild = await _guildApi.GetGuildAsync(guildId, ct : ct);

            if (!getGuild.IsSuccess)
            {
                return(Result.FromError(getGuild));
            }

            var guildOwnerId = getGuild.Entity.OwnerID;

            // succeed if the user is the Owner of the guild
            if (guildOwnerId.Equals(userToCheck.ID))
            {
                return(Result.FromSuccess());
            }

            var memberRoles = guildRoles.Where(r => user.Roles.Contains(r.ID)).ToList();
            IDiscordPermissionSet computedPermissions;

            if (channel.PermissionOverwrites.HasValue)
            {
                computedPermissions = DiscordPermissionSet.ComputePermissions(
                    userToCheck.ID,
                    everyoneRole,
                    memberRoles,
                    channel.PermissionOverwrites.Value
                    );
            }
            else
            {
                computedPermissions = DiscordPermissionSet.ComputePermissions(
                    userToCheck.ID,
                    everyoneRole,
                    memberRoles
                    );
            }

            // succeed if the user is an Administrator of the guild
            if (computedPermissions.HasPermission(DiscordPermission.Administrator))
            {
                return(Result.FromSuccess());
            }

            var hasPermission = computedPermissions.HasPermission(permission);

            return(!hasPermission
                ? new ConditionNotSatisfiedError(
                       $"Guild User requesting the command does not have the required {permission.ToString()} permission")
                : Result.FromSuccess());
        }
    public async Task <Result> HasPermissionAsync
    (
        Snowflake discordServer,
        Snowflake discordUser,
        IPermission requiredPermission,
        PermissionTarget target,
        CancellationToken ct = default
    )
    {
        var getDiscordServer = await _guildAPI.GetGuildAsync(discordServer, ct : ct);

        if (!getDiscordServer.IsSuccess)
        {
            return(Result.FromError(getDiscordServer));
        }

        var guild = getDiscordServer.Entity;

        // The server owner always has all permissions by default
        if (guild.OwnerID == discordUser)
        {
            return(Result.FromSuccess());
        }

        // Special handling for the All target
        if (target == PermissionTarget.All)
        {
            var hasSelf = await HasPermissionAsync
                          (
                discordServer,
                discordUser,
                requiredPermission,
                PermissionTarget.Self,
                ct
                          );

            var hasOther = await HasPermissionAsync
                           (
                discordServer,
                discordUser,
                requiredPermission,
                PermissionTarget.Other,
                ct
                           );

            if (hasSelf.IsSuccess && hasOther.IsSuccess)
            {
                return(Result.FromSuccess());
            }

            return(new UserError("Permission denied."));
        }

        var hasPermission = false;

        var getGuildMember = await _guildAPI.GetGuildMemberAsync(discordServer, discordUser, ct);

        if (!getGuildMember.IsSuccess)
        {
            return(Result.FromError(getGuildMember));
        }

        var member = getGuildMember.Entity;

        // Check if the user is part of any roles which this permission applies to
        var rolePermissions = await GetApplicableRolePermissionsAsync(member.Roles, ct);

        var rolePermission = rolePermissions.FirstOrDefault
                             (
            p =>
            p.Permission == requiredPermission.UniqueIdentifier &&
            p.Target == target
                             );

        if (rolePermission is not null)
        {
            hasPermission = rolePermission.IsGranted;
        }

        // Check if the user has the permission applied to themselves
        var userPermission = await _database.UserPermissions.ServersideQueryAsync
                             (
            q => q
            .Where(p => p.ServerID == discordServer)
            .Where(p => p.UserID == discordUser)
            .Where(p => p.Permission == requiredPermission.UniqueIdentifier)
            .Where(p => p.Target == target)
            .SingleOrDefaultAsync(ct)
                             );

        if (userPermission is not null)
        {
            hasPermission = userPermission.IsGranted;
        }

        if (rolePermission is null && userPermission is null)
        {
            // Use the permission's default value
            hasPermission = requiredPermission.IsGrantedByDefaultTo(target);
        }

        return(hasPermission
            ? Result.FromSuccess()
            : new UserError("Permission denied."));
    }