From 3e73dc8ba5b50db9d2e2bf783d8a4e19b50a46fc Mon Sep 17 00:00:00 2001 From: Toastie Date: Thu, 1 Aug 2024 13:41:30 +1200 Subject: [PATCH] .define slightly improved and refactored --- src/EllieBot/Modules/Searches/Searches.cs | 90 ++++++------------- .../Modules/Searches/SearchesService.cs | 64 ++++++++++++- .../Modules/Searches/_common/DefineData.cs | 10 +++ 3 files changed, 100 insertions(+), 64 deletions(-) create mode 100644 src/EllieBot/Modules/Searches/_common/DefineData.cs diff --git a/src/EllieBot/Modules/Searches/Searches.cs b/src/EllieBot/Modules/Searches/Searches.cs index 9159ac6..b3cc561 100644 --- a/src/EllieBot/Modules/Searches/Searches.cs +++ b/src/EllieBot/Modules/Searches/Searches.cs @@ -327,71 +327,35 @@ public partial class Searches : EllieModule if (!await ValidateQuery(word)) return; - using var http = _httpFactory.CreateClient(); - string res; - try + + var maybeItems = await _service.GetDefinitionsAsync(word); + + if (!maybeItems.TryPickT0(out var defs, out var error)) { - res = await _cache.GetOrCreateAsync($"define_{word}", - e => - { - e.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12); - return http.GetStringAsync("https://api.pearson.com/v2/dictionaries/entries?headword=" - + WebUtility.UrlEncode(word)); - }); - - var responseModel = JsonConvert.DeserializeObject(res); - - var data = responseModel.Results - .Where(x => x.Senses is not null - && x.Senses.Count > 0 - && x.Senses[0].Definition is not null) - .Select(x => (Sense: x.Senses[0], x.PartOfSpeech)) - .ToList(); - - if (!data.Any()) - { - Log.Warning("Definition not found: {Word}", word); - await Response().Error(strs.define_unknown).SendAsync(); - } - - - var col = data.Select(x => ( - Definition: x.Sense.Definition is string - ? x.Sense.Definition.ToString() - : ((JArray)JToken.Parse(x.Sense.Definition.ToString())).First.ToString(), - Example: x.Sense.Examples is null || x.Sense.Examples.Count == 0 - ? string.Empty - : x.Sense.Examples[0].Text, Word: word, - WordType: string.IsNullOrWhiteSpace(x.PartOfSpeech) ? "-" : x.PartOfSpeech)) - .ToList(); - - Log.Information("Sending {Count} definition for: {Word}", col.Count, word); - - await Response() - .Paginated() - .Items(col) - .PageSize(1) - .Page((items, _) => - { - var model = items.First(); - var embed = _sender.CreateEmbed() - .WithDescription(ctx.User.Mention) - .AddField(GetText(strs.word), model.Word, true) - .AddField(GetText(strs._class), model.WordType, true) - .AddField(GetText(strs.definition), model.Definition) - .WithOkColor(); - - if (!string.IsNullOrWhiteSpace(model.Example)) - embed.AddField(GetText(strs.example), model.Example); - - return embed; - }) - .SendAsync(); - } - catch (Exception ex) - { - Log.Error(ex, "Error retrieving definition data for: {Word}", word); + await HandleErrorAsync(error); + return; } + + await Response() + .Paginated() + .Items(defs) + .PageSize(1) + .Page((items, _) => + { + var model = items.First(); + var embed = _sender.CreateEmbed() + .WithDescription(ctx.User.Mention) + .AddField(GetText(strs.word), model.Word, true) + .AddField(GetText(strs._class), model.WordType, true) + .AddField(GetText(strs.definition), model.Definition) + .WithOkColor(); + + if (!string.IsNullOrWhiteSpace(model.Example)) + embed.AddField(GetText(strs.example), model.Example); + + return embed; + }) + .SendAsync(); } [Cmd] diff --git a/src/EllieBot/Modules/Searches/SearchesService.cs b/src/EllieBot/Modules/Searches/SearchesService.cs index c336b72..3f01fc1 100644 --- a/src/EllieBot/Modules/Searches/SearchesService.cs +++ b/src/EllieBot/Modules/Searches/SearchesService.cs @@ -399,7 +399,7 @@ public class SearchesService : IEService return gamesMap[key]; } - public async Task> GetWikipediaPageAsync(string query) + public async Task> GetWikipediaPageAsync(string query) { query = query.Trim(); if (string.IsNullOrEmpty(query)) @@ -516,4 +516,66 @@ public class SearchesService : IEService return ErrorType.Unknown; } } + + private static TypedKey GetDefineKey(string query) + => new TypedKey($"define_{query}"); + + public async Task, ErrorType>> GetDefinitionsAsync(string query) + { + if (string.IsNullOrEmpty(query)) + { + return ErrorType.InvalidInput; + } + + query = Uri.EscapeDataString(query); + + using var http = _httpFactory.CreateClient(); + string res; + try + { + res = await _c.GetOrAddAsync(GetDefineKey(query), + async () => await http.GetStringAsync( + $"https://api.pearson.com/v2/dictionaries/entries?headword={query}"), + TimeSpan.FromHours(12)); + + var responseModel = JsonConvert.DeserializeObject(res); + + var data = responseModel.Results + .Where(x => x.Senses is not null + && x.Senses.Count > 0 + && x.Senses[0].Definition is not null) + .Select(x => (Sense: x.Senses[0], x.PartOfSpeech)) + .ToList(); + + if (!data.Any()) + { + Log.Warning("Definition not found: {Word}", query); + return ErrorType.NotFound; + } + + var items = new List(); + + foreach (var d in data) + { + items.Add(new DefineData + { + Definition = d.Sense.Definition is JArray { Count: > 0 } defs + ? defs[0].ToString() + : d.Sense.Definition.ToString(), + Example = d.Sense.Examples is null || d.Sense.Examples.Count == 0 + ? string.Empty + : d.Sense.Examples[0].Text, + WordType = string.IsNullOrWhiteSpace(d.PartOfSpeech) ? "-" : d.PartOfSpeech, + Word = query, + }); + } + + return items.OrderByDescending(x => !string.IsNullOrWhiteSpace(x.Example)).ToList(); + } + catch (Exception ex) + { + Log.Error(ex, "Error retrieving definition data for: {Word}", query); + return ErrorType.Unknown; + } + } } \ No newline at end of file diff --git a/src/EllieBot/Modules/Searches/_common/DefineData.cs b/src/EllieBot/Modules/Searches/_common/DefineData.cs new file mode 100644 index 0000000..2698d50 --- /dev/null +++ b/src/EllieBot/Modules/Searches/_common/DefineData.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Modules.Searches.Services; + +public sealed class DefineData +{ + public required string Definition { get; init; } + public required string Example { get; init; } + public required string WordType { get; init; } + public required string Word { get; init; } +} \ No newline at end of file