diff --git a/DragonFruit.Six.Api.Tests/Utils/ServiceTests.cs b/DragonFruit.Six.Api.Tests/Utils/ServiceTests.cs index 262345c4..94efb8f0 100644 --- a/DragonFruit.Six.Api.Tests/Utils/ServiceTests.cs +++ b/DragonFruit.Six.Api.Tests/Utils/ServiceTests.cs @@ -1,8 +1,9 @@ // Dragon6 API Copyright DragonFruit Network // Licensed under Apache-2. Refer to the LICENSE file for more info +using System.Linq; using System.Threading.Tasks; -using DragonFruit.Six.Api.Accounts; +using DragonFruit.Six.Api.Services.Geolocation; using DragonFruit.Six.Api.Services.Status; using NUnit.Framework; @@ -12,7 +13,25 @@ namespace DragonFruit.Six.Api.Tests.Utils public class ServiceTests : Dragon6ApiTest { [Test] - public async Task TestGeolocation() => await Client.GeolocateAsync(); + public async Task TestGeolocation() + { + // test the request works without throwing + // as we don't know who's running this, there's no real way to validate correctness of this part + await Client.GeolocateAsync().ConfigureAwait(false); + + var addressInfo = await Client.GeolocateAsync("8.8.8.8", "172.217.16.238", "194.39.167.145", "2620:119:35::35").ConfigureAwait(false); + var addressDirectory = addressInfo.ToDictionary(x => x.IP); + + // google services + Assert.AreEqual("Google", addressDirectory["8.8.8.8"].ServiceProvider); + Assert.AreEqual("Google Servers", addressDirectory["172.217.16.238"].ServiceProvider); + + // IPv6 DNS server + Assert.AreEqual("Cisco OpenDNS, LLC", addressDirectory["2620:119:35::35"].ServiceProvider); + + // random ip address from UK + Assert.AreEqual("United Kingdom", addressDirectory["194.39.167.145"].CountryName); + } [Test] public async Task TestServerStatus() => await Client.GetServerStatusAsync(); diff --git a/DragonFruit.Six.Api/Accounts/UbisoftAccountExtensions.cs b/DragonFruit.Six.Api/Accounts/UbisoftAccountExtensions.cs index 1ab64c3d..bf467fad 100644 --- a/DragonFruit.Six.Api/Accounts/UbisoftAccountExtensions.cs +++ b/DragonFruit.Six.Api/Accounts/UbisoftAccountExtensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using DragonFruit.Data; using DragonFruit.Six.Api.Accounts.Entities; using DragonFruit.Six.Api.Accounts.Enums; using DragonFruit.Six.Api.Accounts.Requests; @@ -66,10 +65,5 @@ public static Task> GetAccou var request = new UbisoftAccountActivityRequest(accounts); return client.PerformAsync(request).ContinueWith(t => t.Result.DeserializeUbisoftAccountActivity()); } - - /// - /// Get the current device location based on a IP Geolocation lookup - /// - public static Task GeolocateAsync(this ApiClient client) => client.PerformAsync(new UbisoftGeolocationRequest()); } } diff --git a/DragonFruit.Six.Api/Accounts/Entities/Geolocation.cs b/DragonFruit.Six.Api/Services/Geolocation/Geolocation.cs similarity index 96% rename from DragonFruit.Six.Api/Accounts/Entities/Geolocation.cs rename to DragonFruit.Six.Api/Services/Geolocation/Geolocation.cs index 05cd1ea5..e371bd99 100644 --- a/DragonFruit.Six.Api/Accounts/Entities/Geolocation.cs +++ b/DragonFruit.Six.Api/Services/Geolocation/Geolocation.cs @@ -4,7 +4,7 @@ using System; using Newtonsoft.Json; -namespace DragonFruit.Six.Api.Accounts.Entities +namespace DragonFruit.Six.Api.Services.Geolocation { [Serializable] public class Geolocation diff --git a/DragonFruit.Six.Api/Services/Geolocation/GeolocationExtensions.cs b/DragonFruit.Six.Api/Services/Geolocation/GeolocationExtensions.cs new file mode 100644 index 00000000..352471f8 --- /dev/null +++ b/DragonFruit.Six.Api/Services/Geolocation/GeolocationExtensions.cs @@ -0,0 +1,51 @@ +// Dragon6 API Copyright DragonFruit Network +// Licensed under Apache-2. Refer to the LICENSE file for more info + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using DragonFruit.Data; +using DragonFruit.Data.Serializers.Newtonsoft; +using DragonFruit.Six.Api.Services.Geolocation.Requests; +using Newtonsoft.Json.Linq; + +namespace DragonFruit.Six.Api.Services.Geolocation +{ + public static class GeolocationExtensions + { + /// + /// Get the current device location based on a IP Geolocation lookup + /// + public static Task GeolocateAsync(this T client) where T : ApiClient + { + return client.PerformAsync(new UbisoftSelfGeolocationRequest()); + } + + /// + /// Get the approximate location and ISP info for a collection of IP addresses + /// + /// The to use + /// A collection of IP addresses to lookup + /// An containing the IP addresses and their approximate location + public static async Task> GeolocateAsync(this Dragon6Client client, params string[] ips) + { + var request = new UbisoftGeolocationRequest(ips); + var container = await client.PerformAsync(request).ConfigureAwait(false); + + return container["ipLocations"]?.ToObject() ?? Array.Empty(); + } + + /// + /// Get the approximate location and ISP info for a collection of IP addresses + /// + /// The to use + /// A collection of IP addresses to lookup + /// An containing the IP addresses and their approximate location + public static Task> GeolocateAsync(this Dragon6Client client, params IPAddress[] ips) + { + return GeolocateAsync(client, ips.Select(x => x.ToString()).ToArray()); + } + } +} diff --git a/DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftGeolocationRequest.cs b/DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftGeolocationRequest.cs new file mode 100644 index 00000000..a558f0a5 --- /dev/null +++ b/DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftGeolocationRequest.cs @@ -0,0 +1,25 @@ +// Dragon6 API Copyright DragonFruit Network +// Licensed under Apache-2. Refer to the LICENSE file for more info + +using System.Collections.Generic; +using DragonFruit.Data; +using DragonFruit.Data.Parameters; + +namespace DragonFruit.Six.Api.Services.Geolocation.Requests +{ + /// + /// Creates a request to get the approximate location a collection of provided IP addresses + /// + public class UbisoftGeolocationRequest : UbiApiRequest + { + public override string Path => $"{Endpoints.BaseEndpoint}/v2/iplocation"; + + public UbisoftGeolocationRequest(IEnumerable addresses) + { + Addresses = addresses; + } + + [QueryParameter("ips", CollectionConversionMode.Concatenated)] + public IEnumerable Addresses { get; } + } +} diff --git a/DragonFruit.Six.Api/Accounts/Requests/UbisoftGeolocationRequest.cs b/DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftSelfGeolocationRequest.cs similarity index 65% rename from DragonFruit.Six.Api/Accounts/Requests/UbisoftGeolocationRequest.cs rename to DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftSelfGeolocationRequest.cs index 8d2393ba..302c154e 100644 --- a/DragonFruit.Six.Api/Accounts/Requests/UbisoftGeolocationRequest.cs +++ b/DragonFruit.Six.Api/Services/Geolocation/Requests/UbisoftSelfGeolocationRequest.cs @@ -6,9 +6,15 @@ using DragonFruit.Data.Requests; using DragonFruit.Six.Api.Enums; -namespace DragonFruit.Six.Api.Accounts.Requests +namespace DragonFruit.Six.Api.Services.Geolocation.Requests { - public class UbisoftGeolocationRequest : ApiRequest, IRequestExecutingCallback + /// + /// Creates a request to get the approximate location of the current user-agent + /// + /// + /// While this endpoint does not require authentication, a valid Ubi-AppId header will be attached + /// + public class UbisoftSelfGeolocationRequest : ApiRequest, IRequestExecutingCallback { public override string Path => $"{Endpoints.BaseEndpoint}/v2/profiles/me/iplocation";