diff --git a/src/Apis/Bing/BingVisualSearch.cs b/src/Apis/Bing/BingVisualSearch.cs index e582462..d7b0f43 100644 --- a/src/Apis/Bing/BingVisualSearch.cs +++ b/src/Apis/Bing/BingVisualSearch.cs @@ -56,7 +56,7 @@ public BingVisualSearch(HttpClient httpClient) } /// - public async Task> ReverseImageSearchAsync(string url, + public async Task> ReverseImageSearchAsync(string url, BingSafeSearchLevel safeSearch = BingSafeSearchLevel.Moderate, string? language = null, CancellationToken cancellationToken = default) { @@ -83,19 +83,18 @@ public async Task> ReverseImageSearch throw new BingException(message, imageCategory); } - var root = document.RootElement.Clone(); - - var rawItems = root + return document.RootElement .GetProperty("tags") .EnumerateArray() .Select(x => x.GetPropertyOrDefault("actions")) .SelectMany(x => x.EnumerateArrayOrEmpty()) - .FirstOrDefault(x => x.GetPropertyOrDefault("actionType").GetStringOrDefault() == "VisualSearch") + .FirstOrDefault(x => x.TryGetProperty("actionType", out var actionTye) && actionTye.ValueEquals("VisualSearch"u8)) .GetPropertyOrDefault("data") .GetPropertyOrDefault("value") - .EnumerateArrayOrEmpty(); - - return rawItems.Select(item => item.Deserialize()!); + .EnumerateArrayOrEmpty() + .Select(item => item.Deserialize()!) + .ToArray() + .AsReadOnly(); } /// @@ -112,7 +111,7 @@ public void Dispose() private static HttpRequestMessage BuildRequest(string url, string invokedSkill, BingSafeSearchLevel safeSearch = BingSafeSearchLevel.Moderate, string? language = null) { - string jsonRequest = $$"""{"imageInfo":{"url":"{{url}}","source":"Url"},"knowledgeRequest":{"invokedSkills":["{{invokedSkill}}"]}}"""; + string jsonRequest = $$$"""{"imageInfo":{"url":"{{{url}}}","source":"Url"},"knowledgeRequest":{"invokedSkills":["{{{invokedSkill}}}"]}}"""; var content = new MultipartFormDataContent { { new StringContent(jsonRequest), "knowledgeRequest" } diff --git a/src/Apis/Bing/IBingVisualSearch.cs b/src/Apis/Bing/IBingVisualSearch.cs index 994b399..01dae9b 100644 --- a/src/Apis/Bing/IBingVisualSearch.cs +++ b/src/Apis/Bing/IBingVisualSearch.cs @@ -16,8 +16,8 @@ public interface IBingVisualSearch /// The safe search level. /// The language of the results. /// The cancellation token. - /// A representing the asynchronous search operation. The result contains an of search results. - Task> ReverseImageSearchAsync(string url, + /// A representing the asynchronous search operation. The result contains a read-only list of search results. + Task> ReverseImageSearchAsync(string url, BingSafeSearchLevel safeSearch = BingSafeSearchLevel.Moderate, string? language = null, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Modules/ImageModule.cs b/src/Modules/ImageModule.cs index 7c123d1..14009aa 100644 --- a/src/Modules/ImageModule.cs +++ b/src/Modules/ImageModule.cs @@ -337,13 +337,12 @@ public virtual async Task BingAsync(string url, bool multiImages, } bool isNsfw = Context.Channel.IsNsfw(); - IBingReverseImageSearchResult[] results; + IReadOnlyList results; _logger.LogInformation("Sending Bing reverse image search request (URL: {Url}, is NSFW: {IsNsfw}, language: {Language})", url, isNsfw, interaction.GetLanguageCode()); try { - results = (await _bingVisualSearch.ReverseImageSearchAsync(url, isNsfw ? BingSafeSearchLevel.Off : BingSafeSearchLevel.Strict, interaction.GetLanguageCode())) - .ToArray(); + results = await _bingVisualSearch.ReverseImageSearchAsync(url, isNsfw ? BingSafeSearchLevel.Off : BingSafeSearchLevel.Strict, interaction.GetLanguageCode()); } catch (BingException e) { @@ -351,15 +350,15 @@ public virtual async Task BingAsync(string url, bool multiImages, return FergunResult.FromError(e.ImageCategory is null ? e.Message : _localizer[$"Bing{e.ImageCategory}"], ephemeral, interaction); } - _logger.LogDebug("Bing reverse image search result count: {Count}", results.Length); + _logger.LogDebug("Bing reverse image search result count: {Count}", results.Count); - if (results.Length == 0) + if (results.Count == 0) { return FergunResult.FromError(_localizer["NoResults"], ephemeral, interaction); } int count = multiImages ? 4 : 1; - int maxIndex = (int)Math.Ceiling((double)results.Length / count) - 1; + int maxIndex = (int)Math.Ceiling((double)results.Count / count) - 1; var paginator = new LazyPaginatorBuilder() .WithPageFactory(GeneratePage) diff --git a/tests/Fergun.Tests/Apis/BingVisualSearchTests.cs b/tests/Fergun.Tests/Apis/BingVisualSearchTests.cs index 5f09b67..076309d 100644 --- a/tests/Fergun.Tests/Apis/BingVisualSearchTests.cs +++ b/tests/Fergun.Tests/Apis/BingVisualSearchTests.cs @@ -1,6 +1,5 @@ using System; using System.Drawing; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Fergun.Apis.Bing; @@ -19,7 +18,7 @@ public class BingVisualSearchTests [InlineData("https://r.bing.com/rp/NFrQjXWivF4omoTPSU03A6aosg0.jpg", BingSafeSearchLevel.Strict, "es")] public async Task ReverseImageSearchAsync_Returns_Results(string url, BingSafeSearchLevel safeSearch, string? language) { - var results = (await _bingVisualSearch.ReverseImageSearchAsync(url, safeSearch, language)).ToArray(); + var results = await _bingVisualSearch.ReverseImageSearchAsync(url, safeSearch, language); Assert.NotNull(results); Assert.NotEmpty(results); diff --git a/tests/Fergun.Tests/Utils.cs b/tests/Fergun.Tests/Utils.cs index 526eb8e..7981031 100644 --- a/tests/Fergun.Tests/Utils.cs +++ b/tests/Fergun.Tests/Utils.cs @@ -127,8 +127,8 @@ public static IBingVisualSearch CreateMockedBingVisualSearchApi(Faker? faker = n faker ??= new Faker(); var bingMock = new Mock(); - bingMock.Setup(x => x.ReverseImageSearchAsync(It.Is(s => s == string.Empty), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Enumerable.Empty); - bingMock.Setup(x => x.ReverseImageSearchAsync(It.Is(s => !string.IsNullOrEmpty(s)), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(() => faker.MakeLazy(50, () => CreateMockedBingReverseImageSearchResult(faker))); + bingMock.Setup(x => x.ReverseImageSearchAsync(It.Is(s => s == string.Empty), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Array.Empty); + bingMock.Setup(x => x.ReverseImageSearchAsync(It.Is(s => !string.IsNullOrEmpty(s)), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(() => faker.Make(50, () => CreateMockedBingReverseImageSearchResult(faker)).AsReadOnly()); bingMock.Setup(x => x.ReverseImageSearchAsync(It.Is(s => s == "https://example.com/error"), It.IsAny(), It.IsAny(), It.IsAny())).ThrowsAsync(new BingException("Error message.")); return bingMock.Object;