Skip to content

Commit

Permalink
Add support for multiple distance locations (#155)
Browse files Browse the repository at this point in the history
* Add support for multiple distance locations

* Fix migration

* Update 7.sql

* Update 7.sql

* Add per subscription location

* comments

* - Add support for handling enabling/disabling specific subscription types or all.
- Update migration 7.
  • Loading branch information
versx authored May 28, 2021
1 parent c80db0c commit ba1030f
Show file tree
Hide file tree
Showing 13 changed files with 351 additions and 118 deletions.
52 changes: 52 additions & 0 deletions migrations/7.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
ALTER TABLE `subscriptions`
DROP COLUMN `distance`;

ALTER TABLE `subscriptions`
DROP COLUMN `latitude`;

ALTER TABLE `subscriptions`
DROP COLUMN `longitude`;

ALTER TABLE `subscriptions`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `pokemon`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `pvp`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `raids`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `quests`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `invasions`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `lures`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

ALTER TABLE `gyms`
ADD COLUMN `location` varchar(32) DEFAULT NULL;

CREATE TABLE `locations` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`subscription_id` int(11) NOT NULL DEFAULT 0,
`guild_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL,
`name` varchar(32) NOT NULL,
`distance` int(11) DEFAULT 0,
`latitude` double DEFAULT 0,
`longitude` double DEFAULT 0,
PRIMARY KEY (`id`),
KEY `FK_location_subscriptions_subscription_id` (`subscription_id`),
CONSTRAINT `FK_location_subscriptions_subscription_id` FOREIGN KEY (`subscription_id`) REFERENCES `subscriptions` (`id`)
);

ALTER TABLE `subscriptions`
DROP COLUMN `enabled`;

ALTER TABLE `subscriptions`
ADD COLUMN `status` smallint(5) unsigned DEFAULT 0;
87 changes: 67 additions & 20 deletions src/Commands/Notifications.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public async Task EnableDisableAsync(CommandContext ctx,
Description("Set the distance and location you'd like to receive raid notifications.")
]
public async Task SetDistanceAsync(CommandContext ctx,
[Description("Name of the location you want to create i.e. Home, Work, etc")] string locationName,
[Description("Maximum distance in meters between the set coordinates.")] int distance,
[Description("Coordinates in `34.00,-117.00` format."), RemainingText] string coordinates)
{
Expand All @@ -154,11 +155,30 @@ public async Task SetDistanceAsync(CommandContext ctx,
return;
}

subscription.DistanceM = distance;
subscription.Latitude = lat;
subscription.Longitude = lng;
var location = subscription.Locations?.FirstOrDefault(x => string.Compare(x.Name, subscription.Location, true) == 0);
if (location == null)
{
location = new LocationSubscription
{
SubscriptionId = subscription.Id,
GuildId = subscription.GuildId,
UserId = subscription.UserId,
Name = locationName,
DistanceM = distance,
Latitude = lat,
Longitude = lng,
};
subscription.Locations.Add(location);
}
else
{
location.DistanceM = distance;
location.Latitude = lat;
location.Longitude = lng;
}
subscription.Save();

// TODO: Change response message to include new/modifed location name
await ctx.RespondEmbed(Translator.Instance.Translate("NOTIFY_DISTANCE_SET").FormatText(ctx.User.Username, distance, lat, lng));
_dep.SubscriptionProcessor.Manager.ReloadSubscriptions();
}
Expand Down Expand Up @@ -381,8 +401,9 @@ public async Task PokeMeAsync(CommandContext ctx,
}

var areas = SubscriptionAreas.GetAreas(server, city);
if (areas.Count == 0 && subscription.DistanceM == 0)
if (areas.Count == 0 && string.IsNullOrEmpty(subscription.Location))
{
// TODO: Provide better response
await ctx.RespondEmbed($"{ctx.User.Username}#{ctx.User.Discriminator} You must either set a distance to receive notifications from (`set-distance <meters> <latitude>,<longitude>`) or provide a city/area for the subscription. Aborting request.", DiscordColor.Red);
return;
}
Expand Down Expand Up @@ -442,7 +463,7 @@ public async Task PokeMeAsync(CommandContext ctx,
(!subPkmn.IVList.Contains($"{attack}/{defense}/{stamina}") && hasStatsSet) ||
// TODO: Check against cities
//(string.Compare(subPkmn.City, cities, true) != 0 && !ContainsCity(subPkmn.City, cities)))
!SubscriptionAreas.ContainsCity(subPkmn.Areas, areas))
!SubscriptionAreas.IsAreasEqual(subPkmn.Areas, areas))
{
subPkmn.Form = form;
subPkmn.MinimumIV = hasStatsSet ? subPkmn.MinimumIV : realIV;
Expand Down Expand Up @@ -627,7 +648,7 @@ public async Task RaidMeAsync(CommandContext ctx,
}

var areas = SubscriptionAreas.GetAreas(server, city);
if (areas.Count == 0 && subscription.DistanceM == 0)
if (areas.Count == 0 && string.IsNullOrEmpty(subscription.Location))
{
await ctx.RespondEmbed($"{ctx.User.Username}#{ctx.User.Discriminator} You must either set a distance to receive notifications from (`set-distance <meters> <latitude>,<longitude>`) or provide a city/area for the subscription. Aborting request.", DiscordColor.Red);
return;
Expand Down Expand Up @@ -800,7 +821,7 @@ public async Task QuestMeAsync(CommandContext ctx,
}

var areas = SubscriptionAreas.GetAreas(server, city);
if (areas.Count == 0 && subscription.DistanceM == 0)
if (areas.Count == 0 && string.IsNullOrEmpty(subscription.Location))
{
await ctx.RespondEmbed($"{ctx.User.Username}#{ctx.User.Discriminator} You must either set a distance to receive notifications from (`set-distance <meters> <latitude>,<longitude>`) or provide a city/area for the subscription. Aborting request.", DiscordColor.Red);
return;
Expand Down Expand Up @@ -1066,7 +1087,7 @@ public async Task InvMeAsync(CommandContext ctx,
}

var areas = SubscriptionAreas.GetAreas(server, city);
if (areas.Count == 0 && subscription.DistanceM == 0)
if (areas.Count == 0 && string.IsNullOrEmpty(subscription.Location))
{
await ctx.RespondEmbed($"{ctx.User.Username}#{ctx.User.Discriminator} You must either set a distance to receive notifications from (`set-distance <meters> <latitude>,<longitude>`) or provide a city/area for the subscription. Aborting request.", DiscordColor.Red);
return;
Expand Down Expand Up @@ -1323,7 +1344,7 @@ public async Task PvpMeAsync(CommandContext ctx,
}

var areas = SubscriptionAreas.GetAreas(server, city);
if (areas.Count == 0 && subscription.DistanceM == 0)
if (areas.Count == 0 && string.IsNullOrEmpty(subscription.Location))
{
await ctx.RespondEmbed($"{ctx.User.Username}#{ctx.User.Discriminator} You must either set a distance to receive notifications from (`set-distance <meters> <latitude>,<longitude>`) or provide a city/area for the subscription. Aborting request.", DiscordColor.Red);
return;
Expand Down Expand Up @@ -1363,7 +1384,7 @@ public async Task PvpMeAsync(CommandContext ctx,
//Exists, check if anything changed.
if (minimumRank != subPkmn.MinimumRank ||
minimumPercent != subPkmn.MinimumPercent ||
!SubscriptionAreas.ContainsCity(subPkmn.Areas, areas))
!SubscriptionAreas.IsAreasEqual(subPkmn.Areas, areas))
{
subPkmn.MinimumRank = minimumRank;
subPkmn.MinimumPercent = minimumPercent;
Expand Down Expand Up @@ -2034,7 +2055,7 @@ private async Task<KeyValuePair<List<string>, List<string>>> AddPokemonSubscript
maxLvl != subPkmn.MaximumLevel ||
gender != subPkmn.Gender ||
(!subPkmn.IVList.Contains($"{ivResult.Attack}/{ivResult.Defense}/{ivResult.Stamina}") && hasStatsSet) ||
!SubscriptionAreas.ContainsCity(subPkmn.Areas, areas))
!SubscriptionAreas.IsAreasEqual(subPkmn.Areas, areas))
{
subPkmn.Form = form;
subPkmn.MinimumIV = hasStatsSet ? subPkmn.MinimumIV : ivResult.IV;
Expand Down Expand Up @@ -2104,7 +2125,7 @@ private async Task<KeyValuePair<List<string>, List<string>>> AddPvPSubscription(
//Exists, check if anything changed.
if (minRank != subPkmn.MinimumRank ||
minPercent != subPkmn.MinimumPercent ||
!SubscriptionAreas.ContainsCity(subPkmn.Areas, areas))
!SubscriptionAreas.IsAreasEqual(subPkmn.Areas, areas))
{
subPkmn.MinimumRank = minRank;
subPkmn.MinimumPercent = minPercent;
Expand Down Expand Up @@ -2838,7 +2859,10 @@ private async Task EnableDisableUserSubscriptions(CommandContext ctx, DiscordUse
}

var cmd = ctx.Message.Content.TrimStart('.', ' ');
subscription.Enabled = cmd.ToLower().Contains("enable");
var isEnableCommand = cmd.ToLower().Contains("enable");
subscription.Status = isEnableCommand
? NotificationStatusType.All
: NotificationStatusType.None;
subscription.Save();

await ctx.TriggerTypingAsync();
Expand Down Expand Up @@ -2898,13 +2922,14 @@ private async Task<List<string>> BuildUserSubscriptionSettings(DiscordClient cli
return messages;
feeds.Sort();

var locationLink = $"[{subscription.Latitude},{subscription.Longitude}]({string.Format(Strings.GoogleMaps, subscription.Latitude, subscription.Longitude)})";
var activeLocation = subscription.Locations?.FirstOrDefault(x => string.Compare(x.Name, subscription.Location, true) == 0);
var locationLink = $"[{activeLocation.Latitude},{activeLocation.Longitude}]({string.Format(Strings.GoogleMaps, activeLocation.Latitude, activeLocation.Longitude)})";
var sb = new StringBuilder();
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_ENABLED").FormatText(subscription.Enabled ? "Yes" : "No"));
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_ENABLED").FormatText(subscription.Status.ToString()));
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_ICON_STYLE").FormatText(subscription.IconStyle));
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_DISTANCE").FormatText(subscription.DistanceM == 0 ?
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_DISTANCE").FormatText(activeLocation.DistanceM == 0 ?
Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_DISTANCE_NOT_SET") :
Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_DISTANCE_KM").FormatText(subscription.DistanceM.ToString("N0"), locationLink)));
Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_DISTANCE_KM").FormatText(activeLocation.DistanceM.ToString("N0"), locationLink)));
if (!string.IsNullOrEmpty(subscription.PhoneNumber))
{
sb.AppendLine(Translator.Instance.Translate("NOTIFY_SETTINGS_EMBED_PHONE_NUMBER").FormatText(subscription.PhoneNumber));
Expand Down Expand Up @@ -3034,6 +3059,8 @@ private async Task<List<string>> BuildUserSubscriptionSettings(DiscordClient cli
return messages;
}

#region Get Subscription Names

private List<string> GetPvPSubscriptionNames(ulong guildId, ulong userId)
{
var list = new List<string>();
Expand Down Expand Up @@ -3169,6 +3196,8 @@ private List<string> GetLureSubscriptionNames(ulong guildId, ulong userId)
return list;
}

#endregion

private DiscordEmbedBuilder BuildExpirationMessage(ulong guildId, DiscordUser user)
{
var customerData = _dep.Stripe.GetCustomerData(guildId, user.Id);
Expand Down Expand Up @@ -3251,14 +3280,22 @@ private static List<PokestopLureType> GetLures(string lureTypes)

internal class SubscriptionAreas
{
/// <summary>
/// Get parsed areas from command delimited string value
/// </summary>
/// <param name="server">Discord server to get valid areas from</param>
/// <param name="city">Comma delimited string value of areas</param>
/// <returns>Returns a <c>List<string></c> of valid areas from provided string value</returns>
public static List<string> GetAreas(DiscordServerConfig server, string city)
{
if (string.IsNullOrEmpty(city))
return new List<string>();

// Parse user defined cities
// Get list of valid cities for Discord server
var validCities = server.Geofences.Select(g => g.Name).ToList();
var cities = /*string.IsNullOrEmpty(city) ||*/ string.Compare(city, Strings.All, true) == 0
// If `all` is explicitly specified then include all valid cities,
// otherwise parse comma separated `city` parameter
var cities = string.Compare(city, Strings.All, true) == 0
? validCities
: city.RemoveSpaces();
var validAreas = validCities.Select(x => x.ToLower());
Expand All @@ -3268,17 +3305,27 @@ public static List<string> GetAreas(DiscordServerConfig server, string city)
.ToList();
}

public static bool ContainsCity(List<string> oldCities, List<string> newCities)
/// <summary>
/// Check if previously subscribed areas differ from newly subscribed areas or not
/// </summary>
/// <param name="oldCities">Previous areas to compare</param>
/// <param name="newCities">New areas to compare</param>
/// <returns>Returns <c>true</c> if areas are equal, otherwise returns <c>false</c>.</returns>
public static bool IsAreasEqual(List<string> oldCities, List<string> newCities)
{
var oldAreas = oldCities.Select(x => x.ToLower());
var newAreas = newCities.Select(x => x.ToLower());
// Check if old subscribed areas and new areas are the same
foreach (var newArea in newAreas)
{
// If old subscribed areas contains new area, skip
if (oldAreas.Contains(newArea))
continue;

// `oldAreas` differs from `newAreas`
return false;
}
// Subscribed areas are equal
return true;
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/Data/Subscriptions/Models/GymSubscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class GymSubscription : SubscriptionItem
[
JsonProperty("name"),
Alias("name"),
Unique,
//Unique,
]
public string Name { get; set; }

Expand All @@ -41,6 +41,12 @@ public class GymSubscription : SubscriptionItem
]
public List<uint> PokemonIDs { get; set; }

[
JsonProperty("location"),
Alias("location"),
]
public string Location { get; set; }

public GymSubscription()
{
PokemonIDs = new List<uint>();
Expand Down
14 changes: 9 additions & 5 deletions src/Data/Subscriptions/Models/InvasionSubscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

[
JsonObject("invasions"),
Alias("invasions")
Alias("invasions"),
]
public class InvasionSubscription : SubscriptionItem
{
[
Alias("subscription_id"),
ForeignKey(typeof(SubscriptionObject))
ForeignKey(typeof(SubscriptionObject)),
]
public int SubscriptionId { get; set; }

Expand All @@ -34,17 +34,21 @@ public class InvasionSubscription : SubscriptionItem
[
JsonProperty("reward_pokemon_id"),
Alias("reward_pokemon_id"),
//Required
]
public int RewardPokemonId { get; set; }

[
JsonProperty("city"),
Alias("city"),
Required
Alias("city"),
]
public List<string> Areas { get; set; }

[
JsonProperty("location"),
Alias("location"),
]
public string Location { get; set; }

public InvasionSubscription()
{
Areas = new List<string>();
Expand Down
51 changes: 51 additions & 0 deletions src/Data/Subscriptions/Models/LocationSubscription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace WhMgr.Data.Subscriptions.Models
{
using Newtonsoft.Json;
using ServiceStack.DataAnnotations;

[
JsonObject("locations"),
Alias("locations"),
]
public class LocationSubscription : SubscriptionItem
{
[
Alias("subscription_id"),
ForeignKey(typeof(SubscriptionObject)),
]
public int SubscriptionId { get; set; }

[
JsonProperty("name"),
Alias("name"),
]
public string Name { get; set; }

[
JsonProperty("distance"),
Alias("distance"),
Default(0),
]
public int DistanceM { get; set; }

/// <summary>
/// Gets or sets the latitude to use with distance checks
/// </summary>
[
JsonProperty("latitude"),
Alias("latitude"),
Default(0),
]
public double Longitude { get; set; }

/// <summary>
/// Gets or sets the longitude to use with distance checks
/// </summary>
[
JsonProperty("longitude"),
Alias("longitude"),
Default(0),
]
public double Latitude { get; set; }
}
}
Loading

0 comments on commit ba1030f

Please sign in to comment.