예제 #1
0
        public override Task <PreconditionResult> CheckPermissionsAsync(
            ICommandContext context, CommandInfo command, IServiceProvider serviceProvider)
        {
            Verify.IsNotNull(context, nameof(context));

            GlobalTournamentsManager globalManager = serviceProvider.GetService <GlobalTournamentsManager>();
            TournamentsManager       manager       = globalManager.GetOrAdd(context.Guild.Id, CreateTournamentsManager);

            // TD is only allowed to run commands when they are a director of the current tournament.
            Result <bool> result = manager.TryReadActionOnCurrentTournament(currentTournament =>
                                                                            currentTournament.GuildId == context.Guild.Id && CanActAsTournamentDirector(context, currentTournament)
                                                                            );

            if (result.Success && result.Value)
            {
                return(Task.FromResult(PreconditionResult.FromSuccess()));
            }
            else if (command.Name == SetupCommandName && context.Message.Content.Length > SetupCommand.Length)
            {
                // TODO: We should investigate if there's a better place to make this check, because the attribute
                // now knows about "setup"
                string tournamentName = context.Message.Content.Substring(SetupCommand.Length).Trim();
                if (IsAdminUser(context) ||
                    (manager.TryGetTournament(tournamentName, out ITournamentState tournament) &&
                     CanActAsTournamentDirector(context, tournament)))
                {
                    return(Task.FromResult(PreconditionResult.FromSuccess()));
                }
            }

            return(Task.FromResult(PreconditionResult.FromError("User did not have tournament director privileges.")));
        }
예제 #2
0
        public void TrySetCurrentTournamentWithNoCurrentTournament()
        {
            TournamentsManager manager = new TournamentsManager()
            {
                GuildId = 1
            };

            TournamentState state      = new TournamentState(1, DefaultTournamentName);
            TournamentState otherState = new TournamentState(1, DefaultTournamentName + "2");

            manager.AddOrUpdateTournament(DefaultTournamentName, state, (name, oldState) => oldState);

            Assert.IsTrue(
                manager.TrySetCurrentTournament(DefaultTournamentName, out string errorMessage),
                "Couldn't set current tournament.");
            manager.TryReadActionOnCurrentTournament(currentState =>
            {
                Assert.AreEqual(DefaultTournamentName, currentState.Name, "Unexpected tournament in TryReadAction");
                return(Task.CompletedTask);
            });
            manager.TryReadWriteActionOnCurrentTournament(currentState =>
            {
                Assert.AreEqual(
                    DefaultTournamentName, currentState.Name, "Unexpected tournament in TryReadWriteAction (Action)");
            });
            manager.TryReadWriteActionOnCurrentTournament(currentState =>
            {
                Assert.AreEqual(
                    DefaultTournamentName, currentState.Name, "Unexpected tournament in TryReadWriteAction (Func)");
                return(Task.CompletedTask);
            });
        }
예제 #3
0
        public static async Task DoReadActionOnCurrentTournamentForMemberAsync(
            this TournamentsManager manager, IUser user, Func <IReadOnlyTournamentState, Task> action)
        {
            Result <Task> result = manager.TryReadActionOnCurrentTournament(action);

            if (result.Success)
            {
                await result.Value;
                return;
            }

            IDMChannel channel = await user.GetOrCreateDMChannelAsync();

            await channel.SendMessageAsync(BotStrings.UnableToPerformCommand(result.ErrorMessage));
        }
예제 #4
0
        // TODO: Test these methods
        internal async Task HandleOnMessageReceived(SocketMessage message)
        {
            if (message.Content.TrimStart().StartsWith("!", StringComparison.InvariantCulture))
            {
                // Ignore commands
                this.Logger.Verbose("Message ignored because it's a command");
                return;
            }

            if (message.Author.Id == this.Client.CurrentUser.Id)
            {
                this.Logger.Verbose("Message ignored because it's from the bot");
                return;
            }

            // Only pay attention to messages in a guild channel
            if (!(message is IUserMessage userMessage &&
                  userMessage.Channel is IGuildChannel guildChannel &&
                  userMessage.Author is IGuildUser guildUser))
            {
                this.Logger.Verbose("Message ignored because it's not in a guild");
                return;
            }

            // TODO: See if there's a cheaper way to check if we have a current tournament without making it public.
            // This is a read-only lock, so it shouldn't be too bad, but it'll be blocked during write operations.
            // Don't use the helper method because we don't want to message the user each time if there's no tournament.
            // We also want to split this check and the TD check because we don't need to get the Discord member for
            // this check.
            TournamentsManager manager = this.GetTournamentsManager(guildChannel.Guild);
            Result <bool>      currentTournamentExists = manager.TryReadActionOnCurrentTournament(currentTournament => true);

            if (!currentTournamentExists.Success)
            {
                this.Logger.Verbose("Message ignored because no current tournament is running");
                return;
            }

            // We want to do the basic access checks before using the more expensive write lock.

            Result <bool> hasDirectorPrivileges = manager.TryReadActionOnCurrentTournament(
                currentTournament => HasTournamentDirectorPrivileges(currentTournament, guildChannel, guildUser));

            if (!(hasDirectorPrivileges.Success && hasDirectorPrivileges.Value))
            {
                this.Logger.Verbose(
                    "Message ignored because user {id} does not have tournament director privileges", guildUser.Id);
                return;
            }

            await manager.DoReadWriteActionOnCurrentTournamentForMemberAsync(
                guildUser,
                async currentTournament =>
            {
                // TODO: We need to have locks on these. Need to check the stages between locks, and ignore the message if
                // it changes.
                // Issue is that we should really rely on a command for this case. Wouldn't quite work with locks.
                // But that would be something like !start, which would stop the rest of the interaction.
                switch (currentTournament.Stage)
                {
                case TournamentStage.AddReaders:
                    await this.HandleAddReadersStage(currentTournament, guildChannel.Guild, message);
                    break;

                case TournamentStage.SetRoundRobins:
                    await this.HandleSetRoundRobinsStage(currentTournament, message);
                    break;

                case TournamentStage.AddTeams:
                    await this.HandleAddTeamsStage(currentTournament, message);
                    break;

                case TournamentStage.Rebracketing:
                    await this.HandleRebracketingStage(currentTournament, guildChannel.Guild, message);
                    break;

                default:
                    this.Logger.Verbose(
                        "Message ignored because it is during the stage {stage}", currentTournament.Stage);
                    break;
                }
            });
        }