From cc66f75d00774cce63bd3d91b01b4994827892c5 Mon Sep 17 00:00:00 2001 From: KevDaDev <65958288+KevinDaGame@users.noreply.github.com> Date: Sat, 17 Feb 2024 15:05:09 +0100 Subject: [PATCH] Implement more definitions --- RemindoBot/Commands/UrbanDictionaryCommand.cs | 17 +- RemindoBot/Handlers/UrbanButtonHandler.cs | 152 ++++++++++++++++++ RemindoBot/Program.cs | 8 +- .../Services/ActualDictionaryService.cs | 42 +++++ .../Services/IActualDictionaryService.cs | 14 ++ .../Services/IUrbanDictionaryService.cs | 2 +- RemindoBot/Services/UrbanDictionaryService.cs | 25 ++- .../Services/UrbanUrbanDictionaryService.cs | 18 --- 8 files changed, 243 insertions(+), 35 deletions(-) create mode 100644 RemindoBot/Handlers/UrbanButtonHandler.cs create mode 100644 RemindoBot/Services/ActualDictionaryService.cs create mode 100644 RemindoBot/Services/IActualDictionaryService.cs delete mode 100644 RemindoBot/Services/UrbanUrbanDictionaryService.cs diff --git a/RemindoBot/Commands/UrbanDictionaryCommand.cs b/RemindoBot/Commands/UrbanDictionaryCommand.cs index fb2d6ac..c34c9e1 100644 --- a/RemindoBot/Commands/UrbanDictionaryCommand.cs +++ b/RemindoBot/Commands/UrbanDictionaryCommand.cs @@ -31,9 +31,10 @@ public async Task Handle(SocketSlashCommand command) string word = command.Data.Options.First().Value.ToString() ?? throw new ArgumentNullException("word", "Word cannot be null"); UrbanDictionaryDefinition? definition; + int definitionCount; try { - definition = await _urbanDictionaryService.GetDefinitionOfWord(word); + definition = await _urbanDictionaryService.GetDefinitionOfWord(word, out definitionCount); } catch (FlurlHttpException) { @@ -51,15 +52,17 @@ public async Task Handle(SocketSlashCommand command) .WithTitle(definition.Word) .WithDescription(definition.Definition) .WithUrl(definition.Permalink) - .WithFooter(definition.Author) + .WithFooter( + $"{definition.Author} - [1/{definitionCount}] - {definition.Thumbs_up} 👍 {definition.Thumbs_down} 👎") .WithColor(Color.Blue) .WithTimestamp(DateTime.Parse(definition.Written_on)); - - MessageComponent? viewButton = new ComponentBuilder() + + MessageComponent buttons = new ComponentBuilder() .WithButton("View on Urban Dictionary", style: ButtonStyle.Link, url: definition.Permalink) + .WithButton("Next definition", customId: $"urban-{word}-1") + .WithButton("Actual definition", customId: $"actual-{word}-0") .Build(); - - - await command.RespondAsync(embed: embed.Build(), components: viewButton); + + await command.RespondAsync(embed: embed.Build(), components: buttons); } } \ No newline at end of file diff --git a/RemindoBot/Handlers/UrbanButtonHandler.cs b/RemindoBot/Handlers/UrbanButtonHandler.cs new file mode 100644 index 0000000..b4c4ba9 --- /dev/null +++ b/RemindoBot/Handlers/UrbanButtonHandler.cs @@ -0,0 +1,152 @@ +using Discord; +using Discord.WebSocket; +using Flurl.Http; +using Microsoft.Extensions.Logging; +using RemindoBot.Services; + +namespace RemindoBot.Handlers; + +public class UrbanButtonHandler : IHandler +{ + private readonly DiscordSocketClient _client; + private readonly ILogger _logger; + private readonly IUrbanDictionaryService _urbanDictionaryService; + private readonly IActualDictionaryService _actualDictionaryService; + + public UrbanButtonHandler(DiscordSocketClient client, ILogger logger, + IUrbanDictionaryService urbanDictionaryService, + IActualDictionaryService actualDictionaryService) + { + _client = client; + _logger = logger; + _urbanDictionaryService = urbanDictionaryService; + _actualDictionaryService = actualDictionaryService; + } + + public void RegisterHandlers() + { + _client.ButtonExecuted += HandleUrbanButton; + } + + public async Task HandleUrbanButton(SocketMessageComponent component) + { + if (component.Data.CustomId.StartsWith("urban-")) + { + string word = component.Data.CustomId.Split("-")[1]; + int number = int.Parse(component.Data.CustomId.Split("-")[^1]); + + if (number < 0) + { + await component.RespondAsync("Invalid definition number"); + return; + } + + UrbanDictionaryDefinition definition; + int definitionCount; + try + { + var definitions = await _urbanDictionaryService.GetDefinitionsOfWord(word); + if (number >= definitions.Count) + { + await component.RespondAsync("no more definitions"); + return; + } + + definitionCount = definitions.Count; + definition = definitions[number]; + } + catch (FlurlHttpException) + { + await component.RespondAsync("Failed to fetch definition"); + return; + } + + EmbedBuilder? embed = new EmbedBuilder() + .WithTitle(definition.Word) + .WithDescription(definition.Definition) + .WithUrl(definition.Permalink) + .WithFooter( + $"{definition.Author} - [{number + 1}/{definitionCount}] - {definition.Thumbs_up} 👍 {definition.Thumbs_down} 👎") + .WithColor(Color.Blue) + .WithTimestamp(DateTime.Parse(definition.Written_on)); + + ComponentBuilder buttonsBuilder = new ComponentBuilder() + .WithButton("View on Urban Dictionary", style: ButtonStyle.Link, url: definition.Permalink); + + if (number > 0) + { + buttonsBuilder.WithButton("Previous definition", customId: $"urban-{word}-{number - 1}"); + } + + if (number < definitionCount - 1) + { + buttonsBuilder.WithButton("Next definition", customId: $"urban-{word}-{number + 1}"); + } + + buttonsBuilder.WithButton("Actual definition", style: ButtonStyle.Success, customId: $"actual-{word}-0"); + + await component.Message.ModifyAsync(properties => + { + properties.Embed = embed.Build(); + properties.Components = buttonsBuilder.Build(); + }); + await component.DeferAsync(); + } + + if (component.Data.CustomId.StartsWith("actual-")) + { + string word = component.Data.CustomId.Split("-")[1]; + int number = int.Parse(component.Data.CustomId.Split("-")[^1]); + if (number < 0) + { + await component.RespondAsync("Invalid definition number"); + return; + } + + ActualDictionaryDefinition definition; + int definitionCount; + try + { + var definitions = await _actualDictionaryService.GetDefinitionsOfWord(word); + if (number >= definitions.Count) + { + await component.RespondAsync("no more definitions"); + return; + } + + definitionCount = definitions.Count; + definition = definitions[number]; + } + catch (FlurlHttpException e) + { + await component.RespondAsync("No definition found"); + _logger.LogError(e, "Failed to fetch definition"); + return; + } + + EmbedBuilder? embed = new EmbedBuilder() + .WithTitle(definition.Word) + .WithDescription(definition.Definition) + .WithFooter($"[{number + 1}/{definitionCount}]") + .WithColor(Color.Blue); + + ComponentBuilder buttonsBuilder = new ComponentBuilder(); + if (number > 0) + { + buttonsBuilder.WithButton("Previous definition", customId: $"actual-{word}-{number - 1}"); + } + + if (number < definitionCount - 1) + { + buttonsBuilder.WithButton("Next definition", customId: $"actual-{word}-{number + 1}"); + } + + await component.Message.ModifyAsync(properties => + { + properties.Embed = embed.Build(); + properties.Components = buttonsBuilder.Build(); + }); + await component.DeferAsync(); + } + } +} \ No newline at end of file diff --git a/RemindoBot/Program.cs b/RemindoBot/Program.cs index 982770f..7a5bc18 100644 --- a/RemindoBot/Program.cs +++ b/RemindoBot/Program.cs @@ -50,7 +50,7 @@ public Task Client_Ready() { var handlerManager = _services.GetRequiredService(); var commandManager = _services.GetRequiredService(); - + handlerManager.RegisterHandlers(); commandManager.RegisterCommands(); return Task.CompletedTask; @@ -75,11 +75,9 @@ private static IServiceProvider CreateServices() .AddTransient() .AddTransient() .AddTransient() + .AddTransient() .AddTransient() - .AddLogging(builder => - { - builder.AddConsole(); - }) + .AddLogging(builder => { builder.AddConsole(); }) .AddDbContext(optionsBuilder => optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))) .BuildServiceProvider(); diff --git a/RemindoBot/Services/ActualDictionaryService.cs b/RemindoBot/Services/ActualDictionaryService.cs new file mode 100644 index 0000000..febed86 --- /dev/null +++ b/RemindoBot/Services/ActualDictionaryService.cs @@ -0,0 +1,42 @@ +using Flurl; +using Flurl.Http; + +namespace RemindoBot.Services; + +public class ActualDictionaryService : IActualDictionaryService +{ + string baseURI = "https://api.dictionaryapi.dev/api/v2/entries/en/"; + + public async Task> GetDefinitionsOfWord(string word) + { + var result = await baseURI.AppendPathSegment(word).GetJsonAsync(); + return result.First().Meanings.Select(m => new ActualDictionaryDefinition + { + Word = result.First().Word, + Definition = m.Definitions.First().Definition + }).ToList(); + } + + public Task GetDefinitionOfWord(string world, out int definitionCount) + { + List result = GetDefinitionsOfWord(world).Result; + definitionCount = result.Count; + return Task.FromResult(result.Count == 0 ? null : result.FirstOrDefault()); + } +} + +public class ActualDictionaryResponse +{ + public string Word { get; set; } + public List Meanings { get; set; } +} + +public class ActualDictionaryMeaning +{ + public List Definitions { get; set; } +} + +public class ActualDictionaryDefinitionR +{ + public string Definition { get; set; } +} \ No newline at end of file diff --git a/RemindoBot/Services/IActualDictionaryService.cs b/RemindoBot/Services/IActualDictionaryService.cs new file mode 100644 index 0000000..c2ebc06 --- /dev/null +++ b/RemindoBot/Services/IActualDictionaryService.cs @@ -0,0 +1,14 @@ +namespace RemindoBot.Services; + +public interface IActualDictionaryService +{ + public Task> GetDefinitionsOfWord(string word); + public Task GetDefinitionOfWord(string world, out int definitionCount); +} + +public class ActualDictionaryDefinition +{ + public string Word { get; set; } + public string Definition { get; set; } + public string Example { get; set; } +} \ No newline at end of file diff --git a/RemindoBot/Services/IUrbanDictionaryService.cs b/RemindoBot/Services/IUrbanDictionaryService.cs index 662c373..80e96b5 100644 --- a/RemindoBot/Services/IUrbanDictionaryService.cs +++ b/RemindoBot/Services/IUrbanDictionaryService.cs @@ -3,5 +3,5 @@ namespace RemindoBot.Services; public interface IUrbanDictionaryService { public Task> GetDefinitionsOfWord(string word); - public Task GetDefinitionOfWord(string world); + public Task GetDefinitionOfWord(string world, out int definitionCount); } \ No newline at end of file diff --git a/RemindoBot/Services/UrbanDictionaryService.cs b/RemindoBot/Services/UrbanDictionaryService.cs index 40cf231..e0cf3c8 100644 --- a/RemindoBot/Services/UrbanDictionaryService.cs +++ b/RemindoBot/Services/UrbanDictionaryService.cs @@ -13,10 +13,27 @@ public async Task> GetDefinitionsOfWord(string w return result.List; } - public async Task GetDefinitionOfWord(string world) + public Task GetDefinitionOfWord(string world, out int definitionCount) { - List result = await GetDefinitionsOfWord(world); - - return result.Count == 0 ? null : result.FirstOrDefault(); + List result = GetDefinitionsOfWord(world).Result; + definitionCount = result.Count; + return Task.FromResult(result.Count == 0 ? null : result.FirstOrDefault()); } +} + +public class UrbanDictionaryResponse +{ + public List List { get; set; } +} + +public class UrbanDictionaryDefinition +{ + public string Definition { get; set; } + public string Example { get; set; } + public string Permalink { get; set; } + public string Word { get; set; } + public int Thumbs_up { get; set; } + public int Thumbs_down { get; set; } + public string Author { get; set; } + public string Written_on { get; set; } } \ No newline at end of file diff --git a/RemindoBot/Services/UrbanUrbanDictionaryService.cs b/RemindoBot/Services/UrbanUrbanDictionaryService.cs deleted file mode 100644 index f7cd1d2..0000000 --- a/RemindoBot/Services/UrbanUrbanDictionaryService.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace RemindoBot.Services; - -public class UrbanDictionaryResponse -{ - public List List { get; set; } -} - -public class UrbanDictionaryDefinition -{ - public string Definition { get; set; } - public string Example { get; set; } - public string Permalink { get; set; } - public string Word { get; set; } - public int ThumbsUp { get; set; } - public int ThumbsDown { get; set; } - public string Author { get; set; } - public string Written_on { get; set; } -} \ No newline at end of file