Skip to content

Commit

Permalink
Require geofence files to be specified in Discord server config inste…
Browse files Browse the repository at this point in the history
…ad of loading implicitly (#105)

* Require Geofence files to be explicitly listed in each Discord server (under new "geofences" property, similar to alarms) instead of loading all files implicitly

* Add method to retrieve all geofences for a particular server (for another contributor's PR later)

* Add missing lock-blocks for access to _geofences cache

* Make a couple example geofence files use a json extension to show off GeoJSON support

* Allow alarms to specify geofence files that are not specified for any servers

* To ensure only one instance of the WhConfig class is alive anywhere, use an object holder to keep track of that instance and allow it to be swapped out when the config is reloaded

* Store geofences on DiscordServerConfig class and use them for area-accepting commands when EnableCities is false

* Probably should just use lock() here

* Fix a merge issue

* Update SubscriptionManager.cs

* Update Bot.cs

Co-authored-by: versx <versx@ver.sx>
Co-authored-by: versx <versefx@gmail.com>
  • Loading branch information
3 people committed Dec 14, 2020
1 parent 4e129c2 commit c1047c6
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 230 deletions.
8 changes: 8 additions & 0 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"moderatorRoleIds": [],
"token": "<DISCORD_BOT_TOKEN>",
"alarms": "alarms.json",
"geofences": [
"City1.txt",
"City2.json"
],
"subscriptions": {
"enabled": false,
"maxPokemonSubscriptions": 0,
Expand Down Expand Up @@ -94,6 +98,10 @@
"moderatorRoleIds": [],
"token": "<DISCORD_BOT_TOKEN>",
"alarms": "alarms2.json",
"geofences": [
"City3.txt",
"City4.json"
],
"subscriptions": {
"enabled": false,
"maxPokemonSubscriptions": 0,
Expand Down
82 changes: 41 additions & 41 deletions src/Bot.cs

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/Commands/Dependencies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,26 @@

public class Dependencies
{
private readonly WhConfigHolder _configHolder;

public InteractivityModule Interactivity;

public WebhookController Whm { get; }

public SubscriptionProcessor SubscriptionProcessor { get; }

public WhConfig WhConfig { get; }
public WhConfig WhConfig => _configHolder.Instance;

public StripeService Stripe { get; }

public OsmManager OsmManager { get; }

public Dependencies(InteractivityModule interactivity, WebhookController whm, SubscriptionProcessor subProcessor, WhConfig whConfig, StripeService stripe)
public Dependencies(InteractivityModule interactivity, WebhookController whm, SubscriptionProcessor subProcessor, WhConfigHolder whConfig, StripeService stripe)
{
Interactivity = interactivity;
Whm = whm;
SubscriptionProcessor = subProcessor;
WhConfig = whConfig;
_configHolder = whConfig;
Stripe = stripe;
OsmManager = new OsmManager();
}
Expand Down
11 changes: 8 additions & 3 deletions src/Commands/Input/SubscriptionInput.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace WhMgr.Commands.Input
using WhMgr.Configuration;

namespace WhMgr.Commands.Input
{
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -35,13 +37,16 @@ public async Task<PokemonValidation> GetPokemonResult()
return validation;
}

public async Task<List<string>> GetAreasResult(List<string> validAreas)
public async Task<List<string>> GetAreasResult(ulong guildId)
{
var deps = _context.Dependencies.GetDependency<Dependencies>();
var server = deps.WhConfig.Servers[guildId];
var validAreas = server.EnableCities ? server.CityRoles : server.Geofences.Select(g => g.Name).ToList();
var message = (await _context.RespondEmbed($"Enter the areas to get notifications from separated by a comma (i.e. `city1,city2`):\n**Available Areas:**\n{string.Join("\n- ", validAreas)}\n- All", DiscordColor.Blurple)).FirstOrDefault();
var cities = await _context.WaitForUserChoice(true);

// Check if gender is a valid gender provided
var areas = SubscriptionAreas.GetAreas(cities, validAreas);
var areas = SubscriptionAreas.GetAreas(server, cities);
if (areas.Count == 0)
{
// No valid areas provided
Expand Down
6 changes: 3 additions & 3 deletions src/Commands/Nests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public async Task PostNestsAsync(CommandContext ctx,
try
{
var eb = GenerateNestMessage(guildId, ctx.Client, nest);
var geofence = _dep.Whm.GetGeofence(nest.Latitude, nest.Longitude);
var geofence = _dep.Whm.GetGeofence(guildId, nest.Latitude, nest.Longitude);
if (geofence == null)
{
//_logger.Warn($"Failed to find geofence for nest {nest.Key}.");
Expand Down Expand Up @@ -195,7 +195,7 @@ public IReadOnlyDictionary<string, string> GetProperties(DiscordGuild guild, Nes
var wazeMapsLink = string.Format(Strings.WazeMaps, nest.Latitude, nest.Longitude);
var scannerMapsLink = string.Format(_dep.WhConfig.Urls.ScannerMap, nest.Latitude, nest.Longitude);
var staticMapLink = StaticMap.GetUrl(_dep.WhConfig.Urls.StaticMap, _dep.WhConfig.StaticMaps["nests"], nest.Latitude, nest.Longitude, pkmnImage, Net.Models.PokemonTeam.All, _dep.OsmManager.GetNest(nest.Name)?.FirstOrDefault());
var geofence = _dep.Whm.GetGeofence(nest.Latitude, nest.Longitude);
var geofence = _dep.Whm.GetGeofence(guild.Id, nest.Latitude, nest.Longitude);
var city = geofence?.Name ?? "Unknown";
var address = new Location(null, city, nest.Latitude, nest.Longitude).GetAddress(_dep.WhConfig);

Expand Down Expand Up @@ -248,7 +248,7 @@ private Dictionary<string, List<Nest>> GroupNests(ulong guildId, IEnumerable<Nes
var dict = new Dictionary<string, List<Nest>>();
foreach (var nest in nests)
{
var geofence = _dep.Whm.GetGeofence(nest.Latitude, nest.Longitude);
var geofence = _dep.Whm.GetGeofence(guildId, nest.Latitude, nest.Longitude);
if (geofence == null)
{
_logger.Warn($"Failed to find geofence for nest {nest.Name}.");
Expand Down
47 changes: 25 additions & 22 deletions src/Commands/Notifications.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace WhMgr.Commands
using WhMgr.Configuration;

namespace WhMgr.Commands
{
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -378,7 +380,7 @@ public async Task PokeMeAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, server.CityRoles);
var areas = SubscriptionAreas.GetAreas(server, city);

// Loop through each valid pokemon entry provided
foreach (var (pokemonId, form) in validation.Valid)
Expand Down Expand Up @@ -536,7 +538,7 @@ public async Task PokeMeNotAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, _dep.WhConfig.Servers[guildId].CityRoles);
var areas = SubscriptionAreas.GetAreas(_dep.WhConfig.Servers[guildId], city);
var pokemonNames = validation.Valid.Select(x => MasterFile.Instance.Pokedex[x.Key].Name + (string.IsNullOrEmpty(x.Value) ? string.Empty : "-" + x.Value));
var error = false;
foreach (var (pokemonId, form) in validation.Valid)
Expand Down Expand Up @@ -619,7 +621,7 @@ public async Task RaidMeAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, server.CityRoles);
var areas = SubscriptionAreas.GetAreas(server, city);
foreach (var (pokemonId, form) in validation.Valid)
{
var subRaid = subscription.Raids.FirstOrDefault(x => x.PokemonId == pokemonId && string.Compare(x.Form, form, true) == 0);
Expand Down Expand Up @@ -709,7 +711,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NO_RAID_SUBSCRIPTION
return;
}

var areas = SubscriptionAreas.GetAreas(city, _dep.WhConfig.Servers[guildId].CityRoles);
var areas = SubscriptionAreas.GetAreas(_dep.WhConfig.Servers[guildId], city);
foreach (var item in validation.Valid)
{
var pokemonId = item.Key;
Expand Down Expand Up @@ -784,7 +786,7 @@ public async Task QuestMeAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, server.CityRoles);
var areas = SubscriptionAreas.GetAreas(server, city);
var subQuest = subscription.Quests.FirstOrDefault(x => string.Compare(x.RewardKeyword, rewardKeyword, true) == 0);
if (subQuest != null)
{
Expand Down Expand Up @@ -867,7 +869,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NO_QUEST_SUBSCRIPTIO
return;
}

var areas = SubscriptionAreas.GetAreas(city, _dep.WhConfig.Servers[guildId].CityRoles);
var areas = SubscriptionAreas.GetAreas(_dep.WhConfig.Servers[guildId], city);
var subQuest = subscription.Quests.FirstOrDefault(x => string.Compare(x.RewardKeyword, rewardKeyword, true) == 0);
// Check if subscribed
if (subQuest == null)
Expand Down Expand Up @@ -1024,7 +1026,7 @@ public async Task InvMeAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, server.CityRoles);
var areas = SubscriptionAreas.GetAreas(server, city);
foreach (var (pokemonId, form) in validation.Valid)
{
var subInvasion = subscription.Invasions.FirstOrDefault(x => x.RewardPokemonId == pokemonId);
Expand Down Expand Up @@ -1113,7 +1115,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("ERROR_NO_INVASION_SUBSCRIP
return;
}

var areas = SubscriptionAreas.GetAreas(city, _dep.WhConfig.Servers[guildId].CityRoles);
var areas = SubscriptionAreas.GetAreas(_dep.WhConfig.Servers[guildId], city);
foreach (var item in validation.Valid)
{
var pokemonId = item.Key;
Expand Down Expand Up @@ -1275,7 +1277,7 @@ public async Task PvpMeAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, server.CityRoles);
var areas = SubscriptionAreas.GetAreas(server, city);
foreach (var (pokemonId, form) in validation.Valid)
{
if (!MasterFile.Instance.Pokedex.ContainsKey(pokemonId))
Expand Down Expand Up @@ -1421,7 +1423,7 @@ public async Task PvpMeNotAsync(CommandContext ctx,
return;
}

var areas = SubscriptionAreas.GetAreas(city, _dep.WhConfig.Servers[guildId].CityRoles);
var areas = SubscriptionAreas.GetAreas(_dep.WhConfig.Servers[guildId], city);
await RemovePvPSubscription(ctx, subscription, validation, pvpLeague, areas);
_dep.SubscriptionProcessor.Manager.ReloadSubscriptions();
}
Expand Down Expand Up @@ -1466,7 +1468,7 @@ public async Task AddAsync(CommandContext ctx)
var ivResult = await pkmnInput.GetIVResult();
var lvlResult = await pkmnInput.GetLevelResult();
var genderResult = await pkmnInput.GetGenderResult();
var areasResult = await pkmnInput.GetAreasResult(_dep.WhConfig.Servers[guildId].CityRoles);
var areasResult = await pkmnInput.GetAreasResult(guildId);

var validPokemonNames = string.Join(", ", pkmnResult.Valid.Keys);
var result = await AddPokemonSubscription(ctx, subscription, pkmnResult, ivResult, lvlResult.MinimumLevel, lvlResult.MaximumLevel, genderResult, areasResult);
Expand Down Expand Up @@ -1525,7 +1527,7 @@ await ctx.RespondEmbed
var pvpLeague = await pvpInput.GetLeagueResult();
var pvpRank = await pvpInput.GetRankResult();
var pvpPercent = await pvpInput.GetPercentResult();
var pvpAreas = await pvpInput.GetAreasResult(server.CityRoles);
var pvpAreas = await pvpInput.GetAreasResult(guildId);

var validPokemonNames = string.Join(", ", pvpPokemon.Valid.Keys);
var pvpResult = await AddPvPSubscription(ctx, subscription, pvpPokemon, pvpLeague, pvpRank, pvpPercent, pvpAreas);
Expand Down Expand Up @@ -1573,7 +1575,7 @@ await ctx.RespondEmbed

var raidInput = new RaidSubscriptionInput(ctx);
var raidPokemon = await raidInput.GetPokemonResult();
var raidAreas = await raidInput.GetAreasResult(server.CityRoles);
var raidAreas = await raidInput.GetAreasResult(guildId);

var validPokemonNames = string.Join(", ", raidPokemon.Valid.Select(x => MasterFile.Instance.Pokedex[x.Key].Name + (string.IsNullOrEmpty(x.Value) ? string.Empty : "-" + x.Value)));
var raidResult = AddRaidSubscription(ctx, subscription, raidPokemon, raidAreas);
Expand Down Expand Up @@ -1620,7 +1622,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("SUCCESS_RAID_SUBSCRIPTIONS

var questInput = new QuestSubscriptionInput(ctx);
var rewardKeyword = await questInput.GetRewardInput();
var areas = await questInput.GetAreasResult(server.CityRoles);
var areas = await questInput.GetAreasResult(guildId);

var subQuest = subscription.Quests.FirstOrDefault(x => string.Compare(x.RewardKeyword, rewardKeyword, true) == 0);
if (subQuest != null)
Expand Down Expand Up @@ -1673,7 +1675,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("SUCCESS_QUEST_SUBSCRIPTION

var invasionInput = new InvasionSubscriptionInput(ctx);
var invasionPokemon = await invasionInput.GetPokemonResult();
var invasionAreas = await invasionInput.GetAreasResult(server.CityRoles);
var invasionAreas = await invasionInput.GetAreasResult(guildId);

var validPokemonNames = string.Join(", ", invasionPokemon.Valid.Select(x => MasterFile.Instance.Pokedex[x.Key].Name));
foreach (var (pokemonId, form) in invasionPokemon.Valid)
Expand Down Expand Up @@ -1998,7 +2000,7 @@ public async Task RemoveAsync(CommandContext ctx)

var pkmnInput = new PokemonSubscriptionInput(ctx);
var pkmnResult = await pkmnInput.GetPokemonResult();
var areasResult = await pkmnInput.GetAreasResult(_dep.WhConfig.Servers[guildId].CityRoles);
var areasResult = await pkmnInput.GetAreasResult(guildId);

await RemovePokemonSubscription(ctx, subscription, pkmnResult, areasResult);
break;
Expand All @@ -2016,7 +2018,7 @@ public async Task RemoveAsync(CommandContext ctx)
var pvpInput = new PvPSubscriptionInput(ctx);
var pvpPokemonResult = await pvpInput.GetPokemonResult();
var pvpLeagueResult = await pvpInput.GetLeagueResult();
var pvpAreasResult = await pvpInput.GetAreasResult(_dep.WhConfig.Servers[guildId].CityRoles);
var pvpAreasResult = await pvpInput.GetAreasResult(guildId);

await RemovePvPSubscription(ctx, subscription, pvpPokemonResult, pvpLeagueResult, pvpAreasResult);
}
Expand All @@ -2033,7 +2035,7 @@ public async Task RemoveAsync(CommandContext ctx)

var raidInput = new RaidSubscriptionInput(ctx);
var raidPokemonResult = await raidInput.GetPokemonResult();
var raidAreasResult = await raidInput.GetAreasResult(server.CityRoles);
var raidAreasResult = await raidInput.GetAreasResult(guildId);

await RemoveRaidSubscription(ctx, subscription, null, raidAreasResult);
}
Expand All @@ -2050,7 +2052,7 @@ public async Task RemoveAsync(CommandContext ctx)

var questInput = new QuestSubscriptionInput(ctx);
var rewardResult = await questInput.GetRewardInput();
var areasResult = await questInput.GetAreasResult(server.CityRoles);
var areasResult = await questInput.GetAreasResult(guildId);

var notSubscribed = new List<string>();
var unsubscribed = new List<string>();
Expand Down Expand Up @@ -2120,7 +2122,7 @@ await ctx.RespondEmbed(Translator.Instance.Translate("SUCCESS_QUEST_SUBSCRIPTION

var invasionInput = new InvasionSubscriptionInput(ctx);
var invasionPokemonResult = await invasionInput.GetPokemonResult();
var invasionAreasResult = await invasionInput.GetAreasResult(server.CityRoles);
var invasionAreasResult = await invasionInput.GetAreasResult(guildId);

foreach (var item in invasionPokemonResult.Valid)
{
Expand Down Expand Up @@ -2976,9 +2978,10 @@ private async Task<bool> CanExecute(CommandContext ctx)

internal class SubscriptionAreas
{
public static List<string> GetAreas(string city, List<string> validCities)
public static List<string> GetAreas(DiscordServerConfig server, string city)
{
// Parse user defined cities
var validCities = server.EnableCities ? server.CityRoles : server.Geofences.Select(g => g.Name).ToList();
var cities = string.IsNullOrEmpty(city) || string.Compare(city, Strings.All, true) == 0
? validCities
: city.Replace(" ,", ",").Replace(", ", ",").Split(',').ToList();
Expand Down
13 changes: 12 additions & 1 deletion src/Configuration/DiscordServerConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace WhMgr.Configuration
using WhMgr.Geofence;

namespace WhMgr.Configuration
{
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -58,6 +60,15 @@ public class DiscordServerConfig
/// </summary>
[JsonProperty("alarms")]
public string AlarmsFile { get; set; }

/// <summary>
/// Gets or sets the list of Geofence files to use for the Discord server (in addition to the common ones)
/// </summary>
[JsonProperty("geofences")]
public string[] GeofenceFiles { get; set; }

[JsonIgnore]
public List<GeofenceItem> Geofences { get; } = new List<GeofenceItem>();

/// <summary>
/// Gets or sets whether to enable custom direct message subscriptions
Expand Down
48 changes: 48 additions & 0 deletions src/Configuration/WhConfigHolder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;

namespace WhMgr.Configuration
{
/// <summary>
/// This class holds a singleton instance of WhConfig which can be swapped out (e.g. after a config reload) without everybody
/// needing to update their references to the config itself.
/// </summary>
public class WhConfigHolder
{
private readonly object _instanceMutex = new object();

private WhConfig _instance;

public WhConfigHolder(WhConfig instance)
{
_instance = instance;
}

/// <summary>
/// Fired after the config instance was swapped for a new one
/// </summary>
public event Action Reloaded;

/// <summary>
/// Provides thread-safe access to the internal WhConfig instance
/// </summary>
public WhConfig Instance
{
get
{
WhConfig value;

lock (_instanceMutex)
value = _instance;

return value;
}
set
{
lock (_instanceMutex)
_instance = value;

Reloaded?.Invoke();
}
}
}
}
Loading

0 comments on commit c1047c6

Please sign in to comment.