Example #1
0
        public async Task <ITextChannel> CreateChannelsForFinals(
            ISelfUser botUser, ITournamentState state, Game finalsGame, int finalsRoundNumber, int roomIndex)
        {
            Verify.IsNotNull(this.Guild, "guild");
            Verify.IsNotNull(state, nameof(state));
            Verify.IsNotNull(finalsGame, nameof(finalsGame));

            TournamentRoles roles = this.GetTournamentRoles(state);

            ICategoryChannel finalsCategoryChannel = await this.Guild.CreateCategoryAsync("Finals");

            ITextChannel channel = await this.CreateTextChannelAsync(
                botUser,
                finalsCategoryChannel,
                finalsGame,
                roles,
                finalsRoundNumber,
                roomIndex);

            state.ChannelIds = state.ChannelIds
                               .Concat(new ulong[] { channel.Id })
                               .Concat(new ulong[] { finalsCategoryChannel.Id });

            return(channel);
        }
Example #2
0
        private async Task <ITextChannel> CreateTextChannelAsync(
            ISelfUser botUser,
            ICategoryChannel parent,
            Game game,
            TournamentRoles roles,
            int roundNumber,
            int roomNumber)
        {
            Verify.IsNotNull(this.Guild, "guild");
            Verify.IsNotNull(parent, nameof(parent));
            Verify.IsNotNull(game, nameof(game));
            Verify.IsNotNull(roles, nameof(roles));

            // The room and role names will be the same.
            this.Logger.Debug("Creating text channel for room {0} in round {1}", roomNumber, roundNumber);
            string       name    = GetTextRoomName(game.Reader, roundNumber);
            ITextChannel channel = await this.Guild.CreateTextChannelAsync(
                name,
                channelProps =>
            {
                channelProps.CategoryId = parent.Id;
            },
                RequestOptionsSettings.Default);

            this.Logger.Debug("Text channel for room {0} in round {1} created", roomNumber, roundNumber);

            // We need to add the bot's permissions first before we disable permissions for everyone.
            await channel.AddPermissionOverwriteAsync(
                botUser, PrivilegedOverwritePermissions, RequestOptionsSettings.Default);

            this.Logger.Debug("Adding permissions to text channel for room {0} in round {1}", roomNumber, roundNumber);
            await channel.AddPermissionOverwriteAsync(
                this.Guild.EveryoneRole, EveryonePermissions, RequestOptionsSettings.Default);

            await channel.AddPermissionOverwriteAsync(
                roles.DirectorRole, PrivilegedOverwritePermissions, RequestOptionsSettings.Default);

            if (roles.RoomReaderRoles.TryGetValue(game.Reader, out IRole readerRole))
            {
                await channel.AddPermissionOverwriteAsync(
                    readerRole, PrivilegedOverwritePermissions, RequestOptionsSettings.Default);
            }
            else
            {
                this.Logger.Warning("Could not find a reader role for a reader with ID {0}.", game.Reader?.Id);
            }

            List <Task> addTeamRolesToChannel = new List <Task>();

            foreach (Team team in game.Teams)
            {
                if (!roles.TeamRoles.TryGetValue(team, out IRole role))
                {
                    this.Logger.Warning("Team {name} did not have a role defined.", team.Name);
                    continue;
                }

                // TODO: Investigate if it's possible to parallelize this. Other attempts to do so (Task.WhenAll,
                // AsyncEnumerable's ParallelForEachAsync) have had bugs where roles sometimes aren't assigned to a
                // channel. Adding an await in the loop seems to be the only thing that
                await this.AddPermission(channel, role);
            }

            await Task.WhenAll(addTeamRolesToChannel);

            this.Logger.Debug("Added permissions to text channel for room {0} in round {1}", roomNumber, roundNumber);
            return(channel);
        }
Example #3
0
        public async Task SetupFinalsAsync(IGuildUser readerUser, string rawTeamNameParts)
        {
            ITextChannel channel = null;

            await this.DoReadWriteActionOnCurrentTournamentAsync(
                async currentTournament =>
            {
                if (currentTournament?.Stage != TournamentStage.RunningTournament)
                {
                    this.Logger.Debug("Could not start finals in stage {stage}", currentTournament?.Stage);
                    await this.SendChannelMessage(BotStrings.ErrorFinalsOnlySetDuringPrelimsOrPlayoffs);
                    return;
                }

                if (!currentTournament.TryGetReader(readerUser.Id, out Reader reader))
                {
                    this.Logger.Debug("Could not start finals because {1} is not a reader", readerUser.Id);
                    await this.SendChannelMessage(BotStrings.ErrorGivenUserIsntAReader);
                    return;
                }

                if (rawTeamNameParts == null)
                {
                    this.Logger.Debug(
                        "Could not start finals because no teams were specified");
                    await this.SendChannelMessage(BotStrings.ErrorNoTeamsSpecified);
                    return;
                }

                string combinedTeamNames = string.Join(" ", rawTeamNameParts).Trim();
                if (!TeamNameParser.TryGetTeamNamesFromParts(
                        combinedTeamNames, out IList <string> teamNames, out string errorMessage))
                {
                    this.Logger.Debug(
                        "Could not start finals because of this error: {errorMessage}", errorMessage);
                    await this.SendChannelMessage(BotStrings.ErrorGenericMessage(errorMessage));
                    return;
                }

                if (teamNames.Count != 2)
                {
                    this.Logger.Debug(
                        "Could not start finals because {count} teams were specified", teamNames.Count);
                    await this.SendChannelMessage(BotStrings.ErrorTwoTeamsMustBeSpecifiedFinals(teamNames.Count));
                    return;
                }

                Team[] teams = teamNames.Select(name => new Team()
                {
                    Name = name
                })
                               .ToArray();
                if (currentTournament.Teams.Intersect(teams).Count() != teams.Length)
                {
                    this.Logger.Debug(
                        "Could not start finals because some teams were not in the tournament", teamNames.Count);
                    await this.SendChannelMessage(
                        BotStrings.ErrorAtLeastOneTeamNotInTournament(string.Join(", ", teamNames)));
                    return;
                }

                // Create a finals channel and give access to the teams and readers
                Game finalsGame = new Game()
                {
                    Reader = reader,
                    Teams  = teams
                };

                int finalsRoundNumber   = currentTournament.Schedule.Rounds.Count + 1;
                IList <Game> finalGames =
                    currentTournament.Schedule.Rounds[currentTournament.Schedule.Rounds.Count - 1].Games;
                int roomIndex = 0;
                foreach (Game game in finalGames)
                {
                    if (game.Reader.Equals(reader))
                    {
                        break;
                    }

                    roomIndex++;
                }

                if (roomIndex >= finalGames.Count)
                {
                    // Need to have a fall-back somehow. For now default to the first room.
                    roomIndex = 0;
                }

                // TODO: Look into creating the channels after the update stage so we can release the lock
                // sooner. However, this does mean that a failure to create channels will leave us in a bad
                // state.
                channel = await this.ChannelManager.CreateChannelsForFinals(
                    this.Context.Client.CurrentUser, currentTournament, finalsGame, finalsRoundNumber, roomIndex);

                currentTournament.UpdateStage(
                    TournamentStage.Finals, out string nextStageTitle, out string nextStageInstructions);
            });

            if (channel != null)
            {
                this.Logger.Debug("Finals started successfully");
                await this.Context.Channel.SendMessageAsync(
                    BotStrings.FinalsParticipantsPleaseJoin(channel.Mention),
                    options : RequestOptionsSettings.Default);
            }
        }