public string GetPossibleInvasionEncounters()
        {
            var toInt = new Func <string, int>(x =>
            {
                var val = x.Split('_')[0];
                if (!int.TryParse(val, out var result))
                {
                    Console.Error.WriteLine($"Failed to parse {val} as integer");
                }
                return(result);
            });
            var first  = string.Join(", ", Encounters.First.Select(x => MasterFile.GetPokemon(toInt(x), 0)?.Name));
            var second = string.Join(", ", Encounters.Second.Select(x => MasterFile.GetPokemon(toInt(x), 0)?.Name));
            //var third = string.Join(", ", invasion.Encounters.Third.Select(x => Database.Instance.Pokemon[x].Name));
            var msg = string.Empty;

            if (SecondReward ?? false)
            {
                //85%/15% Rate
                msg += $"85% - {first}\r\n";
                msg += $"15% - {second}\r\n";
            }
            else
            {
                //100% Rate
                msg += $"100% - {first}\r\n";
            }
            return(msg);
        }
Exemple #2
0
        public async Task PostNestsAsync(CommandContext ctx,
                                         [Description("")] string args = null)
        {
            var guildId = ctx.Guild?.Id ?? ctx.Client.Guilds.Keys.FirstOrDefault(x => _dep.WhConfig.Servers.ContainsKey(x));

            if (!_dep.WhConfig.Servers.ContainsKey(guildId))
            {
                await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NOT_IN_DISCORD_SERVER"), DiscordColor.Red);

                return;
            }

            var server    = _dep.WhConfig.Servers[guildId];
            var channelId = server.NestsChannelId;
            var channel   = await ctx.Client.GetChannelAsync(channelId);

            if (channel == null)
            {
                await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NESTS_DISABLED").FormatText(ctx.User.Username), DiscordColor.Red);

                return;
            }

            var deleted = await ctx.Client.DeleteMessages(channelId);

            if (deleted.Item2 == 0)
            {
                _logger.Warn($"Failed to delete messages in channel: {channelId}");
            }

            var nests = GetNests(_dep.WhConfig.Database.Nests.ToString());

            if (nests == null)
            {
                await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NESTS_LIST").FormatText(ctx.User.Username));

                return;
            }

            var postNestAsList = string.Compare(args, "list", true) == 0;

            if (postNestAsList)
            {
                var groupedNests = GroupNests(guildId, nests);
                groupedNests.ToImmutableSortedDictionary();
                var sortedKeys = groupedNests.Keys.ToList();
                sortedKeys.Sort();
                foreach (var key in sortedKeys)
                {
                    var eb = new DiscordEmbedBuilder
                    {
                        Title       = key,
                        Description = string.Empty,
                        Color       = DiscordColor.Green
                    };
                    var message = string.Empty;
                    foreach (var nest in groupedNests[key])
                    {
                        if (nest.Average < server.NestsMinimumPerHour)
                        {
                            continue;
                        }

                        var pkmn      = MasterFile.GetPokemon(nest.PokemonId, 0);
                        var pkmnName  = Translator.Instance.GetPokemonName(pkmn.PokedexId);
                        var gmapsLink = string.Format(Strings.GoogleMaps, nest.Latitude, nest.Longitude);
                        // TODO: Check if possible shiny
                        message += $"[**{nest.Name}**]({gmapsLink}): {pkmnName} (#{nest.PokemonId}) {nest.Average:N0} per hour\r\n";
                        if (message.Length >= 2048)
                        {
                            eb.Description = message.Substring(0, Math.Min(message.Length, 2048));
                            message        = string.Empty;
                            await channel.SendMessageAsync(embed : eb);

                            eb = new DiscordEmbedBuilder
                            {
                                Title       = key,
                                Description = string.Empty,
                                Color       = DiscordColor.Green
                            };
                        }
                    }
                    if (message.Length > 0)
                    {
                        eb.Description = message;
                        message        = string.Empty;
                        await channel.SendMessageAsync(embed : eb);
                    }
                    Thread.Sleep(1000);
                }
            }
            else
            {
                var cities = server.CityRoles.Select(x => x.ToLower());
                for (var i = 0; i < nests.Count; i++)
                {
                    var nest = nests[i];
                    if (nest.Average == 0)
                    {
                        continue;
                    }

                    try
                    {
                        var eb       = GenerateNestMessage(guildId, ctx.Client, nest);
                        var geofence = _dep.Whm.GetGeofence(nest.Latitude, nest.Longitude);
                        if (geofence == null)
                        {
                            //_logger.Warn($"Failed to find geofence for nest {nest.Key}.");
                            continue;
                        }

                        if (!cities.Contains(geofence.Name.ToLower()))
                        {
                            continue;
                        }

                        if (nest.Average < server.NestsMinimumPerHour)
                        {
                            continue;
                        }

                        await channel.SendMessageAsync(embed : eb);

                        Thread.Sleep(200);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(ex);
                    }
                }
            }
        }
Exemple #3
0
        private IReadOnlyDictionary <string, string> GetProperties(DiscordGuild guild, WhConfig whConfig, string city, string raidImageUrl)
        {
            var pkmnInfo        = MasterFile.GetPokemon(PokemonId, Form);
            var name            = IsEgg ? "Egg" /*TODO: Localize*/ : Translator.Instance.GetPokemonName(PokemonId);
            var form            = Translator.Instance.GetFormName(Form);
            var gender          = Gender.GetPokemonGenderIcon();
            var level           = Level;
            var move1           = Translator.Instance.GetMoveName(FastMove);
            var move2           = Translator.Instance.GetMoveName(ChargeMove);
            var types           = pkmnInfo?.Types;
            var type1           = types?[0];
            var type2           = types?.Count > 1 ? types?[1] : PokemonType.None;
            var type1Emoji      = types?[0].GetTypeEmojiIcons();
            var type2Emoji      = pkmnInfo?.Types?.Count > 1 ? types?[1].GetTypeEmojiIcons() : string.Empty;
            var typeEmojis      = $"{type1Emoji} {type2Emoji}";
            var weaknesses      = Weaknesses == null ? string.Empty : string.Join(", ", Weaknesses);
            var weaknessesEmoji = types?.GetWeaknessEmojiIcons();
            var perfectRange    = PokemonId.MaxCpAtLevel(20);
            var boostedRange    = PokemonId.MaxCpAtLevel(25);
            var worstRange      = PokemonId.MinCpAtLevel(20);
            var worstBoosted    = PokemonId.MinCpAtLevel(25);
            var exEmojiId       = MasterFile.Instance.Emojis["ex"];
            var exEmoji         = exEmojiId > 0 ? $"<:ex:{exEmojiId}>" : "EX";
            var teamEmojiId     = MasterFile.Instance.Emojis[Team.ToString().ToLower()];
            var teamEmoji       = teamEmojiId > 0 ? $"<:{Team.ToString().ToLower()}:{teamEmojiId}>" : Team.ToString();

            var gmapsLink               = string.Format(Strings.GoogleMaps, Latitude, Longitude);
            var appleMapsLink           = string.Format(Strings.AppleMaps, Latitude, Longitude);
            var wazeMapsLink            = string.Format(Strings.WazeMaps, Latitude, Longitude);
            var scannerMapsLink         = string.Format(whConfig.Urls.ScannerMap, Latitude, Longitude);
            var templatePath            = Path.Combine(whConfig.StaticMaps.TemplatesFolder, whConfig.StaticMaps.Raids.TemplateFile);
            var staticMapLink           = Utils.GetStaticMapsUrl(templatePath, whConfig.Urls.StaticMap, whConfig.StaticMaps.Raids.ZoomLevel, Latitude, Longitude, raidImageUrl, Team);
            var gmapsLocationLink       = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? gmapsLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, gmapsLink);
            var appleMapsLocationLink   = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? appleMapsLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, appleMapsLink);
            var wazeMapsLocationLink    = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? wazeMapsLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, wazeMapsLink);
            var scannerMapsLocationLink = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? scannerMapsLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, scannerMapsLink);
            var googleAddress           = Utils.GetGoogleAddress(city, Latitude, Longitude, whConfig.GoogleMapsKey);
            //var staticMapLocationLink = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? staticMapLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, staticMapLink);

            const string defaultMissingValue = "?";
            var          dict = new Dictionary <string, string>
            {
                //Raid boss properties
                { "pkmn_id", PokemonId.ToString() },
                { "pkmn_id_3", PokemonId.ToString("D3") },
                { "pkmn_name", name },
                { "pkmn_img_url", raidImageUrl },
                { "form", form },
                { "form_id", Form.ToString() },
                { "form_id_3", Form.ToString("D3") },
                { "is_egg", Convert.ToString(IsEgg) },
                { "is_ex", Convert.ToString(IsExEligible) },
                { "ex_emoji", exEmoji },
                { "team", Team.ToString() },
                { "team_emoji", teamEmoji },
                { "cp", CP ?? defaultMissingValue },
                { "lvl", level ?? defaultMissingValue },
                { "gender", gender ?? defaultMissingValue },
                { "move_1", move1 ?? defaultMissingValue },
                { "move_2", move2 ?? defaultMissingValue },
                { "moveset", $"{move1}/{move2}" },
                { "type_1", type1?.ToString() ?? defaultMissingValue },
                { "type_2", type2?.ToString() ?? defaultMissingValue },
                { "type_1_emoji", type1Emoji },
                { "type_2_emoji", type2Emoji },
                { "types", $"{type1}/{type2}" },
                { "types_emoji", typeEmojis },
                { "weaknesses", weaknesses },
                { "weaknesses_emoji", weaknessesEmoji },
                { "perfect_cp", perfectRange.ToString() },
                { "perfect_cp_boosted", boostedRange.ToString() },
                { "worst_cp", worstRange.ToString() },
                { "worst_cp_boosted", worstBoosted.ToString() },

                //Time properties
                { "start_time", StartTime.ToLongTimeString() },
                { "start_time_24h", StartTime.ToString("HH:mm:ss") },
                { "start_time_left", DateTime.Now.GetTimeRemaining(StartTime).ToReadableStringNoSeconds() },
                { "end_time", EndTime.ToLongTimeString() },
                { "end_time_24h", EndTime.ToString("HH:mm:ss") },
                { "end_time_left", EndTime.GetTimeRemaining().ToReadableStringNoSeconds() },

                //Location properties
                { "geofence", city ?? defaultMissingValue },
                { "lat", Latitude.ToString() },
                { "lng", Longitude.ToString() },
                { "lat_5", Math.Round(Latitude, 5).ToString() },
                { "lng_5", Math.Round(Longitude, 5).ToString() },

                //Location links
                { "tilemaps_url", staticMapLink },
                { "gmaps_url", gmapsLocationLink },
                { "applemaps_url", appleMapsLocationLink },
                { "wazemaps_url", wazeMapsLocationLink },
                { "scanmaps_url", scannerMapsLocationLink },

                { "address", googleAddress?.Address },

                //Gym properties
                { "gym_id", GymId },
                { "gym_name", GymName },
                { "gym_url", GymUrl },

                // Discord Guild properties
                { "guild_name", guild?.Name },
                { "guild_img_url", guild?.IconUrl },

                { "date_time", DateTime.Now.ToString() },

                //Misc properties
                { "br", "\r\n" }
            };

            return(dict);
        }
Exemple #4
0
        private IReadOnlyDictionary <string, string> GetProperties(MessageProperties properties)// DiscordGuild guild, WhConfig whConfig, string city, string pokemonImageUrl)
        {
            var pkmnInfo         = MasterFile.GetPokemon(Id, FormId);
            var pkmnName         = Translator.Instance.GetPokemonName(Id);
            var form             = Translator.Instance.GetFormName(FormId);
            var costume          = Translator.Instance.GetCostumeName(Costume);
            var gender           = Gender.GetPokemonGenderIcon();
            var genderEmoji      = Gender.GetGenderEmojiIcon();
            var level            = Level;
            var size             = Size?.ToString();
            var weather          = Weather?.ToString();
            var hasWeather       = Weather.HasValue && Weather != WeatherCondition.None;
            var isWeatherBoosted = pkmnInfo?.IsWeatherBoosted(Weather ?? WeatherCondition.None);
            var weatherKey       = $"weather_{Convert.ToInt32(Weather ?? WeatherCondition.None)}";
            var weatherEmoji     = string.IsNullOrEmpty(MasterFile.Instance.CustomEmojis[weatherKey])
                ? MasterFile.Instance.CustomEmojis.ContainsKey(weatherKey) && Weather != WeatherCondition.None
                    ? (Weather ?? WeatherCondition.None).GetWeatherEmojiIcon()
                    : string.Empty
                : MasterFile.Instance.CustomEmojis[weatherKey];
            var move1        = int.TryParse(FastMove, out var fastMoveId) ? Translator.Instance.GetMoveName(fastMoveId) : "Unknown";
            var move2        = int.TryParse(ChargeMove, out var chargeMoveId) ? Translator.Instance.GetMoveName(chargeMoveId) : "Unknown";
            var type1        = pkmnInfo?.Types?[0];
            var type2        = pkmnInfo?.Types?.Count > 1 ? pkmnInfo.Types?[1] : PokemonType.None;
            var type1Emoji   = pkmnInfo?.Types?[0].GetTypeEmojiIcons();
            var type2Emoji   = pkmnInfo?.Types?.Count > 1 ? pkmnInfo?.Types?[1].GetTypeEmojiIcons() : string.Empty;
            var typeEmojis   = $"{type1Emoji} {type2Emoji}";
            var catchPokemon = IsDitto ? Translator.Instance.GetPokemonName(DisplayPokemonId ?? Id) : pkmnName;
            var isShiny      = Shiny ?? false;
            var height       = double.TryParse(Height, out var realHeight) ? Math.Round(realHeight).ToString() : "";
            var weight       = double.TryParse(Weight, out var realWeight) ? Math.Round(realWeight).ToString() : "";

            var gmapsLink               = string.Format(Strings.GoogleMaps, Latitude, Longitude);
            var appleMapsLink           = string.Format(Strings.AppleMaps, Latitude, Longitude);
            var wazeMapsLink            = string.Format(Strings.WazeMaps, Latitude, Longitude);
            var scannerMapsLink         = string.Format(properties.Config.Urls.ScannerMap, Latitude, Longitude);
            var staticMapLink           = StaticMap.GetUrl(properties.Config.Urls.StaticMap, properties.Config.StaticMaps["pokemon"], Latitude, Longitude, properties.ImageUrl);
            var gmapsLocationLink       = UrlShortener.CreateShortUrl(properties.Config.ShortUrlApiUrl, gmapsLink);
            var appleMapsLocationLink   = UrlShortener.CreateShortUrl(properties.Config.ShortUrlApiUrl, appleMapsLink);
            var wazeMapsLocationLink    = UrlShortener.CreateShortUrl(properties.Config.ShortUrlApiUrl, wazeMapsLink);
            var scannerMapsLocationLink = UrlShortener.CreateShortUrl(properties.Config.ShortUrlApiUrl, scannerMapsLink);
            var address = new Location(null, properties.City, Latitude, Longitude).GetAddress(properties.Config);
            //var staticMapLocationLink = string.IsNullOrEmpty(whConfig.ShortUrlApiUrl) ? staticMapLink : NetUtil.CreateShortUrl(whConfig.ShortUrlApiUrl, staticMapLink);
            var pokestop = Pokestop.Pokestops.ContainsKey(PokestopId) ? Pokestop.Pokestops[PokestopId] : null;

            var greatLeagueEmoji = PvPLeague.Great.GetLeagueEmojiIcon();
            var ultraLeagueEmoji = PvPLeague.Ultra.GetLeagueEmojiIcon();
            var pvpStats         = GetPvP();

            const string defaultMissingValue = "?";
            var          dict = new Dictionary <string, string>
            {
                // Main properties
                { "pkmn_id", Convert.ToString(Id) },
                { "pkmn_id_3", Id.ToString("D3") },
                { "pkmn_name", pkmnName },
                { "pkmn_img_url", properties.ImageUrl },
                { "form", form },
                { "form_id", Convert.ToString(FormId) },
                { "form_id_3", FormId.ToString("D3") },
                { "costume", costume ?? defaultMissingValue },
                { "costume_id", Convert.ToString(Costume) },
                { "costume_id_3", Costume.ToString("D3") },
                { "cp", CP ?? defaultMissingValue },
                { "lvl", level ?? defaultMissingValue },
                { "gender", gender },
                { "gender_emoji", genderEmoji },
                { "size", size ?? defaultMissingValue },
                { "move_1", move1 ?? defaultMissingValue },
                { "move_2", move2 ?? defaultMissingValue },
                { "moveset", $"{move1}/{move2}" },
                { "type_1", type1?.ToString() ?? defaultMissingValue },
                { "type_2", type2?.ToString() ?? defaultMissingValue },
                { "type_1_emoji", type1Emoji },
                { "type_2_emoji", type2Emoji },
                { "types", $"{type1} | {type2}" },
                { "types_emoji", typeEmojis },
                { "atk_iv", Attack ?? defaultMissingValue },
                { "def_iv", Defense ?? defaultMissingValue },
                { "sta_iv", Stamina ?? defaultMissingValue },
                { "iv", IV ?? defaultMissingValue },
                { "iv_rnd", IVRounded ?? defaultMissingValue },
                { "is_shiny", Convert.ToString(isShiny) },

                // Catch rate properties
                { "has_capture_rates", Convert.ToString(CatchRate1.HasValue && CatchRate2.HasValue && CatchRate3.HasValue) },
                { "capture_1", CatchRate1.HasValue ? Math.Round(CatchRate1.Value * 100).ToString() : string.Empty },
                { "capture_2", CatchRate2.HasValue ? Math.Round(CatchRate2.Value * 100).ToString() : string.Empty },
                { "capture_3", CatchRate3.HasValue ? Math.Round(CatchRate3.Value * 100).ToString() : string.Empty },
                { "capture_1_emoji", CaptureRateType.PokeBall.GetCaptureRateEmojiIcon() },
                { "capture_2_emoji", CaptureRateType.GreatBall.GetCaptureRateEmojiIcon() },
                { "capture_3_emoji", CaptureRateType.UltraBall.GetCaptureRateEmojiIcon() },

                // PvP stat properties
                { "is_great", Convert.ToString(MatchesGreatLeague) },
                { "is_ultra", Convert.ToString(MatchesUltraLeague) },
                { "is_pvp", Convert.ToString(MatchesGreatLeague || MatchesUltraLeague) },
                //{ "great_league_stats", greatLeagueStats },
                //{ "ultra_league_stats", ultraLeagueStats },
                { "great_league_emoji", greatLeagueEmoji },
                { "ultra_league_emoji", ultraLeagueEmoji },
                { "pvp_stats", pvpStats },

                // Other properties
                { "height", height ?? defaultMissingValue },
                { "weight", weight ?? defaultMissingValue },
                { "is_ditto", Convert.ToString(IsDitto) },
                { "original_pkmn_id", Convert.ToString(DisplayPokemonId) },
                { "original_pkmn_id_3", (DisplayPokemonId ?? 0).ToString("D3") },
                { "original_pkmn_name", catchPokemon },
                { "is_weather_boosted", Convert.ToString(isWeatherBoosted) },
                { "has_weather", Convert.ToString(hasWeather) },
                { "weather", weather ?? defaultMissingValue },
                { "weather_emoji", weatherEmoji ?? defaultMissingValue },
                { "username", Username ?? defaultMissingValue },
                { "spawnpoint_id", SpawnpointId ?? defaultMissingValue },
                { "encounter_id", EncounterId ?? defaultMissingValue },

                // Time properties
                { "despawn_time", DespawnTime.ToString("hh:mm:ss tt") },
                { "despawn_time_24h", DespawnTime.ToString("HH:mm:ss") },
                { "despawn_time_verified", DisappearTimeVerified ? "" : "~" },
                { "is_despawn_time_verified", Convert.ToString(DisappearTimeVerified) },
                { "time_left", SecondsLeft.ToReadableString(true) ?? defaultMissingValue },

                // Location properties
                { "geofence", properties.City ?? defaultMissingValue },
                { "lat", Convert.ToString(Latitude) },
                { "lng", Convert.ToString(Longitude) },
                { "lat_5", Latitude.ToString("0.00000") },
                { "lng_5", Longitude.ToString("0.00000") },

                // Location links
                { "tilemaps_url", staticMapLink },
                { "gmaps_url", gmapsLocationLink },
                { "applemaps_url", appleMapsLocationLink },
                { "wazemaps_url", wazeMapsLocationLink },
                { "scanmaps_url", scannerMapsLocationLink },

                { "address", address?.Address },

                // Pokestop properties
                { "near_pokestop", Convert.ToString(pokestop != null) },
                { "pokestop_id", PokestopId ?? defaultMissingValue },
                { "pokestop_name", pokestop?.Name ?? defaultMissingValue },
                { "pokestop_url", pokestop?.Url ?? defaultMissingValue },

                // Discord Guild properties
                { "guild_name", properties.Guild?.Name },
                { "guild_img_url", properties.Guild?.IconUrl },

                // Event properties
                { "is_event", Convert.ToString(IsEvent.HasValue && IsEvent.Value) },

                { "date_time", DateTime.Now.ToString() },

                // Misc properties
                { "br", "\r\n" }
            };

            return(dict);
        }
Exemple #5
0
        public IReadOnlyDictionary <string, string> GetProperties(DiscordGuild guild, Nest nest, string pokemonImageUrl)
        {
            var      pkmnInfo        = MasterFile.GetPokemon(nest.PokemonId, 0);
            var      pkmnImage       = pokemonImageUrl;
            var      nestName        = nest.Name ?? "Unknown";
            var      type1           = pkmnInfo?.Types?[0];
            var      type2           = pkmnInfo?.Types?.Count > 1 ? pkmnInfo.Types?[1] : PokemonType.None;
            var      type1Emoji      = pkmnInfo?.Types?[0].GetTypeEmojiIcons();
            var      type2Emoji      = pkmnInfo?.Types?.Count > 1 ? pkmnInfo?.Types?[1].GetTypeEmojiIcons() : string.Empty;
            var      typeEmojis      = $"{type1Emoji} {type2Emoji}";
            var      gmapsLink       = string.Format(Strings.GoogleMaps, nest.Latitude, nest.Longitude);
            var      appleMapsLink   = string.Format(Strings.AppleMaps, nest.Latitude, nest.Longitude);
            var      wazeMapsLink    = string.Format(Strings.WazeMaps, nest.Latitude, nest.Longitude);
            var      scannerMapsLink = string.Format(_dep.WhConfig.Urls.ScannerMap, nest.Latitude, nest.Longitude);
            var      templatePath    = Path.Combine(_dep.WhConfig.StaticMaps.TemplatesFolder, _dep.WhConfig.StaticMaps.Nests.TemplateFile);
            var      staticMapLink   = Utils.GetStaticMapsUrl(templatePath, _dep.WhConfig.Urls.StaticMap, _dep.WhConfig.StaticMaps.Nests.ZoomLevel, nest.Latitude, nest.Longitude, pkmnImage, null, _dep.OsmManager.GetNest(nest.Name)?.FirstOrDefault());
            var      geofence        = _dep.Whm.GetGeofence(nest.Latitude, nest.Longitude);
            var      city            = geofence?.Name ?? "Unknown";
            Location address         = null;

            if (!string.IsNullOrEmpty(_dep.WhConfig.GoogleMapsKey))
            {
                address = Utils.GetGoogleAddress(city, nest.Latitude, nest.Longitude, _dep.WhConfig.GoogleMapsKey);
            }
            else if (!string.IsNullOrEmpty(_dep.WhConfig.NominatimEndpoint))
            {
                address = Utils.GetNominatimAddress(city, nest.Latitude, nest.Longitude, _dep.WhConfig.NominatimEndpoint);
            }

            var dict = new Dictionary <string, string>
            {
                //Main properties
                { "pkmn_id", Convert.ToString(nest.PokemonId) },
                { "pkmn_id_3", nest.PokemonId.ToString("D3") },
                { "pkmn_name", pkmnInfo?.Name },
                { "pkmn_img_url", pkmnImage },
                { "avg_spawns", Convert.ToString(nest.Average) },
                { "nest_name", nestName },
                { "type_1", Convert.ToString(type1) },
                { "type_2", Convert.ToString(type2) },
                { "type_1_emoji", type1Emoji },
                { "type_2_emoji", type2Emoji },
                { "types", $"{type1} | {type2}" },
                { "types_emojis", typeEmojis },

                //Location properties
                { "geofence", city },
                { "lat", Convert.ToString(nest.Latitude) },
                { "lng", Convert.ToString(nest.Longitude) },
                { "lat_5", Convert.ToString(Math.Round(nest.Latitude, 5)) },
                { "lng_5", Convert.ToString(Math.Round(nest.Longitude, 5)) },

                //Location links
                { "tilemaps_url", staticMapLink },
                { "gmaps_url", gmapsLink },
                { "applemaps_url", appleMapsLink },
                { "wazemaps_url", wazeMapsLink },
                { "scanmaps_url", scannerMapsLink },

                { "address", address?.Address },

                // Discord Guild properties
                { "guild_name", guild?.Name },
                { "guild_img_url", guild?.IconUrl },

                { "date_time", DateTime.Now.ToString() },

                //Misc properties
                { "br", "\r\n" }
            };

            return(dict);
        }
        public async Task ProcessRaidSubscription(RaidData raid)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(raid.PokemonId))
            {
                return;
            }

            var loc = _whm.GetGeofence(raid.Latitude, raid.Longitude);

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city for coordinates {raid.Latitude},{raid.Longitude}, skipping...");
                return;
            }

            var subscriptions = Manager.GetUserSubscriptionsByRaidBossId(raid.PokemonId);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject user;
            var pokemon = MasterFile.GetPokemon(raid.PokemonId, raid.Form);

            for (int i = 0; i < subscriptions.Count; i++)
            {
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Servers[user.GuildId].EnableSubscriptions)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    var member = await client.GetMemberById(user.GuildId, user.UserId);

                    if (member == null)
                    {
                        _logger.Warn($"Failed to find member with id {user.UserId}.");
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Info($"User {user.UserId} is not a supporter, skipping raid boss {pokemon.Name}...");
                        continue;
                    }

                    // Only check distance if user has it set
                    if (user.DistanceM > 0)
                    {
                        var distance = new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(raid.Latitude, raid.Longitude));
                        if (user.DistanceM < distance)
                        {
                            //Skip if distance is set and is not met.
                            _logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for raid boss {pokemon.Name}, raid is farther than set distance of '{user.DistanceM} meters.");
                            continue;
                        }
                    }

                    if (user.Gyms.Count > 0 && (!user.Gyms?.Exists(x =>
                                                                   !string.IsNullOrEmpty(x?.Name) &&
                                                                   (
                                                                       (raid.GymName?.ToLower()?.Contains(x.Name?.ToLower()) ?? false) ||
                                                                       (raid.GymName?.ToLower()?.StartsWith(x.Name?.ToLower()) ?? false)
                                                                   )
                                                                   ) ?? false))
                    {
                        //Skip if list is not empty and gym is not in list.
                        _logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for raid boss {pokemon.Name}, raid '{raid.GymName}' is not in list of subscribed gyms.");
                        continue;
                    }

                    var form   = Translator.Instance.GetFormName(raid.Form);
                    var exists = user.Raids.FirstOrDefault(x =>
                                                           x.PokemonId == raid.PokemonId &&
                                                           (string.IsNullOrEmpty(x.Form) || string.Compare(x.Form, form, true) == 0) &&
                                                           (string.IsNullOrEmpty(x.City) || (!string.IsNullOrEmpty(x.City) && string.Compare(loc.Name, x.City, true) == 0))
                                                           ) != null;
                    if (!exists)
                    {
                        //_logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for raid boss {pokemon.Name}, raid is in city '{loc.Name}'.");
                        continue;
                    }

                    var embed = raid.GenerateRaidMessage(user.GuildId, client, _whConfig, null, loc.Name);
                    foreach (var emb in embed.Embeds)
                    {
                        _queue.Enqueue(new NotificationItem(user, member, emb, pokemon.Name, loc.Name));
                    }

                    Statistics.Instance.SubscriptionRaidsSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            user          = null;
            loc           = null;

            await Task.CompletedTask;
        }
        public async Task ProcessPokemonSubscription(PokemonData pkmn)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(pkmn.Id))
            {
                return;
            }

            var loc = _whm.GetGeofence(pkmn.Latitude, pkmn.Longitude);

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                return;
            }

            var subscriptions = Manager.GetUserSubscriptionsByPokemonId(pkmn.Id);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject  user;
            PokemonSubscription subscribedPokemon;
            DiscordMember       member = null;
            var pokemon       = MasterFile.GetPokemon(pkmn.Id, pkmn.FormId);
            var matchesIV     = false;
            var matchesLvl    = false;
            var matchesGender = false;
            var matchesIVList = false;

            for (var i = 0; i < subscriptions.Count; i++)
            {
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Servers[user.GuildId].EnableSubscriptions)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    try
                    {
                        member = await client.GetMemberById(user.GuildId, user.UserId);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"FAILED TO GET MEMBER BY ID {user.UserId}");
                        _logger.Error(ex);
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Debug($"User {member?.Username} ({user.UserId}) is not a supporter, skipping pokemon {pokemon.Name}...");
                        continue;
                    }

                    if (member?.Roles == null || loc == null)
                    {
                        continue;
                    }

                    if (!member.Roles.Select(x => x?.Name?.ToLower()).Contains(loc?.Name?.ToLower()))
                    {
                        //_logger.Info($"User {member.Username} does not have city role {loc.Name}, skipping pokemon {pokemon.Name}.");
                        continue;
                    }

                    /*
                     * var exists = user.Pokemon.Exists(x => string.IsNullOrEmpty(x.City) || (!string.IsNullOrEmpty(x.City) && string.Compare(loc.Name, x.City, true) == 0));
                     * if (!exists)
                     * {
                     *  //_logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for Pokemon {pokemon.PokemonId} because the Pokemon is in city '{loc.Name}'.");
                     *  continue;
                     * }
                     */

                    // Only check distance if user has it set
                    if (user.DistanceM > 0)
                    {
                        var distance = new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(pkmn.Latitude, pkmn.Longitude));
                        if (user.DistanceM < distance)
                        {
                            //Skip if distance is set and is not with specified distance.
                            _logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for Pokemon {pokemon.Name}, Pokemon is farther than set distance of '{user.DistanceM} meters.");
                            continue;
                        }
                    }

                    var form = Translator.Instance.GetFormName(pkmn.FormId);
                    subscribedPokemon = user.Pokemon.FirstOrDefault(x =>
                                                                    x.PokemonId == pkmn.Id &&
                                                                    (string.IsNullOrEmpty(x.Form) || string.Compare(x.Form, form, true) == 0) &&
                                                                    (string.IsNullOrEmpty(x.City) || (!string.IsNullOrEmpty(x.City) && string.Compare(loc.Name, x.City, true) == 0))
                                                                    );
                    if (subscribedPokemon == null)
                    {
                        //_logger.Info($"User {member.Username} not subscribed to Pokemon {pokemon.Name} (Form: {form}).");
                        continue;
                    }

                    matchesIV = Filters.MatchesIV(pkmn.IV, subscribedPokemon.MinimumIV);
                    //var matchesCP = _whm.Filters.MatchesCpFilter(pkmn.CP, subscribedPokemon.MinimumCP);
                    matchesLvl    = Filters.MatchesLvl(pkmn.Level, (uint)subscribedPokemon.MinimumLevel, (uint)subscribedPokemon.MaximumLevel);
                    matchesGender = Filters.MatchesGender(pkmn.Gender, subscribedPokemon.Gender);
                    matchesIVList = subscribedPokemon.IVList?.Contains($"{pkmn.Attack}/{pkmn.Defense}/{pkmn.Stamina}") ?? false;

                    if (!(
                            (/*!subscribedPokemon.HasStats && */ matchesIV && matchesLvl && matchesGender) ||
                            (subscribedPokemon.HasStats && matchesIVList)
                            ))
                    {
                        continue;
                    }

                    var embed = await pkmn.GeneratePokemonMessage(user.GuildId, client, _whConfig, null, loc.Name);

                    foreach (var emb in embed.Embeds)
                    {
                        _queue.Enqueue(new NotificationItem(user, member, emb, pokemon.Name, loc.Name, pkmn));
                    }

                    Statistics.Instance.SubscriptionPokemonSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            member        = null;
            user          = null;
            loc           = null;
            pokemon       = null;

            await Task.CompletedTask;
        }
        public async Task ProcessPokemonSubscription(PokemonData pkmn)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(pkmn.Id))
            {
                return;
            }

            // Cache the result per-guild so that geospatial stuff isn't queried for every single subscription below
            Dictionary <ulong, GeofenceItem> locationCache = new Dictionary <ulong, GeofenceItem>();

            GeofenceItem GetGeofence(ulong guildId)
            {
                if (!locationCache.TryGetValue(guildId, out var geofence))
                {
                    geofence = _whm.GetGeofence(guildId, pkmn.Latitude, pkmn.Longitude);
                    locationCache.Add(guildId, geofence);
                }

                return(geofence);
            }

            var subscriptions = Manager.GetUserSubscriptionsByPokemonId(pkmn.Id);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject  user;
            PokemonSubscription subscribedPokemon;
            DiscordMember       member = null;
            var pokemon       = MasterFile.GetPokemon(pkmn.Id, pkmn.FormId);
            var matchesIV     = false;
            var matchesLvl    = false;
            var matchesGender = false;
            var matchesIVList = false;

            for (var i = 0; i < subscriptions.Count; i++)
            {
                //var start = DateTime.Now;
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers[user.GuildId].Subscriptions.Enabled)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    try
                    {
                        member = await client.GetMemberById(user.GuildId, user.UserId);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"FAILED TO GET MEMBER BY ID {user.UserId}");
                        _logger.Error(ex);
                        continue;
                    }

                    if (member?.Roles == null)
                    {
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Instance.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Debug($"User {member?.Username} ({user.UserId}) is not a supporter, skipping pokemon {pokemon.Name}...");
                        // Automatically disable users subscriptions if not supporter to prevent issues
                        //user.Enabled = false;
                        //user.Save(false);
                        continue;
                    }

                    var form = Translator.Instance.GetFormName(pkmn.FormId);
                    subscribedPokemon = user.Pokemon.FirstOrDefault(x =>
                                                                    x.PokemonId == pkmn.Id &&
                                                                    (string.IsNullOrEmpty(x.Form) || (!string.IsNullOrEmpty(x.Form) && string.Compare(x.Form, form, true) == 0))
                                                                    );
                    // Not subscribed to Pokemon
                    if (subscribedPokemon == null)
                    {
                        //_logger.Debug($"User {member.Username} not subscribed to Pokemon {pokemon.Name} (Form: {form}).");
                        continue;
                    }

                    matchesIV = Filters.MatchesIV(pkmn.IV, subscribedPokemon.MinimumIV);
                    //var matchesCP = _whm.Filters.MatchesCpFilter(pkmn.CP, subscribedPokemon.MinimumCP);
                    matchesLvl    = Filters.MatchesLvl(pkmn.Level, (uint)subscribedPokemon.MinimumLevel, (uint)subscribedPokemon.MaximumLevel);
                    matchesGender = Filters.MatchesGender(pkmn.Gender, subscribedPokemon.Gender);
                    matchesIVList = subscribedPokemon.IVList?.Contains($"{pkmn.Attack}/{pkmn.Defense}/{pkmn.Stamina}") ?? false;

                    if (!(
                            (!subscribedPokemon.HasStats && matchesIV && matchesLvl && matchesGender) ||
                            (subscribedPokemon.HasStats && matchesIVList)
                            ))
                    {
                        continue;
                    }

                    var geofence = GetGeofence(user.GuildId);
                    if (geofence == null)
                    {
                        //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                        continue;
                    }

                    var distanceMatches = user.DistanceM > 0 && user.DistanceM > new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(pkmn.Latitude, pkmn.Longitude));
                    var geofenceMatches = subscribedPokemon.Areas.Select(x => x.ToLower()).Contains(geofence.Name.ToLower());

                    // If set distance does not match and no geofences match, then skip Pokemon...
                    if (!distanceMatches && !geofenceMatches)
                    {
                        continue;
                    }

                    var embed = pkmn.GeneratePokemonMessage(user.GuildId, client, _whConfig.Instance, null, geofence.Name);
                    //var end = DateTime.Now.Subtract(start);
                    //_logger.Debug($"Took {end} to process Pokemon subscription for user {user.UserId}");
                    embed.Embeds.ForEach(x => _queue.Enqueue(new NotificationItem(user, member, x, pokemon.Name, geofence.Name, pkmn)));

                    Statistics.Instance.SubscriptionPokemonSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            member        = null;
            user          = null;
            pokemon       = null;

            await Task.CompletedTask;
        }
        public async Task ProcessPvPSubscription(PokemonData pkmn)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(pkmn.Id))
            {
                return;
            }

            var loc = _whm.GetGeofence(pkmn.Latitude, pkmn.Longitude);

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                return;
            }

            var subscriptions = Manager.GetUserSubscriptionsByPvPPokemonId(pkmn.Id);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject user;
            PvPSubscription    subscribedPokemon;
            DiscordMember      member = null;
            var pokemon      = MasterFile.GetPokemon(pkmn.Id, pkmn.FormId);
            var matchesGreat = false;
            var matchesUltra = false;

            for (var i = 0; i < subscriptions.Count; i++)
            {
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Servers[user.GuildId].EnableSubscriptions)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    try
                    {
                        member = await client.GetMemberById(user.GuildId, user.UserId);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"FAILED TO GET MEMBER BY ID {user.UserId}");
                        _logger.Error(ex);
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Debug($"User {member?.Username} ({user.UserId}) is not a supporter, skipping pvp pokemon {pokemon.Name}...");
                        continue;
                    }

                    if (member?.Roles == null || loc == null)
                    {
                        continue;
                    }

                    // If member does not have role associated with city and server does have city roles configured, skip subscription.
                    if (!member.Roles.Select(x => x?.Name?.ToLower()).Contains(loc?.Name?.ToLower()) && _whConfig.Servers[user.GuildId].CityRoles?.Count > 0)
                    {
                        //_logger.Info($"User {member.Username} does not have city role {loc.Name}, skipping pokemon {pokemon.Name}.");
                        continue;
                    }

                    // Only check distance if user has it set
                    if (user.DistanceM > 0)
                    {
                        var distance = new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(pkmn.Latitude, pkmn.Longitude));
                        if (user.DistanceM < distance)
                        {
                            //Skip if distance is set and is not with specified distance.
                            _logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for PvP Pokemon {pokemon.Name}, Pokemon is farther than set distance of '{user.DistanceM} meters.");
                            continue;
                        }
                    }

                    var form = Translator.Instance.GetFormName(pkmn.FormId);
                    subscribedPokemon = user.PvP.FirstOrDefault(x =>
                                                                x.PokemonId == pkmn.Id &&
                                                                (string.IsNullOrEmpty(x.Form) || string.Compare(x.Form, form, true) == 0) &&
                                                                (string.IsNullOrEmpty(x.City) || (!string.IsNullOrEmpty(x.City) && string.Compare(loc.Name, x.City, true) == 0))
                                                                );
                    if (subscribedPokemon == null)
                    {
                        //_logger.Info($"User {member.Username} not subscribed to PvP Pokemon {pokemon.Name} (Form: {form}).");
                        continue;
                    }

                    matchesGreat = pkmn.GreatLeague?.Exists(x => subscribedPokemon.League == PvPLeague.Great &&
                                                            (x.CP ?? 0) >= 2400 && (x.CP ?? 0) <= 2500 &&
                                                            (x.Rank ?? 4096) <= subscribedPokemon.MinimumRank &&
                                                            (x.Percentage ?? 0) >= subscribedPokemon.MinimumPercent) ?? false;
                    matchesUltra = pkmn.GreatLeague?.Exists(x => subscribedPokemon.League == PvPLeague.Ultra &&
                                                            (x.CP ?? 0) >= 2400 && (x.CP ?? 0) <= 2500 &&
                                                            (x.Rank ?? 4096) <= subscribedPokemon.MinimumRank &&
                                                            (x.Percentage ?? 0) >= subscribedPokemon.MinimumPercent) ?? false;

                    if (!(matchesGreat || matchesUltra))
                    {
                        continue;
                    }

                    var embed = await pkmn.GeneratePokemonMessage(user.GuildId, client, _whConfig, null, loc.Name);

                    foreach (var emb in embed.Embeds)
                    {
                        _queue.Enqueue(new NotificationItem(user, member, emb, pokemon.Name, loc.Name));
                    }

                    Statistics.Instance.SubscriptionPokemonSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            member        = null;
            user          = null;
            loc           = null;
            pokemon       = null;

            await Task.CompletedTask;
        }
        public async Task ProcessGymSubscription(RaidData raid)
        {
            //if (!MasterFile.Instance.Pokedex.ContainsKey(raid.PokemonId))
            //    return;

            // Cache the result per-guild so that geospatial stuff isn't queried for every single subscription below
            Dictionary <ulong, GeofenceItem> locationCache = new Dictionary <ulong, GeofenceItem>();

            GeofenceItem GetGeofence(ulong guildId)
            {
                if (!locationCache.TryGetValue(guildId, out var geofence))
                {
                    geofence = _whm.GetGeofence(guildId, raid.Latitude, raid.Longitude);
                    locationCache.Add(guildId, geofence);
                }

                return(geofence);
            }

            var subscriptions = Manager.GetUserSubscriptionsByGymName(raid.GymName);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject user;
            var pokemon = MasterFile.GetPokemon(raid.PokemonId, raid.Form);

            for (int i = 0; i < subscriptions.Count; i++)
            {
                //var start = DateTime.Now;
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers[user.GuildId].Subscriptions.Enabled)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    var member = await client.GetMemberById(user.GuildId, user.UserId);

                    if (member == null)
                    {
                        _logger.Warn($"Failed to find member with id {user.UserId}.");
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Instance.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Info($"User {user.UserId} is not a supporter, skipping raid boss {pokemon.Name} for gym {raid.GymName}...");
                        // Automatically disable users subscriptions if not supporter to prevent issues
                        //user.Enabled = false;
                        //user.Save(false);
                        continue;
                    }

                    var geofence = GetGeofence(user.GuildId);
                    if (geofence == null)
                    {
                        //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                        continue;
                    }

                    var gymSub = user.Gyms.FirstOrDefault(x => string.Compare(x.Name, raid.GymName, true) == 0);
                    if (gymSub == null)
                    {
                        continue;
                    }

                    var checkLevel      = gymSub.MinimumLevel > 0 && gymSub.MaximumLevel > 0;
                    var containsPokemon = gymSub.PokemonIDs?.Contains((uint)raid.PokemonId) ?? false;
                    if (!checkLevel && !containsPokemon)
                    {
                        continue;
                    }

                    var embed = raid.GenerateRaidMessage(user.GuildId, client, _whConfig.Instance, null, geofence.Name);
                    //var end = DateTime.Now;
                    //_logger.Debug($"Took {end} to process gym raid subscription for user {user.UserId}");
                    embed.Embeds.ForEach(x => _queue.Enqueue(new NotificationItem(user, member, x, pokemon.Name, geofence.Name)));

                    Statistics.Instance.SubscriptionRaidsSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            user          = null;

            await Task.CompletedTask;
        }
        public async Task ProcessRaidSubscription(RaidData raid)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(raid.PokemonId))
            {
                return;
            }

            // Cache the result per-guild so that geospatial stuff isn't queried for every single subscription below
            Dictionary <ulong, GeofenceItem> locationCache = new Dictionary <ulong, GeofenceItem>();

            GeofenceItem GetGeofence(ulong guildId)
            {
                if (!locationCache.TryGetValue(guildId, out var geofence))
                {
                    geofence = _whm.GetGeofence(guildId, raid.Latitude, raid.Longitude);
                    locationCache.Add(guildId, geofence);
                }

                return(geofence);
            }

            var subscriptions = Manager.GetUserSubscriptionsByRaidBossId(raid.PokemonId);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject user;
            var pokemon = MasterFile.GetPokemon(raid.PokemonId, raid.Form);

            for (int i = 0; i < subscriptions.Count; i++)
            {
                //var start = DateTime.Now;
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Instance.Servers[user.GuildId].Subscriptions.Enabled)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    var member = await client.GetMemberById(user.GuildId, user.UserId);

                    if (member == null)
                    {
                        _logger.Warn($"Failed to find member with id {user.UserId}.");
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Instance.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Info($"User {user.UserId} is not a supporter, skipping raid boss {pokemon.Name}...");
                        // Automatically disable users subscriptions if not supporter to prevent issues
                        //user.Enabled = false;
                        //user.Save(false);
                        continue;
                    }

                    var form    = Translator.Instance.GetFormName(raid.Form);
                    var subPkmn = user.Raids.FirstOrDefault(x =>
                                                            x.PokemonId == raid.PokemonId &&
                                                            (string.IsNullOrEmpty(x.Form) || (!string.IsNullOrEmpty(x.Form) && string.Compare(x.Form, form, true) == 0))
                                                            );
                    // Not subscribed to Pokemon
                    if (subPkmn == null)
                    {
                        //_logger.Debug($"Skipping notification for user {member.DisplayName} ({member.Id}) for raid boss {pokemon.Name}, raid is in city '{loc.Name}'.");
                        continue;
                    }

                    var geofence = GetGeofence(user.GuildId);
                    if (geofence == null)
                    {
                        //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                        continue;
                    }

                    var distanceMatches = user.DistanceM > 0 && user.DistanceM > new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(raid.Latitude, raid.Longitude));
                    var geofenceMatches = subPkmn.Areas.Select(x => x.ToLower()).Contains(geofence.Name.ToLower());

                    // If set distance does not match and no geofences match, then skip Pokemon...
                    if (!distanceMatches && !geofenceMatches)
                    {
                        continue;
                    }

                    var embed = raid.GenerateRaidMessage(user.GuildId, client, _whConfig.Instance, null, geofence.Name);
                    //var end = DateTime.Now;
                    //_logger.Debug($"Took {end} to process raid subscription for user {user.UserId}");
                    embed.Embeds.ForEach(x => _queue.Enqueue(new NotificationItem(user, member, x, pokemon.Name, geofence.Name)));

                    Statistics.Instance.SubscriptionRaidsSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            user          = null;

            await Task.CompletedTask;
        }
        public async Task ProcessPvPSubscription(PokemonData pkmn)
        {
            if (!MasterFile.Instance.Pokedex.ContainsKey(pkmn.Id))
            {
                return;
            }

            var loc = _whm.GetGeofence(pkmn.Latitude, pkmn.Longitude);

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pkmn.Latitude},{pkmn.Longitude} {db.Pokemon[pkmn.Id].Name} {pkmn.IV}, skipping...");
                return;
            }

            var subscriptions = Manager.GetUserSubscriptionsByPvPPokemonId(pkmn.Id);

            if (subscriptions == null)
            {
                _logger.Warn($"Failed to get subscriptions from database table.");
                return;
            }

            SubscriptionObject user;
            PvPSubscription    subscribedPokemon;
            DiscordMember      member = null;
            var pokemon      = MasterFile.GetPokemon(pkmn.Id, pkmn.FormId);
            var matchesGreat = false;
            var matchesUltra = false;

            for (var i = 0; i < subscriptions.Count; i++)
            {
                try
                {
                    user = subscriptions[i];
                    if (user == null)
                    {
                        continue;
                    }

                    if (!user.Enabled)
                    {
                        continue;
                    }

                    if (!_whConfig.Servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    if (!_whConfig.Servers[user.GuildId].Subscriptions.Enabled)
                    {
                        continue;
                    }

                    if (!_servers.ContainsKey(user.GuildId))
                    {
                        continue;
                    }

                    var client = _servers[user.GuildId];

                    try
                    {
                        member = await client.GetMemberById(user.GuildId, user.UserId);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"FAILED TO GET MEMBER BY ID {user.UserId}");
                        _logger.Error(ex);
                        continue;
                    }

                    if (member?.Roles == null || loc == null)
                    {
                        continue;
                    }

                    if (!member.HasSupporterRole(_whConfig.Servers[user.GuildId].DonorRoleIds))
                    {
                        _logger.Debug($"User {member?.Username} ({user.UserId}) is not a supporter, skipping pvp pokemon {pokemon.Name}...");
                        // Automatically disable users subscriptions if not supporter to prevent issues
                        user.Enabled = false;
                        user.Save(false);
                        continue;
                    }

                    var form = Translator.Instance.GetFormName(pkmn.FormId);
                    subscribedPokemon = user.PvP.FirstOrDefault(x =>
                                                                x.PokemonId == pkmn.Id &&
                                                                (string.IsNullOrEmpty(x.Form) || (!string.IsNullOrEmpty(x.Form) && string.Compare(x.Form, form, true) == 0))
                                                                );
                    // Not subscribed to Pokemon
                    if (subscribedPokemon == null)
                    {
                        //_logger.Info($"User {member.Username} not subscribed to PvP Pokemon {pokemon.Name} (Form: {form}).");
                        continue;
                    }

                    matchesGreat = pkmn.GreatLeague != null && (pkmn.GreatLeague?.Exists(x => subscribedPokemon.League == PvPLeague.Great &&
                                                                                         (x.CP ?? 0) >= Strings.MinimumGreatLeagueCP && (x.CP ?? 0) <= Strings.MaximumGreatLeagueCP &&
                                                                                         (x.Rank ?? 4096) <= subscribedPokemon.MinimumRank &&
                                                                                         (x.Percentage ?? 0) * 100 >= subscribedPokemon.MinimumPercent) ?? false);
                    matchesUltra = pkmn.UltraLeague != null && (pkmn.UltraLeague?.Exists(x => subscribedPokemon.League == PvPLeague.Ultra &&
                                                                                         (x.CP ?? 0) >= Strings.MinimumUltraLeagueCP && (x.CP ?? 0) <= Strings.MaximumUltraLeagueCP &&
                                                                                         (x.Rank ?? 4096) <= subscribedPokemon.MinimumRank &&
                                                                                         (x.Percentage ?? 0) * 100 >= subscribedPokemon.MinimumPercent) ?? false);

                    // Check if Pokemon IV stats match any relevant great or ultra league ranks, if not skip.
                    if (!matchesGreat && !matchesUltra)
                    {
                        continue;
                    }

                    var distanceMatches = user.DistanceM > 0 && user.DistanceM > new Coordinates(user.Latitude, user.Longitude).DistanceTo(new Coordinates(pkmn.Latitude, pkmn.Longitude));
                    var geofenceMatches = subscribedPokemon.Areas.Select(x => x.ToLower()).Contains(loc.Name.ToLower());

                    // If set distance does not match and no geofences match, then skip Pokemon...
                    if (!distanceMatches && !geofenceMatches)
                    {
                        continue;
                    }

                    var embed = await pkmn.GeneratePokemonMessage(user.GuildId, client, _whConfig, null, loc.Name);

                    foreach (var emb in embed.Embeds)
                    {
                        _queue.Enqueue(new NotificationItem(user, member, emb, pokemon.Name, loc.Name));
                    }

                    Statistics.Instance.SubscriptionPokemonSent++;
                    Thread.Sleep(5);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }

            subscriptions.Clear();
            subscriptions = null;
            member        = null;
            user          = null;
            loc           = null;
            pokemon       = null;

            await Task.CompletedTask;
        }
Exemple #13
0
        public async Task StatsAsync(CommandContext ctx,
            [Description("")] string pokemon,
            [Description("")] string start,
            [Description("")] string end)
        {
            var pokeId = pokemon.PokemonIdFromName();
            if (pokeId == 0)
            {
                // TODO: Localize
                await ctx.RespondEmbed($"{ctx.User.Username} Unable to find Pokemon by name or id {pokemon}");
                return;
            }

            // TODO: Parse and validate start/end

            var sql = @"
            SELECT
              COUNT(id) AS total,
              SUM(iv > 0) AS with_iv,
              SUM(iv IS NULL) AS without_iv,
              SUM(iv = 0) AS iv_0,
              SUM(iv >= 1 AND iv < 10) AS iv_1_9,
              SUM(iv >= 10 AND iv < 20) AS iv_10_19,
              SUM(iv >= 20 AND iv < 30) AS iv_20_29,
              SUM(iv >= 30 AND iv < 40) AS iv_30_39,
              SUM(iv >= 40 AND iv < 50) AS iv_40_49,
              SUM(iv >= 50 AND iv < 60) AS iv_50_59,
              SUM(iv >= 60 AND iv < 70) AS iv_60_69,
              SUM(iv >= 70 AND iv < 80) AS iv_70_79,
              SUM(iv >= 80 AND iv < 90) AS iv_80_89,
              SUM(iv >= 90 AND iv < 100) AS iv_90_99,
              SUM(iv = 100) AS iv_100,
              SUM(gender = 1) AS male,
              SUM(gender = 2) AS female,
              SUM(gender = 3) AS genderless,
              SUM(level >= 1 AND level <= 9) AS level_1_9,
              SUM(level >= 10 AND level <= 19) AS level_10_19,
              SUM(level >= 20 AND level <= 29) AS level_20_29,
              SUM(level >= 30 AND level <= 35) AS level_30_35
            FROM
              pokemon
            WHERE
              pokemon_id = @pokemonId
              AND first_seen_timestamp >= @start
              AND first_seen_timestamp <= @end
            ";
            var dict = new Dictionary<string, object>
            {
                { "pokemonId", pokemon },
                { "start", start },
                { "end", end }
            };
            var results = ExecuteQuery<object>(sql, dict);
            if (results.Count > 0)
            {
                var result = (dynamic)results.FirstOrDefault();
                Console.WriteLine(result);
                int total = Convert.ToInt32(result.total ?? 0);
                int withIV = Convert.ToInt32(result.with_iv ?? 0);
                int withoutIV = Convert.ToInt32(result.without_iv ?? 0);
                int iv0 = Convert.ToInt32(result.iv_0 ?? 0);
                int iv1_9 = Convert.ToInt32(result.iv_1_9 ?? 0);
                int iv10_19 = Convert.ToInt32(result.iv_10_19 ?? 0);
                int iv20_29 = Convert.ToInt32(result.iv_20_29 ?? 0);
                int iv30_39 = Convert.ToInt32(result.iv_30_39 ?? 0);
                int iv40_49 = Convert.ToInt32(result.iv_40_49 ?? 0);
                int iv50_59 = Convert.ToInt32(result.iv_50_59 ?? 0);
                int iv60_69 = Convert.ToInt32(result.iv_60_69 ?? 0);
                int iv70_79 = Convert.ToInt32(result.iv_70_79 ?? 0);
                int iv80_89 = Convert.ToInt32(result.iv_80_89 ?? 0);
                int iv90_99 = Convert.ToInt32(result.iv_90_99 ?? 0);
                int iv100 = Convert.ToInt32(result.iv_100 ?? 0);
                int male = Convert.ToInt32(result.male ?? 0);
                int female = Convert.ToInt32(result.female ?? 0);
                int genderless = Convert.ToInt32(result.genderless ?? 0);
                int level1_9 = Convert.ToInt32(result.level_1_9 ?? 0);
                int level10_19 = Convert.ToInt32(result.level_10_19 ?? 0);
                int level20_29 = Convert.ToInt32(result.level_20_29 ?? 0);
                int level30_35 = Convert.ToInt32(result.level_30_35 ?? 0);

                var pkmn = MasterFile.GetPokemon(pokeId, 0);
                var eb = new DiscordEmbedBuilder
                {
                    Color = DiscordColor.Blurple,
                    Title = $"{ctx.Guild.Name} Community Day Stats",
                    Description = $"**{pkmn.Name}** ({pokeId})\r\nBetween: {start} - {end}",
                    Footer = new DiscordEmbedBuilder.EmbedFooter
                    {
                        Text = $"{ctx.Guild?.Name} | {DateTime.Now}",
                        IconUrl = ctx.Guild?.IconUrl
                    }
                };
                // TODO: Localize
                eb.AddField("Total", total.ToString("N0"), true);
                eb.AddField("With IV", withIV.ToString("N0"), true);
                eb.AddField("Without IV", withoutIV.ToString("N0"), true);

                eb.AddField("0% IV", iv0.ToString("N0"), true);
                eb.AddField("1-9% IV", iv1_9.ToString("N0"), true);
                eb.AddField("10-19% IV", iv10_19.ToString("N0"), true);
                eb.AddField("20-29% IV", iv20_29.ToString("N0"), true);
                eb.AddField("30-39% IV", iv30_39.ToString("N0"), true);
                eb.AddField("40-49% IV", iv40_49.ToString("N0"), true);
                eb.AddField("50-59% IV", iv50_59.ToString("N0"), true);
                eb.AddField("60-69% IV", iv60_69.ToString("N0"), true);
                eb.AddField("70-79% IV", iv70_79.ToString("N0"), true);
                eb.AddField("80-89% IV", iv80_89.ToString("N0"), true);
                eb.AddField("90-99% IV", iv90_99.ToString("N0"), true);
                eb.AddField("100 % IV", iv100.ToString("N0"), true);

                eb.AddField("Male", male.ToString("N0"), true);
                eb.AddField("Female", female.ToString("N0"), true);
                eb.AddField("Genderless", genderless.ToString("N0"), true);

                eb.AddField("Level 1-9", level1_9.ToString("N0"), true);
                eb.AddField("Level 10-19", level10_19.ToString("N0"), true);
                eb.AddField("Level 20-29", level20_29.ToString("N0"), true);
                eb.AddField("Level 30-35", level30_35.ToString("N0"), true);
                await ctx.RespondAsync(embed: eb);
            }
        }
Exemple #14
0
        public async Task ListNestsAsync(CommandContext ctx, string pokemon = null)
        {
            var guildId = ctx.Guild?.Id ?? ctx.Client.Guilds.Keys.FirstOrDefault(x => _dep.WhConfig.Servers.ContainsKey(x));

            var pokeId = pokemon.PokemonIdFromName();

            if (pokeId == 0)
            {
                await ctx.RespondEmbed(Translator.Instance.Translate("NOTIFY_INVALID_POKEMON_ID_OR_NAME").FormatText(ctx.User.Username, pokemon), DiscordColor.Red);

                return;
            }

            var pkmn = MasterFile.GetPokemon(pokeId, 0);
            var eb   = new DiscordEmbedBuilder
            {
                Title  = $"Local {pkmn.Name} Nests",
                Color  = DiscordColor.Blurple,
                Footer = new DiscordEmbedBuilder.EmbedFooter
                {
                    Text    = $"{ctx.Guild?.Name} | {DateTime.Now}",
                    IconUrl = ctx.Guild?.IconUrl
                }
            };

            var nests = GetNestsByPokemon(_dep.WhConfig.Database.Nests.ToString())?.Where(x => x.Key == pokeId);

            if (nests == null)
            {
                await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NESTS_LIST").FormatText(ctx.User.Username), DiscordColor.Red);

                return;
            }

            var cities       = _dep.WhConfig.Servers[guildId].CityRoles.Select(x => x.ToLower()).ToList();
            var groupedNests = GroupNests(nests);

            foreach (var nest in groupedNests)
            {
                var sb = new StringBuilder();
                foreach (var gn in nest.Value)
                {
                    if (gn.Average == 0)
                    {
                        continue;
                    }

                    var geofence = _dep.Whm.GetGeofence(gn.Latitude, gn.Longitude);
                    if (!cities.Contains(geofence?.Name))
                    {
                        continue;
                    }

                    sb.AppendLine($"[{gn.Name}]({string.Format(Strings.GoogleMaps, gn.Latitude, gn.Longitude)}) Avg/h: {gn.Average:N0}");
                }
                eb.AddField($"{nest.Key}", sb.ToString(), true);
            }
            if (eb.Fields.Count == 0)
            {
                eb.Description = $"{ctx.User.Username} No local nests found for `{pkmn.Name}`.";
                eb.Color       = DiscordColor.Yellow;
            }

            await ctx.RespondAsync(embed : eb);
        }