public async Task Run(CommandEventArgs e) { if (!_context.IsInitialized) { await e.Channel.SendMessage($"**The data is loading but is not ready yet...**"); return; } var take = 10; var args = CommandHelper.ParseArgs(e, new { Query = "", Exact = false, Species = false, Tribe = "", Owner = "", Skip = 0, OldMap = false }, x => x.For(y => y.Query, noPrefix: true, untilNextToken: true, isRequired: true) .For(y => y.Exact, flag: true) .For(y => y.Species, flag: true) .For(y => y.Tribe, untilNextToken: true) .For(y => y.Owner, untilNextToken: true) .For(y => y.Skip, defaultValue: 0) .For(y => y.OldMap, flag: true)); if (args == null || args.Skip < 0 || string.IsNullOrWhiteSpace(args.Query) || args.Query.Length < 2) { await e.Channel.SendMessage(string.Join(Environment.NewLine, new string[] { $"**My logic circuits cannot process this command! I am just a bot after all... :(**", !string.IsNullOrWhiteSpace(SyntaxHelp) ? $"Help me by following this syntax: **!{Name}** {SyntaxHelp}" : null }.Where(x => x != null))); return; } var aliases = ArkSpeciesAliases.Instance.GetAliases(args.Query); var speciesNames = aliases ?? new[] { args.Query }; var filtered = _context.Creatures?.Where(x => x.Tamed == true); if (args.Tribe != null) { filtered = filtered.Where(x => x.Tribe != null && x.Tribe.Equals(args.Tribe, StringComparison.OrdinalIgnoreCase)); } if (args.Owner != null) { //this is what the user expect when they issue the command //if they are in a tribe they want the dinos from the tribe var tribes = _context.Tribes.Where(x => x.Members != null && x.Members.Contains(args.Owner, StringComparer.OrdinalIgnoreCase)).Select(x => x.Name).ToArray(); filtered = filtered.Where(x => (x.OwnerName != null && x.OwnerName.Equals(args.Owner, StringComparison.OrdinalIgnoreCase)) || (x.Tribe != null && tribes.Length > 0 && tribes.Contains(x.Tribe))); } if (args.Exact) { filtered = filtered?.Where(x => x.Name != null && x.Name.Equals(args.Query, StringComparison.OrdinalIgnoreCase)); } else if (args.Species) { filtered = filtered?.Where(x => speciesNames != null && x.SpeciesClass != null && speciesNames.Contains(x.SpeciesClass, StringComparer.OrdinalIgnoreCase)); } else { filtered = filtered?.Where(x => (x.Name != null && x.Name.IndexOf(args.Query, StringComparison.OrdinalIgnoreCase) != -1) || (speciesNames != null && x.SpeciesClass != null && speciesNames.Contains(x.SpeciesClass, StringComparer.OrdinalIgnoreCase))); } var matches = filtered?.OrderByDescending(x => x.FullLevel ?? x.BaseLevel).ThenByDescending(x => x.Experience ?? decimal.MinValue).Skip(args.Skip).Take(take).ToArray(); var count = filtered.Count(); var nextUpdate = _context.ApproxTimeUntilNextUpdate; var nextUpdateTmp = nextUpdate?.ToStringCustom(); var nextUpdateString = (nextUpdate.HasValue ? (!string.IsNullOrWhiteSpace(nextUpdateTmp) ? $", next update in ~{nextUpdateTmp}" : ", waiting for new update ...") : ""); var lastUpdate = _context.LastUpdate; var lastUpdateString = lastUpdate.ToStringWithRelativeDay(); if (nextUpdate.HasValue) { nextUpdate = TimeSpan.FromSeconds(Math.Round(nextUpdate.Value.TotalSeconds)); } if (matches == null || matches.Length < 1) { await e.Channel.SendMessage($"**No matching tamed creatures found!** (updated {lastUpdateString}{nextUpdateString})"); if (args.Species && aliases == null && _context.Creatures != null && _context.ArkSpeciesStatsData?.SpeciesStats != null) { //var allspecies = _context.Creatures.Select(x => x.SpeciesName).Distinct(StringComparer.OrdinalIgnoreCase).Where(x => !x.Equals("raft", StringComparison.OrdinalIgnoreCase)).ToArray(); var sequence = args.Query.ToLower().ToCharArray(); var tamableSpecies = _context.ArkSpeciesStatsData.SpeciesStats.Select(x => x.Name).ToArray(); //intersection to remove all non-tamable creatures from the list of suggestions (ex. alphas, bosses) var similarity = ArkSpeciesAliases.Instance.Aliases.Where(x => tamableSpecies.Intersect(x, StringComparer.OrdinalIgnoreCase).Count() > 0).Select(x => { var s = x.Select(y => new { key = y, s = StatisticsHelper.CompareToCharacterSequence(y, sequence) }).OrderByDescending(y => y.s).FirstOrDefault(); return(new { key = s.key, primary = x.FirstOrDefault(), all = x, val = s.s /*s >= 0 ? s : 0*/ }); }).ToArray(); var possible = StatisticsHelper.FilterUsingStandardDeviation(similarity, x => x.val, (dist, sd) => dist >= sd * 1.5, false); if (possible != null && possible.Length > 0) { var distances = possible.Select((x, i) => new { key = x.key, primary = x.primary, index = i, similarity = x.val, result = args.Query.FindLowestLevenshteinWordDistanceInString(x.key) }) .Where(x => x.result != null) .OrderBy(x => x.result.Item2).ThenBy(x => x.similarity).ToArray(); var best = StatisticsHelper.FilterUsingStandardDeviation(distances, x => x.result.Item2, (dist, sd) => dist <= sd, false); var suggestions = best.Select(x => $"***\"{x.primary}\"***").ToArray().Join((n, l) => n == l ? " *or* " : "\u200B*,* "); await e.Channel.SendMessage($"*Did you perhaps mean* {suggestions}\u200B*?*"); //\u200B } } } else { var sb = new StringBuilder(); sb.Append($"**Found {count} matching tamed creatures"); if (count > 10) { sb.Append(" (showing top " + (args.Skip == 0 ? matches.Length.ToString() : $"{args.Skip + 1}-{args.Skip + matches.Length}") + ")"); } sb.AppendLine($"** (updated {lastUpdateString}{nextUpdateString})"); foreach (var x in matches) { sb.Append($"● {(!string.IsNullOrWhiteSpace(x.Name) ? $"**{x.Name}**, ***{x.SpeciesName}***" : $"**{x.SpeciesName}**")} (lvl ***{x.FullLevel ?? x.BaseLevel}***"); if (x.Tribe != null || x.OwnerName != null) { sb.Append($" owned by ***{string.Join("/", new[] { x.Tribe, x.OwnerName }.Where(y => !string.IsNullOrWhiteSpace(y)).ToArray())}***"); } sb.AppendLine(Invariant($") at ***{x.Latitude:N1}***, ***{x.Longitude:N1}***")); } await CommandHelper.SendPartitioned(e.Channel, sb.ToString()); await CommandHelper.SendAnnotatedMap(e.Channel, matches.Select(x => new PointF((float)x.Longitude, (float)x.Latitude)).ToArray(), _config.TempFileOutputDirPath, !args.OldMap? 20f : 10f, template : !args.OldMap?MapTemplate.Vectorized : MapTemplate.Sketch); }