Beispiel #1
0
        private string GetImageLeaderboardEntry(ScoreboardSummaryEntry team, ScoreboardImageDetails image, int friendlyIndex, bool useAbbreviatedDivision = false, string prefix = "#")
        {
            string divisionFormatString = useAbbreviatedDivision ? "{0,6}" : "  {0,-10}";
            string vulnPenString        = new string(' ', 10);

            if (ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.VulnerabilityDisplay, image.VulnerabilitiesFound, image.VulnerabilitiesRemaining))
            {
                vulnPenString = $"{image.VulnerabilitiesFound,5}v {image.Penalties,2}p";
            }

            return($"{prefix}{friendlyIndex,-5}{team.TeamId,-7}{team.Location,4}" +
                   string.Format(divisionFormatString, AbbreviateDivision(team)) + $"{team.Tier,10}" +
                   $"{ScoreRetrieverMetadata.FormattingOptions.FormatScoreForLeaderboard(image.Score),13}" +
                   vulnPenString +
                   $"{(ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.TimeDisplay, image.PlayTime) ? image.PlayTime.ToHoursMinutesSecondsString() : ""),7}" +
                   $"{image.Warnings.ToConciseString(),4}");
        }
        public async Task <ScoreboardDetails> GetDetailsAsync(TeamId team)
        {
            if (team == null)
            {
                throw new ArgumentNullException(nameof(team));
            }

            string detailsPage;
            Uri    detailsUri = BuildDetailsUri(team);

            await RateLimiter.GetWorkAuthorizationAsync().ConfigureAwait(false);

            Task <string> stringTask = Client.GetStringAsync(detailsUri);

            RateLimiter.AddPrerequisite(stringTask);
            try
            {
                detailsPage = await stringTask.ConfigureAwait(false);

                // hacky, cause they don't return a proper error page for nonexistant teams
                if (!detailsPage.Contains(@"<div id='chart_div' class='chart'>"))
                {
                    throw new ArgumentException("The given team does not exist.");
                }
            }
            catch (HttpRequestException e)
            {
                throw new InvalidOperationException("Error getting team details page, perhaps the scoreboard is offline?", e);
            }

            ScoreboardDetails retVal = new ScoreboardDetails();

            retVal.OriginUri = detailsUri;

            HtmlDocument doc = new HtmlDocument();

            doc.LoadHtml(detailsPage);
            var timestampHeader = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/h2[2]")?.InnerText;

            retVal.SnapshotTimestamp = timestampHeader == null ? DateTimeOffset.UtcNow : DateTimeOffset.Parse(timestampHeader.Replace("Generated At: ", string.Empty).Replace("UTC", "+0:00"));
            var summaryHeaderRow     = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[1]/tr[1]");
            var summaryHeaderRowData = summaryHeaderRow.ChildNodes.Select(x => x.InnerText).ToArray();
            var summaryRow           = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[1]/tr[2]");
            var summaryRowData       = summaryRow.ChildNodes.Select(x => x.InnerText).ToArray();

            ParseDetailedSummaryEntry(retVal, summaryRowData);

            // summary parsed
            var imagesTable = doc.DocumentNode.SelectSingleNode("/html/body/div[2]/div/table[2]").ChildNodes.Where(n => n.Name != "#text").ToArray();

            for (int i = 1; i < imagesTable.Length; i++)
            {
                // skip team IDs to account for legacy scoreboards
                string[] dataEntries         = imagesTable[i].ChildNodes.Select(n => n.InnerText.Trim()).SkipWhile(s => TeamId.TryParse(s, out TeamId _)).ToArray();
                ScoreboardImageDetails image = new ScoreboardImageDetails();
                image.PointsPossible           = 100;
                image.ImageName                = dataEntries[0];
                image.PlayTime                 = Utilities.ParseHourMinuteSecondTimespan(dataEntries[1]);
                image.VulnerabilitiesFound     = int.Parse(dataEntries[2]);
                image.VulnerabilitiesRemaining = int.Parse(dataEntries[3]);
                image.Penalties                = int.Parse(dataEntries[4]);
                image.Score     = double.Parse(dataEntries[5]);
                image.Warnings |= dataEntries[6].Contains("T") ? ScoreWarnings.TimeOver : 0;
                image.Warnings |= dataEntries[6].Contains("M") ? ScoreWarnings.MultiImage : 0;
                retVal.Images.Add(image);
            }

            // reparse summary table (CCS+Cisco case)
            // pseudoimages: Cisco, administrative adjustment (usually penalty)
            int ciscoIndex   = summaryHeaderRowData.IndexOfWhere(x => x.ToLower().Contains("cisco"));
            int penaltyIndex = summaryHeaderRowData.IndexOfWhere(x => x.ToLower().Contains("adjust"));

            ScoreboardImageDetails CreatePseudoImage(string name, double score, double possible)
            {
                var image = new ScoreboardImageDetails();

                image.PointsPossible = possible;
                image.ImageName      = name;
                image.Score          = score;

                image.VulnerabilitiesFound     = 0;
                image.VulnerabilitiesRemaining = 0;
                image.Penalties = 0;
                image.Warnings  = 0;
                image.PlayTime  = TimeSpan.Zero;

                return(image);
            }

            if (ciscoIndex != -1)
            {
                // pseudoimage
                // FIXME shouldn't display vulns and penalties and time

                double ciscoDenom = -1;
                try
                {
                    ciscoDenom = _roundInferenceService.GetCiscoPointsPossible(Round, retVal.Summary.Division, retVal.Summary.Tier);
                }
                catch
                {
                    // probably because round 0; unknown total
                }

                retVal.Images.Add(CreatePseudoImage("Cisco (Total)", double.Parse(summaryRowData[ciscoIndex]), ciscoDenom));
            }

            if (penaltyIndex != -1)
            {
                retVal.Images.Add(CreatePseudoImage("Administrative Adjustment", double.Parse(summaryRowData[penaltyIndex]), 0));
            }

            // score graph
            try
            {
                var   teamScoreGraphHeader = new Regex(@"\['Time'(?:, '(\w+)')* *\]");
                var   teamScoreGraphEntry  = new Regex(@"\['(\d{2}/\d{2} \d{2}:\d{2})'(?:, (-?\d+|null))*\]");
                Match headerMatch          = teamScoreGraphHeader.Match(detailsPage);
                if (headerMatch?.Success ?? false)
                {
                    retVal.ImageScoresOverTime = new Dictionary <string, SortedDictionary <DateTimeOffset, int?> >();
                    string[] imageHeaders = headerMatch.Groups[1].Captures.Cast <Capture>().Select(c => c.Value).ToArray();
                    SortedDictionary <DateTimeOffset, int?>[] dictArr = new SortedDictionary <DateTimeOffset, int?> [imageHeaders.Length];
                    for (int i = 0; i < dictArr.Length; i++)
                    {
                        dictArr[i] = new SortedDictionary <DateTimeOffset, int?>();
                        retVal.ImageScoresOverTime[imageHeaders[i]] = dictArr[i];
                    }
                    foreach (var m in teamScoreGraphEntry.Matches(detailsPage).Cast <Match>().Where(g => g?.Success ?? false))
                    {
                        DateTimeOffset dto = default(DateTimeOffset);
                        try
                        {
                            // MM/dd hh:mm
                            string   dateStr           = m.Groups[1].Value;
                            string[] dateStrComponents = dateStr.Split(' ');
                            string[] dateComponents    = dateStrComponents[0].Split('/');
                            string[] timeComponents    = dateStrComponents[1].Split(':');
                            dto = new DateTimeOffset(DateTimeOffset.UtcNow.Year, int.Parse(dateComponents[0]), int.Parse(dateComponents[1]), int.Parse(timeComponents[0]), int.Parse(timeComponents[1]), 0, TimeSpan.Zero);
                        }
                        catch
                        {
                            continue;
                        }

                        var captures = m.Groups[2].Captures;

                        for (int i = 0; i < captures.Count; i++)
                        {
                            int?scoreVal = null;
                            if (int.TryParse(captures[i].Value, out int thingValTemp))
                            {
                                scoreVal = thingValTemp;
                            }
                            dictArr[i][dto] = scoreVal;
                        }
                    }
                }
            }
            catch
            {
                // TODO log
            }
            return(retVal);
        }
Beispiel #3
0
        public string CreateImageLeaderboardEmbed(IEnumerable <KeyValuePair <ScoreboardSummaryEntry, ScoreboardImageDetails> > completeImageData, string filterDescription, int teamCount = -1, int pageNumber = 1, int pageSize = 15)
        {
            if (pageSize <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(pageSize));
            }

            if (teamCount == -1)
            {
                completeImageData = completeImageData.ToIList();
                teamCount         = completeImageData.Count();
            }

            int pageCount = (int)Math.Ceiling(((double)teamCount) / pageSize);

            pageNumber--;

            if (pageNumber < 0 || pageNumber >= pageCount)
            {
                throw new ArgumentOutOfRangeException(nameof(pageNumber));
            }

            IList <KeyValuePair <ScoreboardSummaryEntry, ScoreboardImageDetails> > thisPageImageData = completeImageData.Skip(pageNumber * pageSize).Take(pageSize).ToIList();

            StringBuilder resultBuilder = new StringBuilder();

            resultBuilder.Append("**CyberPatriot Image Scoreboard");
            if (!string.IsNullOrWhiteSpace(filterDescription))
            {
                resultBuilder.Append(", ").Append(filterDescription);
            }
            if (pageCount > 1)
            {
                resultBuilder.Append($" (Page {pageNumber + 1} of {pageCount})");
            }
            resultBuilder.AppendLine("**");

            ScoreboardImageDetails canonicalImage = thisPageImageData[0].Value;

            resultBuilder.AppendFormat("**`{0}`", canonicalImage.ImageName);
            int    vulnCt       = canonicalImage.VulnerabilitiesRemaining + canonicalImage.VulnerabilitiesFound;
            double pts          = canonicalImage.PointsPossible;
            bool   displayVulns = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.VulnerabilityDisplay, vulnCt);
            bool   displayPts   = ScoreFormattingOptions.EvaluateNumericDisplay(ScoreRetrieverMetadata.FormattingOptions.VulnerabilityDisplay, pts);

            if (displayVulns || displayPts)
            {
                resultBuilder.Append(": ");
                if (displayVulns)
                {
                    resultBuilder.Append(Utilities.Pluralize("vulnerability", vulnCt));

                    if (displayPts)
                    {
                        resultBuilder.Append(", ");
                    }
                }
                if (displayPts)
                {
                    resultBuilder.Append(ScoreRetrieverMetadata.FormattingOptions.FormatScore(pts)).Append(" points possible");
                }
            }

            resultBuilder.AppendLine("**");
            resultBuilder.AppendLine("```");

            bool conciseDivision = !thisPageImageData.Any(x => x.Key.Category != null);

            for (int i = 0; i < thisPageImageData.Count; i++)
            {
                var teamScore     = thisPageImageData[i];
                int friendlyIndex = i + 1 + (pageNumber * pageSize);
                resultBuilder.AppendLine(GetImageLeaderboardEntry(teamScore.Key, teamScore.Value, friendlyIndex, useAbbreviatedDivision: conciseDivision));
            }
            resultBuilder.AppendLine("```");

            return(resultBuilder.ToString());
        }