Skip to content

Commit

Permalink
Merge pull request #68 from flyingfisch/49-add-slash-commands
Browse files Browse the repository at this point in the history
Switched commands from text commands to slash commands
  • Loading branch information
flyingfisch authored Aug 9, 2022
2 parents 80a0ead + f5b7e08 commit 625a545
Show file tree
Hide file tree
Showing 20 changed files with 377 additions and 227 deletions.
46 changes: 19 additions & 27 deletions src/FischBot.UnitTests/Modules/InfoModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,11 @@ namespace FischBot.UnitTests.Modules
public class InfoModuleTests
{
private Mock<IDiscordModuleService> _moduleService;
private Mock<CommandService> _commands;
private Mock<IConfiguration> _configuration;

[TestInitialize]
public void InitializeDependencies()
{
_moduleService = new Mock<IDiscordModuleService>();
_commands = new Mock<CommandService>();
_configuration = new Mock<IConfiguration>();
}

[TestMethod]
Expand All @@ -35,17 +31,15 @@ public async Task SayAsync_CallsReplyAsyncWithProvidedMessage()
await infoModule.SayAsync(echoMessage);

// Assert
_moduleService.Verify(moduleService => moduleService.ReplyAsync(It.IsAny<ICommandContext>(),
It.Is<string>(m => m == echoMessage),
It.IsAny<bool>(),
It.IsAny<Embed>(),
It.IsAny<RequestOptions>(),
It.IsAny<AllowedMentions>(),
It.IsAny<MessageReference>(),
It.IsAny<MessageComponent>(),
It.IsAny<ISticker[]>(),
It.IsAny<Embed[]>(),
It.IsAny<MessageFlags>()), Times.Once);
_moduleService.Verify(moduleService => moduleService.RespondAsync(It.IsAny<IInteractionContext>(),
It.Is<string>(m => m == echoMessage),
It.IsAny<Embed[]>(),
It.IsAny<bool>(),
It.IsAny<bool>(),
It.IsAny<AllowedMentions>(),
It.IsAny<RequestOptions>(),
It.IsAny<MessageComponent>(),
It.IsAny<Embed>()), Times.Once);
}

[TestMethod]
Expand All @@ -59,22 +53,20 @@ public async Task ReceivePraiseAsync_CallsReplyAsyncWithBlushEmoji()
await infoModule.ReceivePraiseAsync();

// Assert
_moduleService.Verify(moduleService => moduleService.ReplyAsync(It.IsAny<ICommandContext>(),
It.Is<string>(m => m == blushEmoji),
It.IsAny<bool>(),
It.IsAny<Embed>(),
It.IsAny<RequestOptions>(),
It.IsAny<AllowedMentions>(),
It.IsAny<MessageReference>(),
It.IsAny<MessageComponent>(),
It.IsAny<ISticker[]>(),
It.IsAny<Embed[]>(),
It.IsAny<MessageFlags>()), Times.Once);
_moduleService.Verify(moduleService => moduleService.RespondAsync(It.IsAny<IInteractionContext>(),
It.Is<string>(m => m == blushEmoji),
It.IsAny<Embed[]>(),
It.IsAny<bool>(),
It.IsAny<bool>(),
It.IsAny<AllowedMentions>(),
It.IsAny<RequestOptions>(),
It.IsAny<MessageComponent>(),
It.IsAny<Embed>()), Times.Once);
}

private InfoModule BuildInfoModule()
{
return new InfoModule(_moduleService.Object, _commands.Object, _configuration.Object);
return new InfoModule(_moduleService.Object);
}
}
}
4 changes: 2 additions & 2 deletions src/FischBot/FischBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>0.3.7</AssemblyVersion>
<Version>0.3-combfish-rev7</Version>
<AssemblyVersion>0.4.0</AssemblyVersion>
<Version>0.4-dragonfish-rev0</Version>
<Deterministic>false</Deterministic>
</PropertyGroup>

Expand Down
94 changes: 94 additions & 0 deletions src/FischBot/Handlers/InteractionHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace FischBot.Handlers
{
public class InteractionHandler
{
private readonly DiscordSocketClient _discordClient;
private readonly InteractionService _commands;
private readonly IServiceProvider _services;
private readonly ILogger _logger;

public InteractionHandler(DiscordSocketClient discordClient, InteractionService commands, IServiceProvider services)
{
_discordClient = discordClient;
_commands = commands;
_services = services;
_logger = services.GetRequiredService<ILogger<InteractionHandler>>();
}

public async Task InitializeAsync()
{
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);

_discordClient.InteractionCreated += HandleInteraction;

_commands.SlashCommandExecuted += SlashCommandExecuted;
_commands.ContextCommandExecuted += ContextCommandExecuted;
_commands.ComponentCommandExecuted += ComponentCommandExecuted;
}

private async Task HandleInteraction(SocketInteraction interaction)
{
var context = new SocketInteractionContext(_discordClient, interaction);

try
{
await _commands.ExecuteCommandAsync(context, _services);
}
catch (Exception)
{
if (interaction.Type == InteractionType.ApplicationCommand)
{
await interaction.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync());
}
}
}

private async Task ComponentCommandExecuted(ComponentCommandInfo command, Discord.IInteractionContext context, IResult result)
{
if (result.IsSuccess)
{
_logger.LogInformation($"Component command [{command.Name}] executed for [{context.User.Username}] on [{context.Guild.Name}]");
}
else
{
_logger.LogError($"Component command failed to execute for [{context.User.Username}] <-> [{result}]!");
await context.Interaction.RespondAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!", ephemeral: true);
}
}

private async Task ContextCommandExecuted(ContextCommandInfo command, Discord.IInteractionContext context, IResult result)
{
if (result.IsSuccess)
{
_logger.LogInformation($"Context command [{command.Name}] executed for [{context.User.Username}] on [{context.Guild.Name}]");
}
else
{
_logger.LogError($"Context command failed to execute for [{context.User.Username}] <-> [{result}]!");
await context.Interaction.RespondAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!", ephemeral: true);
}
}

private async Task SlashCommandExecuted(SlashCommandInfo command, Discord.IInteractionContext context, IResult result)
{
if (result.IsSuccess)
{
_logger.LogInformation($"Slash command [{command.Name}] executed for [{context.User.Username}] on [{context.Guild.Name}]");
}
else
{
_logger.LogError($"Slash command failed to execute for [{context.User.Username}] <-> [{result}]!");
await context.Interaction.RespondAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!", ephemeral: true);
}
}
}
}
15 changes: 7 additions & 8 deletions src/FischBot/Modules/AstronomyModule.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Interactions;
using FischBot.Services.AstronomyService;
using FischBot.Services.DiscordModuleService;

namespace FischBot.Modules
{
[Group("astro")]
public class AstronomyModule : FischBotModuleBase<SocketCommandContext>
[Group("astro", "Astronomy commands")]
public class AstronomyModule : FischBotInteractionModuleBase<SocketInteractionContext>
{
private readonly IAstronomyService _astronomyService;

Expand All @@ -17,9 +17,8 @@ public AstronomyModule(IDiscordModuleService moduleService, IAstronomyService as
_astronomyService = astronomyService;
}

[Command("apod")]
[Summary("Displays the NASA astronomy picture of the day.")]
public async Task DisplayApod([Summary("Date to query for.")] DateTime? date = null)
[SlashCommand("apod", "Displays the NASA astronomy picture of the day")]
public async Task DisplayApod([Summary(description: "Date to query for (optional)")] DateTime? date = null)
{
var apod = await _astronomyService.GetPictureOfTheDay(date);

Expand All @@ -36,15 +35,15 @@ public async Task DisplayApod([Summary("Date to query for.")] DateTime? date = n
.WithImageUrl(apod.ThumbnailUrl)
.Build();

await ReplyAsync(embed: videoEmbed);
await RespondAsync(embed: videoEmbed);
}
else
{
var imageEmbed = embed
.WithImageUrl(apod.Url)
.Build();

await ReplyAsync(embed: imageEmbed);
await RespondAsync(embed: imageEmbed);
}
}
}
Expand Down
16 changes: 7 additions & 9 deletions src/FischBot/Modules/CryptocurrencyModule.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Interactions;
using FischBot.Services.DiscordModuleService;
using FischBot.Services.FinanceService;

namespace FischBot.Modules
{
[Group("crypto")]
public class CryptocurrencyModule : FischBotModuleBase<SocketCommandContext>
[Group("crypto", "Cryptocurrency commands")]
public class CryptocurrencyModule : FischBotInteractionModuleBase<SocketInteractionContext>
{
private readonly IFinanceService _financeService;

Expand All @@ -18,9 +17,8 @@ public CryptocurrencyModule(IDiscordModuleService moduleService, IFinanceService
_financeService = financeService;
}

[Command("price")]
[Summary("Gets today's price information for the specified crypto symbol.")]
public async Task DisplayRealtimeCryptoInfo([Summary("Crypto symbol to get information for")] string symbol)
[SlashCommand("price", "Gets today's price information for the specified crypto symbol.")]
public async Task DisplayRealtimeCryptoInfo([Summary(description: "Crypto symbol to get information for")] string symbol)
{
try
{
Expand All @@ -46,11 +44,11 @@ public async Task DisplayRealtimeCryptoInfo([Summary("Crypto symbol to get infor
.WithFooter($"Source: twelvedata | Daily usage: {usageStats.daily_usage}/{usageStats.plan_daily_limit}")
.Build();

await ReplyAsync(embed: embed);
await RespondAsync(embed: embed);
}
catch (Exception)
{
await ReplyAsync("Unable to get information for that cryptocurrency.");
await RespondAsync("Unable to get information for that cryptocurrency.", ephemeral: true);
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/FischBot/Modules/FischBotInteractionModuleBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Threading.Tasks;
using Discord;
using Discord.Interactions;
using FischBot.Services.DiscordModuleService;

namespace FischBot.Modules
{
public class FischBotInteractionModuleBase<T> : InteractionModuleBase where T : class, IInteractionContext
{
private readonly IDiscordModuleService _moduleService;

public FischBotInteractionModuleBase(IDiscordModuleService moduleService) : base()
{
_moduleService = moduleService;
}

protected override async Task RespondAsync(
string text = null,
Embed[] embeds = null,
bool isTTS = false,
bool ephemeral = false,
AllowedMentions allowedMentions = null,
RequestOptions options = null,
MessageComponent components = null,
Embed embed = null)
{
await _moduleService.RespondAsync(Context, text, embeds, isTTS, ephemeral, allowedMentions, options, components, embed);
}
}
}
25 changes: 14 additions & 11 deletions src/FischBot/Modules/FunModule.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Interactions;
using FischBot.Helpers;
using FischBot.Services.DiscordModuleService;

namespace FischBot.Modules
{
public class FunModule : FischBotModuleBase<SocketCommandContext>
public class FunModule : FischBotInteractionModuleBase<SocketInteractionContext>
{
private static readonly string[] _emojiPartyEmojis =
{
Expand All @@ -18,27 +18,30 @@ public class FunModule : FischBotModuleBase<SocketCommandContext>
"🤭", "🤠", "🥺", "😈", "☝", "✊", "🦊", "🐠"
};

private static readonly string[] _partyTimeMessages =
{
"**PARTAY TIME**", "**TIME TO _PARTY_**", "Everybody loves EMOJIS"
};

public FunModule(IDiscordModuleService moduleService) : base(moduleService)
{
}

[Command("emojiparty")]
[Summary("Throws an emoji party!")]
[SlashCommand("emojiparty", "Throws an emoji party!")]
public async Task ThrowEmojiParty()
{
var emojisToPartyWith = _emojiPartyEmojis.Distinct().Shuffle().Take(20);

foreach (var emoji in emojisToPartyWith)
{
await Context.Message.AddReactionAsync(new Emoji(emoji));
}
await RespondAsync(_partyTimeMessages.Shuffle().First());

var partyTimeMessage = await GetOriginalResponseAsync();
await partyTimeMessage.AddReactionsAsync(emojisToPartyWith.Select(emoji => new Emoji(emoji)));
}

[Command("lamp")]
[Summary("Announces that the lamp has spoken, thus ending arguments.")]
[SlashCommand("lamp", "Announces that the lamp has spoken, thus ending arguments.")]
public async Task TheLampHasSpoken()
{
await ReplyAsync("https://raw.githubusercontent.com/flyingfisch/FischBotDiscord-csharp/master/assets/the-lamp-has-spoken.png");
await RespondAsync("https://raw.githubusercontent.com/flyingfisch/FischBotDiscord-csharp/master/assets/the-lamp-has-spoken.png");
}
}
}
Loading

0 comments on commit 625a545

Please sign in to comment.