Add a scaffold for a command

This commit is contained in:
Gender Shrapnel 2023-10-13 19:50:54 +02:00
parent ce0185c6a2
commit 117c980240
Signed by: modzero
SSH Key Fingerprint: SHA256:hsF2onMcqHsX09jLIFn7GvltdH9NTfFd/1tCnBNfQ4g
6 changed files with 223 additions and 60 deletions

3
.gitignore vendored
View File

@ -37,3 +37,6 @@ msbuild.wrn
# Visual Studio 2015 # Visual Studio 2015
.vs/ .vs/
# Local configuration
appsettings.Development.json

View File

@ -0,0 +1,33 @@
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace GodReplacementProduct;
public sealed class GodReplacementBot : IHostedService
{
private readonly IConfiguration _config;
private readonly DiscordSocketClient _client;
public GodReplacementBot(
IConfiguration config,
DiscordSocketClient client
)
{
_config = config;
_client = client;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var token = _config.GetValue<string>("GodReplacementProject:DiscordToken");
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await _client.StopAsync();
}
}

View File

@ -0,0 +1,32 @@
using Discord;
using Microsoft.Extensions.Logging;
namespace GodReplacementProduct;
public class GodReplacementLogger
{
private readonly ILogger<GodReplacementLogger> _logger;
public GodReplacementLogger(ILogger<GodReplacementLogger> logger)
{
_logger = logger;
}
public Task LogAsync(LogMessage message)
{
var severity = message.Severity switch
{
LogSeverity.Critical => LogLevel.Critical,
LogSeverity.Error => LogLevel.Error,
LogSeverity.Warning => LogLevel.Warning,
LogSeverity.Info => LogLevel.Information,
LogSeverity.Verbose => LogLevel.Debug,
LogSeverity.Debug => LogLevel.Trace,
_ => LogLevel.Information,
};
_logger.Log(severity, message.Exception, "[{Source}] {Message}", message.Source, message.Message);
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,79 @@
using System.Reflection;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace GodReplacementProduct;
public class InteractionHandler : IHostedService {
private readonly DiscordSocketClient _client;
private readonly InteractionService _handler;
private readonly IServiceProvider _services;
private readonly ILogger<InteractionHandler> _logger;
public InteractionHandler(DiscordSocketClient client, InteractionService handler, IServiceProvider services, ILogger<InteractionHandler> logger)
{
_client = client;
_handler = handler;
_services = services;
_logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken) {
_client.Ready += ReadyAsync;
await _handler.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
_client.InteractionCreated += HandleInteraction;
}
private async Task ReadyAsync()
{
var options = _services.GetService<IOptions<DevelopmentOptions>>();
if (options is not null) {
_logger.LogInformation("Development mode, registering commands to guild {0}", options.Value.TestGuildId);
await _handler.RegisterCommandsToGuildAsync(options.Value.TestGuildId, true);
} else {
_logger.LogInformation("Registering commands globally");
await _handler.RegisterCommandsGloballyAsync(true);
}
}
private async Task HandleInteraction(SocketInteraction interaction)
{
try
{
// Create an execution context that matches the generic type parameter of your InteractionModuleBase<T> modules.
var context = new SocketInteractionContext(_client, interaction);
// Execute the incoming command.
var result = await _handler.ExecuteCommandAsync(context, _services);
if (!result.IsSuccess)
switch (result.Error)
{
case InteractionCommandError.UnmetPrecondition:
break;
default:
break;
}
}
catch
{
// If Slash Command execution fails it is most likely that the original interaction acknowledgement will persist. It is a good idea to delete the original
// response, or at least let the user know that something went wrong during the command execution.
if (interaction.Type is InteractionType.ApplicationCommand)
await interaction.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync());
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_client.Ready -= ReadyAsync;
_client.InteractionCreated -= HandleInteraction;
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,13 @@
using Discord.Interactions;
namespace GodReplacementProduct.Modules;
public class FactChecker : InteractionModuleBase<SocketInteractionContext>
{
[SlashCommand("set_fact", "Record a fact for posterity")]
public async Task SetFact(string fact_name, string fact_value) =>
await RespondAsync($"{fact_name} - {fact_value}", ephemeral: true);
}

View File

@ -1,9 +1,8 @@
using Discord; using Discord.Interactions;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
@ -38,67 +37,71 @@ public class Program
} }
public static IHost BuildHost(string[] args) => new HostBuilder() public static IHost BuildHost(string[] args) => new HostBuilder()
.ConfigureAppConfiguration((context) => context .ConfigureDefaults(args)
.AddUserSecrets<Program>() .ConfigureAppConfiguration((context, builder) =>
.Build() {
) builder
.ConfigureServices(services => services .AddEnvironmentVariables(prefix: "DC_")
.AddSingleton<DiscordSocketClient>() .AddEnvironmentVariables("DOTNET_")
.AddHostedService<GodReplacementBot>() .AddJsonFile("appsettings.json", optional: true)
) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
.UseSerilog((context, services, loggerConfiguration) => loggerConfiguration
if (context.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>();
}
builder.Build();
})
.ConfigureServices((context, services) =>
{
services
.AddSingleton<GodReplacementLogger>()
.AddSingleton(provider =>
{
var logger = provider.GetRequiredService<GodReplacementLogger>();
var client = new DiscordSocketClient();
client.Log += logger.LogAsync;
return client;
})
.AddSingleton(provider =>
{
var logger = provider.GetRequiredService<GodReplacementLogger>();
var interactions = new InteractionService(provider.GetService<DiscordSocketClient>());
interactions.Log += logger.LogAsync;
return interactions;
})
.AddHostedService<GodReplacementBot>()
.AddHostedService<InteractionHandler>();
if (context.HostingEnvironment.IsDevelopment())
{
services.Configure<DevelopmentOptions>(context.Configuration.GetSection(DevelopmentOptions.Development));
}
})
.UseSerilog((context, services, loggerConfiguration) =>
{
loggerConfiguration
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information);
if (context.HostingEnvironment.IsDevelopment()) {
loggerConfiguration.MinimumLevel.Debug();
}
loggerConfiguration
.ReadFrom.Configuration(context.Configuration) .ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Console()) .WriteTo.Console();
})
.Build(); .Build();
} }
public sealed class GodReplacementBot : IHostedService public class DevelopmentOptions
{ {
private readonly ILogger<GodReplacementBot> _logger; public const string Development = "Development";
private readonly IConfiguration _config;
private readonly DiscordSocketClient _client;
public GodReplacementBot(
ILogger<GodReplacementBot> logger,
IConfiguration config,
DiscordSocketClient client
)
{
_config = config;
_client = client;
_logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var token = _config.GetValue<string>("GodReplacementProject:DiscordToken");
_client.Log += LogAsync;
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await _client.StopAsync();
}
private Task LogAsync(LogMessage message) {
var severity = message.Severity switch
{
LogSeverity.Critical => LogLevel.Critical,
LogSeverity.Error => LogLevel.Error,
LogSeverity.Warning => LogLevel.Warning,
LogSeverity.Info => LogLevel.Information,
LogSeverity.Verbose => LogLevel.Debug,
LogSeverity.Debug => LogLevel.Trace,
_ => LogLevel.Information,
};
_logger.Log(severity, message.Exception, "[{Source}] {Message}", message.Source, message.Message);
return Task.CompletedTask;
}
public ulong TestGuildId { get; set; }
} }