Example #1
0
    /// <summary>
    /// Applies the given autorole to the given user, if it is applicable. If the user no longer qualifies,
    /// the autorole is removed.
    /// </summary>
    /// <param name="autorole">The autorole.</param>
    /// <param name="guildID">The ID of the guild the user is on.</param>
    /// <param name="userID">The ID of the user.</param>
    /// <param name="ct">The cancellation token in use.</param>
    /// <returns>A modification result which may or may not have succeeded.</returns>
    public async Task <Result <AutoroleUpdateStatus> > UpdateAutoroleForUserAsync
    (
        AutoroleConfiguration autorole,
        Snowflake guildID,
        Snowflake userID,
        CancellationToken ct = default
    )
    {
        if (!autorole.IsEnabled)
        {
            return(Disabled);
        }

        if (!autorole.Conditions.Any())
        {
            return(Unconditional);
        }

        var getRoles = await _guildAPI.GetGuildRolesAsync(guildID, ct);

        if (!getRoles.IsSuccess)
        {
            return(Result <AutoroleUpdateStatus> .FromError(getRoles));
        }

        var roles = getRoles.Entity;

        if (roles.All(r => r.ID != autorole.DiscordRoleID))
        {
            // If the role can't be found any longer, we disable it
            var disableAutoroleAsync = await _autoroles.DisableAutoroleAsync(autorole, ct);

            return(!disableAutoroleAsync.IsSuccess
                ? Result <AutoroleUpdateStatus> .FromError(disableAutoroleAsync)
                : Disabled);
        }

        var getIsUserQualified = await _autoroles.IsUserQualifiedForAutoroleAsync(autorole, userID, ct);

        if (!getIsUserQualified.IsSuccess)
        {
            return(Result <AutoroleUpdateStatus> .FromError(getIsUserQualified));
        }

        var isUserQualified = getIsUserQualified.Entity;

        var getMember = await _guildAPI.GetGuildMemberAsync(guildID, userID, ct);

        if (!getMember.IsSuccess)
        {
            return(Result <AutoroleUpdateStatus> .FromError(getMember));
        }

        var member = getMember.Entity;

        if (!member.User.IsDefined(out var user))
        {
            return(Unqualified);
        }

        if (user.IsBot.IsDefined(out var isBot) && isBot)
        {
            return(Unqualified);
        }

        var userHasRole = member.Roles.Contains(autorole.DiscordRoleID);

        switch (isUserQualified)
        {
        case true when userHasRole:
        {
            return(Unchanged);
        }

        case false when userHasRole:
        {
            var removeRole = await _guildAPI.RemoveGuildMemberRoleAsync
                             (
                guildID,
                userID,
                autorole.DiscordRoleID,
                ct : ct
                             );

            if (!removeRole.IsSuccess)
            {
                return(Result <AutoroleUpdateStatus> .FromError(removeRole));
            }

            var getConfirmation = await _autoroles.GetOrCreateAutoroleConfirmationAsync
                                  (
                autorole,
                userID,
                ct
                                  );

            if (!getConfirmation.IsSuccess)
            {
                return(Removed);
            }

            // Remove any existing affirmation
            var confirmation       = getConfirmation.Entity;
            var removeConfirmation = await _autoroles.RemoveAutoroleConfirmationAsync(confirmation, ct);

            return(!removeConfirmation.IsSuccess
                    ? Result <AutoroleUpdateStatus> .FromError(removeConfirmation)
                    : Removed);
        }

        case false:
        {
            // At this point, the user doesn't have the role, and either is or is not qualified.
            // We consider a no-op for an unqualified user a success.
            return(Unqualified);
        }
        }

        if (autorole.RequiresConfirmation)
        {
            var getConfirmation = await _autoroles.GetOrCreateAutoroleConfirmationAsync
                                  (
                autorole,
                userID,
                ct
                                  );

            if (!getConfirmation.IsSuccess)
            {
                return(Result <AutoroleUpdateStatus> .FromError(getConfirmation));
            }

            var confirmation = getConfirmation.Entity;
            if (!confirmation.IsConfirmed)
            {
                // We consider a no-op for an qualified but not affirmed user a success.
                return(RequiresAffirmation);
            }
        }

        var addRole = await _guildAPI.AddGuildMemberRoleAsync(guildID, userID, autorole.DiscordRoleID, ct : ct);

        return(!addRole.IsSuccess
            ? Result <AutoroleUpdateStatus> .FromError(addRole)
            : Applied);
    }