예제 #1
0
        public DetermineConditionResult IsEntityNameValid
        (
            IEnumerable <string> commandNames,
            string?entityName
        )
        {
            if (entityName.IsNullOrWhitespace())
            {
                return(DetermineConditionResult.FromError("Names cannot be empty."));
            }

            if (entityName.Any(c => _reservedNameCharacters.Contains(c)))
            {
                return(DetermineConditionResult.FromError
                       (
                           $"Names may not contain any of the following characters: {_reservedNameCharacters.Humanize()}"
                       ));
            }

            if (_reservedNames.Any(n => string.Equals(n, entityName, StringComparison.OrdinalIgnoreCase)))
            {
                return(DetermineConditionResult.FromError
                       (
                           "That is a reserved name."
                       ));
            }

            if (commandNames.Any(entityName.Contains))
            {
                return(DetermineConditionResult.FromError("Names may not be the same as a command."));
            }

            return(DetermineConditionResult.FromSuccess());
        }
예제 #2
0
        /// <summary>
        /// Sends a consent request to the given DM channel.
        /// </summary>
        /// <param name="channel">The channel.</param>
        /// <returns>An execution result.</returns>
        public async Task <DetermineConditionResult> RequestConsentAsync(IDMChannel channel)
        {
            try
            {
                var consentBuilder = _feedback.CreateEmbedBase(Color.Orange);
                consentBuilder.WithDescription
                (
                    "Hello there! This appears to be the first time you're using the bot (or you've not granted your " +
                    "consent for it to store potentially sensitive or identifiable data about you).\n" +
                    "\n" +
                    "In order to use Amby and her commands, you need to give her your consent to store various data " +
                    "about you. We need this consent in order to be compliant with data regulations in the European " +
                    "Union (and it'd be rude not to ask!).\n" +
                    "\n" +
                    "In short, if you use the bot, we're going to be storing " +
                    "stuff like your Discord ID, some messages, server IDs, etc. You can - and should! - read the " +
                    "full privacy policy before you agree to anything. It's not very long (3 pages) and shouldn't take " +
                    "more than five minutes to read through.\n" +
                    "\n" +
                    "Once you've read it, you can grant consent by running the `!privacy grant-consent` command over DM. If you " +
                    "don't want to consent to anything, just don't use the bot :smiley:"
                );

                await channel.SendMessageAsync(string.Empty, embed : consentBuilder.Build());

                await SendPrivacyPolicyAsync(channel);
            }
            catch (HttpException hex) when(hex.HttpCode == HttpStatusCode.Forbidden)
            {
                return(DetermineConditionResult.FromError("Could not send the privacy message over DM."));
            }

            return(DetermineConditionResult.FromSuccess());
        }
예제 #3
0
        /// <summary>
        /// Verifies all yaml files in the given directory.
        /// </summary>
        /// <param name="directory">The directory to load files from.</param>
        /// <returns>A condition result, which may or may not have succeeded.</returns>
        public DetermineConditionResult VerifyFilesInDirectory(string directory)
        {
            var files = Directory.EnumerateFiles(directory, "*.yml", SearchOption.AllDirectories).Where(p => !p.EndsWith("Species.yml")).ToList();

            if (files.Count <= 0)
            {
                return(DetermineConditionResult.FromError("No files to verify in input directory."));
            }

            foreach (var file in files)
            {
                var verificationResult = VerifyFile <Transformation>(file);
                if (!verificationResult.IsSuccess)
                {
                    return(verificationResult);
                }
            }

            var speciesPath = Path.Combine(directory, "Species.yml");

            if (!File.Exists(speciesPath))
            {
                return(DetermineConditionResult.FromSuccess());
            }

            var speciesVerificationResult = VerifyFile <Species>(speciesPath);

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

            return(DetermineConditionResult.FromSuccess());
        }
예제 #4
0
        /// <summary>
        /// Verifies the content of the given file, treating it as a transformation file.
        /// </summary>
        /// <param name="file">The path to the file.</param>
        /// <typeparam name="T">The class to verify the file as.</typeparam>
        /// <returns>A condition result, which may or may not have succeeded.</returns>
        public DetermineConditionResult VerifyFile <T>(string file)
        {
            using var sr = new StreamReader(File.OpenRead(file));
            var deserB = new DeserializerBuilder()
                         .WithTypeConverter(new ColourYamlConverter())
                         .WithNodeDeserializer(i => new ValidatingNodeDeserializer(i), s => s.InsteadOf <ObjectNodeDeserializer>())
                         .WithNamingConvention(UnderscoredNamingConvention.Instance);

            if (typeof(T) != typeof(Species))
            {
                deserB = deserB.WithTypeConverter(new RawSpeciesYamlConverter());
            }

            var deser = deserB.Build();

            var content = sr.ReadToEnd();

            try
            {
                deser.Deserialize <T>(content);
            }
            catch (YamlException yex)
            {
                return(DetermineConditionResult.FromError(yex, Path.GetFileName(file)));
            }

            return(DetermineConditionResult.FromSuccess());
        }
예제 #5
0
        /// <summary>
        /// Loads the sass from disk.
        /// </summary>
        private async Task <DetermineConditionResult> LoadSassAsync()
        {
            var sassPath     = UPath.Combine(UPath.Root, "Sass", "sass.txt");
            var sassNSFWPath = UPath.Combine(UPath.Root, "Sass", "sass-nsfw.txt");

            var getSassStream = _content.OpenLocalStream(sassPath);

            if (getSassStream.IsSuccess)
            {
                await using var sassStream = getSassStream.Entity;
                _sass = (await AsyncIO.ReadAllLinesAsync(sassStream)).ToList();
            }
            else
            {
                return(DetermineConditionResult.FromError(getSassStream));
            }

            var getNSFWSassStream = _content.OpenLocalStream(sassNSFWPath);

            if (getNSFWSassStream.IsSuccess)
            {
                await using var nsfwSassStream = getNSFWSassStream.Entity;
                _sassNSFW = (await AsyncIO.ReadAllLinesAsync(nsfwSassStream)).ToList();
            }
            else
            {
                return(DetermineConditionResult.FromError(getNSFWSassStream));
            }

            _isSassLoaded = true;
            return(DetermineConditionResult.FromSuccess());
        }
예제 #6
0
        public async Task <DetermineConditionResult> CanUserTransformUserAsync
        (
            IGuild discordServer,
            IUser invokingUser,
            IUser targetUser
        )
        {
            var getLocalProtectionResult = await GetOrCreateServerUserProtectionAsync(targetUser, discordServer);

            if (!getLocalProtectionResult.IsSuccess)
            {
                return(DetermineConditionResult.FromError(getLocalProtectionResult));
            }

            var localProtection = getLocalProtectionResult.Entity;

            if (!localProtection.HasOptedIn)
            {
                return(DetermineConditionResult.FromError("The target hasn't opted into transformations."));
            }

            var getGlobalProtectionResult = await GetOrCreateGlobalUserProtectionAsync(targetUser);

            if (!getGlobalProtectionResult.IsSuccess)
            {
                return(DetermineConditionResult.FromError(getGlobalProtectionResult));
            }

            var globalProtection = getGlobalProtectionResult.Entity;

            switch (localProtection.Type)
            {
            case ProtectionType.Blacklist:
            {
                return(globalProtection.Blacklist.All(u => u.DiscordID != (long)invokingUser.Id)
                        ? DetermineConditionResult.FromSuccess()
                        : DetermineConditionResult.FromError("You're on that user's blacklist."));
            }

            case ProtectionType.Whitelist:
            {
                return(globalProtection.Whitelist.Any(u => u.DiscordID == (long)invokingUser.Id)
                        ? DetermineConditionResult.FromSuccess()
                        : DetermineConditionResult.FromError("You're not on that user's whitelist."));
            }

            default:
            {
                throw new ArgumentOutOfRangeException();
            }
            }
        }
예제 #7
0
        /// <inheritdoc />
        public async ValueTask <DetermineConditionResult> CheckAsync(RequireContextAttribute attribute, CancellationToken ct)
        {
            var getChannel = await _channelAPI.GetChannelAsync(_context.ChannelID, ct);

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

            var channel = getChannel.Entity;

            switch (attribute.Context)
            {
            case ChannelContext.DM:
            {
                return(channel.Type is DM
                        ? DetermineConditionResult.FromSuccess()
                        : DetermineConditionResult.FromError("This command can only be used in a DM."));
            }

            case ChannelContext.GroupDM:
            {
                return(channel.Type is GroupDM
                        ? DetermineConditionResult.FromSuccess()
                        : DetermineConditionResult.FromError("This command can only be used in a group DM."));
            }

            case ChannelContext.Guild:
            {
                return(channel.Type is GuildText or GuildVoice or GuildCategory or GuildNews or GuildStore
                        ? DetermineConditionResult.FromSuccess()
                        : DetermineConditionResult.FromError("This command can only be used in a guild."));
            }

            default:
            {
                throw new ArgumentOutOfRangeException();
            }
            }
        }
예제 #8
0
        public async Task <DetermineConditionResult> HasPermissionAsync
        (
            [NotNull] IGuild discordServer,
            [NotNull] IGuildUser discordUser,
            IPermission requiredPermission,
            PermissionTarget target
        )
        {
            // The server owner always has all permissions by default
            if (discordServer.OwnerId == discordUser.Id)
            {
                return(DetermineConditionResult.FromSuccess());
            }

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

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

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

                return(DetermineConditionResult.FromError("Permission denied."));
            }

            var hasPermission = false;

            // Check if the user is part of any roles which this permission applies to
            var rolePermission = await GetApplicableRolePermissions(discordUser)
                                 .FirstOrDefaultAsync
                                 (
                p =>
                p.Permission == requiredPermission.UniqueIdentifier &&
                p.Target == target
                                 );

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

            // Check if the user has the permission applied to themselves
            var userPermission = await GetApplicableUserPermissions(discordServer, discordUser)
                                 .FirstOrDefaultAsync
                                 (
                p =>
                p.Permission == requiredPermission.UniqueIdentifier &&
                p.Target == target
                                 );

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

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

            if (hasPermission)
            {
                return(DetermineConditionResult.FromSuccess());
            }

            return(DetermineConditionResult.FromError("Permission denied."));
        }