diff --git a/src/Web/ApiError.cs b/src/Web/ApiError.cs index 3708d2c..cec8b74 100644 --- a/src/Web/ApiError.cs +++ b/src/Web/ApiError.cs @@ -1,7 +1,6 @@ -namespace Geekbot.Web +namespace Geekbot.Web; + +public record ApiError { - public class ApiError - { - public string Message { get; set; } - } + public string Message { get; set; } } \ No newline at end of file diff --git a/src/Web/Controllers/Callback/CallbackController.cs b/src/Web/Controllers/Callback/CallbackController.cs index 91cb6ad..7d05049 100644 --- a/src/Web/Controllers/Callback/CallbackController.cs +++ b/src/Web/Controllers/Callback/CallbackController.cs @@ -1,54 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; -using System.Threading.Tasks; using Geekbot.Core.GlobalSettings; using Microsoft.AspNetCore.Mvc; -namespace Geekbot.Web.Controllers.Callback +namespace Geekbot.Web.Controllers.Callback; + +[ApiController] +public class CallbackController : ControllerBase { - public class CallbackController : Controller + private readonly IGlobalSettings _globalSettings; + + public CallbackController(IGlobalSettings globalSettings) { - private readonly IGlobalSettings _globalSettings; + _globalSettings = globalSettings; + } - public CallbackController(IGlobalSettings globalSettings) + [Route("/callback")] + public async Task DoCallback([FromQuery] string code) + { + var token = ""; + using (var client = new HttpClient()) { - _globalSettings = globalSettings; - } - - [Route("/callback")] - public async Task DoCallback([FromQuery] string code) - { - var token = ""; - using (var client = new HttpClient()) + client.BaseAddress = new Uri("https://discordapp.com"); + var appId = _globalSettings.GetKey("DiscordApplicationId"); + var accessToken = _globalSettings.GetKey("OAuthToken"); + var callbackUrl = _globalSettings.GetKey("OAuthCallbackUrl"); + + var form = new Dictionary { - client.BaseAddress = new Uri("https://discordapp.com"); - var appId = _globalSettings.GetKey("DiscordApplicationId"); - var accessToken = _globalSettings.GetKey("OAuthToken"); - var callbackUrl = _globalSettings.GetKey("OAuthCallbackUrl"); + { "client_id", appId }, + { "client_secret", accessToken }, + { "grant_type", "authorization_code" }, + { "code", code }, + { "scope", "identify email guilds" }, + { "redirect_uri", callbackUrl } + }; - var form = new Dictionary - { - {"client_id", appId}, - {"client_secret", accessToken}, - {"grant_type", "authorization_code"}, - {"code", code}, - {"scope", "identify email guilds"}, - {"redirect_uri", callbackUrl} - }; + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); + var result = await client.PostAsync("/api/oauth2/token", new FormUrlEncodedContent(form)); + result.EnsureSuccessStatusCode(); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); - var result = await client.PostAsync("/api/oauth2/token", new FormUrlEncodedContent(form)); - result.EnsureSuccessStatusCode(); - - var stringResponse = await result.Content.ReadAsStringAsync(); - var responseData = JsonSerializer.Deserialize(stringResponse); - token = responseData.AccessToken; - } - - return new RedirectResult($"https://geekbot.pizzaandcoffee.rocks/login?token={token}", false); + var stringResponse = await result.Content.ReadAsStringAsync(); + var responseData = JsonSerializer.Deserialize(stringResponse); + token = responseData.AccessToken; } + + return new RedirectResult($"https://geekbot.pizzaandcoffee.rocks/login?token={token}", false); } } \ No newline at end of file diff --git a/src/Web/Controllers/Callback/CallbackTokenResponse.cs b/src/Web/Controllers/Callback/CallbackTokenResponse.cs new file mode 100644 index 0000000..b2e21db --- /dev/null +++ b/src/Web/Controllers/Callback/CallbackTokenResponse.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Geekbot.Web.Controllers.Callback; + +public record CallbackTokenResponse +{ + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } + + [JsonPropertyName("token_type")] + public string TokenType { get; set; } + + [JsonPropertyName("expires_in")] + public int ExpiresIn { get; set; } + + [JsonPropertyName("refresh_token")] + public string RefreshToken { get; set; } + + [JsonPropertyName("scope")] + public string Scope { get; set; } +} \ No newline at end of file diff --git a/src/Web/Controllers/Callback/CallbackTokenResponseDto.cs b/src/Web/Controllers/Callback/CallbackTokenResponseDto.cs deleted file mode 100644 index 3292300..0000000 --- a/src/Web/Controllers/Callback/CallbackTokenResponseDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Web.Controllers.Callback -{ - public class CallbackTokenResponseDto - { - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } - - [JsonPropertyName("token_type")] - public string TokenType { get; set; } - - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } - - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } - - [JsonPropertyName("scope")] - public string Scope { get; set; } - } -} \ No newline at end of file diff --git a/src/Web/Controllers/Commands/CommandController.cs b/src/Web/Controllers/Commands/CommandController.cs index e6c73e3..89a44d6 100644 --- a/src/Web/Controllers/Commands/CommandController.cs +++ b/src/Web/Controllers/Commands/CommandController.cs @@ -1,41 +1,40 @@ -using System.Linq; -using Discord.Commands; +using Discord.Commands; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; -namespace Geekbot.Web.Controllers.Commands -{ - [EnableCors("AllowSpecificOrigin")] - public class CommandController : Controller - { - private readonly CommandService _commands; +namespace Geekbot.Web.Controllers.Commands; - public CommandController(CommandService commands) - { - _commands = commands; - } - - [Route("/v1/commands")] - public IActionResult GetCommands() - { - var commandList = (from cmd in _commands.Commands - let cmdParamsObj = cmd.Parameters.Select(cmdParam => new CommandParamDto - { - Summary = cmdParam.Summary, - Default = cmdParam.DefaultValue?.ToString(), - Type = cmdParam.Type?.ToString() - }) - .ToList() - let param = string.Join(", !", cmd.Aliases) - select new CommandDto +[ApiController] +[EnableCors("AllowSpecificOrigin")] +public class CommandController : ControllerBase +{ + private readonly CommandService _commands; + + public CommandController(CommandService commands) + { + _commands = commands; + } + + [HttpGet("/v1/commands")] + public IActionResult GetCommands() + { + var commandList = (from cmd in _commands.Commands + let cmdParamsObj = cmd.Parameters.Select(cmdParam => new ResponseCommandParam { - Name = cmd.Name, - Summary = cmd.Summary, - IsAdminCommand = param.Contains("admin") || param.Contains("owner"), - Aliases = cmd.Aliases.ToList(), - Params = cmdParamsObj - }).ToList(); - return Ok(commandList.FindAll(e => !e.Aliases[0].StartsWith("owner"))); - } + Summary = cmdParam.Summary, + Default = cmdParam.DefaultValue?.ToString(), + Type = cmdParam.Type?.ToString() + }) + .ToList() + let param = string.Join(", !", cmd.Aliases) + select new ResponseCommand + { + Name = cmd.Name, + Summary = cmd.Summary, + IsAdminCommand = param.Contains("admin") || param.Contains("owner"), + Aliases = cmd.Aliases.ToList(), + Params = cmdParamsObj + }).ToList(); + return Ok(commandList.FindAll(e => !e.Aliases[0].StartsWith("owner"))); } } \ No newline at end of file diff --git a/src/Web/Controllers/Commands/CommandDto.cs b/src/Web/Controllers/Commands/CommandDto.cs deleted file mode 100644 index 7183a75..0000000 --- a/src/Web/Controllers/Commands/CommandDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Geekbot.Web.Controllers.Commands -{ - public class CommandDto - { - public string Name { get; set; } - public string Summary { get; set; } - public bool IsAdminCommand { get; set; } - public List Aliases { get; set; } - public List Params { get; set; } - } -} \ No newline at end of file diff --git a/src/Web/Controllers/Commands/CommandParamDto.cs b/src/Web/Controllers/Commands/CommandParamDto.cs deleted file mode 100644 index 77c9da6..0000000 --- a/src/Web/Controllers/Commands/CommandParamDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Geekbot.Web.Controllers.Commands -{ - public class CommandParamDto - { - public string Summary { get; set; } - public string Default { get; set; } - public string Type { get; set; } - } -} \ No newline at end of file diff --git a/src/Web/Controllers/Commands/ResponseCommand.cs b/src/Web/Controllers/Commands/ResponseCommand.cs new file mode 100644 index 0000000..76993da --- /dev/null +++ b/src/Web/Controllers/Commands/ResponseCommand.cs @@ -0,0 +1,10 @@ +namespace Geekbot.Web.Controllers.Commands; + +public record ResponseCommand +{ + public string Name { get; set; } + public string Summary { get; set; } + public bool IsAdminCommand { get; set; } + public List Aliases { get; set; } + public List Params { get; set; } +} \ No newline at end of file diff --git a/src/Web/Controllers/Commands/ResponseCommandParam.cs b/src/Web/Controllers/Commands/ResponseCommandParam.cs new file mode 100644 index 0000000..720a208 --- /dev/null +++ b/src/Web/Controllers/Commands/ResponseCommandParam.cs @@ -0,0 +1,8 @@ +namespace Geekbot.Web.Controllers.Commands; + +public record ResponseCommandParam +{ + public string Summary { get; set; } + public string Default { get; set; } + public string Type { get; set; } +} \ No newline at end of file diff --git a/src/Web/Controllers/Highscores/HighscoreController.cs b/src/Web/Controllers/Highscores/HighscoreController.cs index e990431..e7cd57f 100644 --- a/src/Web/Controllers/Highscores/HighscoreController.cs +++ b/src/Web/Controllers/Highscores/HighscoreController.cs @@ -1,56 +1,56 @@ -using System.Collections.Generic; using Geekbot.Core.Highscores; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; -namespace Geekbot.Web.Controllers.Highscores +namespace Geekbot.Web.Controllers.Highscores; + +[ApiController] +[EnableCors("AllowSpecificOrigin")] +public class HighscoreController : ControllerBase { - [EnableCors("AllowSpecificOrigin")] - public class HighscoreController : Controller + private readonly IHighscoreManager _highscoreManager; + + public HighscoreController(IHighscoreManager highscoreManager) { - private readonly IHighscoreManager _highscoreManager; + _highscoreManager = highscoreManager; + } - public HighscoreController(IHighscoreManager highscoreManager) + [HttpPost] + [Route("/v1/highscore")] + public IActionResult GetHighscores([FromBody] HighscoreControllerPostBody body) + { + if (!ModelState.IsValid || body == null) { - _highscoreManager = highscoreManager; + var error = new SerializableError(ModelState); + return BadRequest(error); } - [HttpPost] - [Route("/v1/highscore")] - public IActionResult GetHighscores([FromBody] HighscoreControllerPostBodyDto body) + Dictionary list; + try { - if (!ModelState.IsValid || body == null) - { - var error = new SerializableError(ModelState); - return BadRequest(error); - } - - Dictionary list; - try - { - list = _highscoreManager.GetHighscoresWithUserData(body.Type, body.GuildId, body.Amount); - } - catch (HighscoreListEmptyException) - { - return NotFound(new ApiError - { - Message = $"No {body.Type} found on this server" - }); - } - - var response = new List(); - var counter = 1; - foreach (var item in list) - { - response.Add(new HighscoreControllerReponseBody - { - count = item.Value, - rank = counter, - user = item.Key - }); - counter++; - } - return Ok(response); + list = _highscoreManager.GetHighscoresWithUserData(body.Type, body.GuildId, body.Amount); } + catch (HighscoreListEmptyException) + { + return NotFound(new ApiError + { + Message = $"No {body.Type} found on this server" + }); + } + + var response = new List(); + var counter = 1; + foreach (var item in list) + { + response.Add(new HighscoreControllerReponseBody + { + count = item.Value, + rank = counter, + user = item.Key + }); + counter++; + } + + return Ok(response); } } \ No newline at end of file diff --git a/src/Web/Controllers/Highscores/HighscoreControllerPostBody.cs b/src/Web/Controllers/Highscores/HighscoreControllerPostBody.cs new file mode 100644 index 0000000..a05a5f5 --- /dev/null +++ b/src/Web/Controllers/Highscores/HighscoreControllerPostBody.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using Geekbot.Core.Highscores; + +namespace Geekbot.Web.Controllers.Highscores; + +public record HighscoreControllerPostBody +{ + [Required] + public ulong GuildId { get; set; } + + public HighscoreTypes Type { get; } = HighscoreTypes.messages; + + [Range(1, 150)] + public int Amount { get; } = 50; +} \ No newline at end of file diff --git a/src/Web/Controllers/Highscores/HighscoreControllerPostBodyDto.cs b/src/Web/Controllers/Highscores/HighscoreControllerPostBodyDto.cs deleted file mode 100644 index c3fe6a8..0000000 --- a/src/Web/Controllers/Highscores/HighscoreControllerPostBodyDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Geekbot.Core.Highscores; - -namespace Geekbot.Web.Controllers.Highscores -{ - public class HighscoreControllerPostBodyDto - { - [Required] - public ulong GuildId { get; set; } - - public HighscoreTypes Type { get; } = HighscoreTypes.messages; - - [Range(1, 150)] - public int Amount { get; } = 50; - } -} \ No newline at end of file diff --git a/src/Web/Controllers/Highscores/HighscoreControllerReponseBody.cs b/src/Web/Controllers/Highscores/HighscoreControllerReponseBody.cs index 3cea17f..9f5e119 100644 --- a/src/Web/Controllers/Highscores/HighscoreControllerReponseBody.cs +++ b/src/Web/Controllers/Highscores/HighscoreControllerReponseBody.cs @@ -1,11 +1,12 @@ using Geekbot.Core.Highscores; -namespace Geekbot.Web.Controllers.Highscores +namespace Geekbot.Web.Controllers.Highscores; + +public record HighscoreControllerReponseBody { - public class HighscoreControllerReponseBody - { - public int rank { get; set; } - public HighscoreUserDto user { get; set; } - public int count { get; set; } - } + public int rank { get; set; } + + public HighscoreUserDto user { get; set; } + + public int count { get; set; } } \ No newline at end of file diff --git a/src/Web/Controllers/Interactions/InteractionController.cs b/src/Web/Controllers/Interactions/InteractionController.cs index 73c40eb..0f9cf4f 100644 --- a/src/Web/Controllers/Interactions/InteractionController.cs +++ b/src/Web/Controllers/Interactions/InteractionController.cs @@ -1,101 +1,99 @@ -using System; -using System.IO; using System.Text; using System.Text.Json; -using System.Threading.Tasks; using Geekbot.Core.GlobalSettings; using Geekbot.Core.Interactions; using Geekbot.Core.Interactions.Request; using Geekbot.Core.Interactions.Response; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Sodium; -namespace Geekbot.Web.Controllers.Interactions +namespace Geekbot.Web.Controllers.Interactions; + +[ApiController] +public class InteractionController : ControllerBase { - public class InteractionController : Controller + private readonly IInteractionCommandManager _interactionCommandManager; + private readonly byte[] _publicKeyBytes; + + public InteractionController(IGlobalSettings globalSettings, IInteractionCommandManager interactionCommandManager) { - private readonly IInteractionCommandManager _interactionCommandManager; - private readonly byte[] _publicKeyBytes; + _interactionCommandManager = interactionCommandManager; + var publicKey = globalSettings.GetKey("DiscordPublicKey"); + _publicKeyBytes = Convert.FromHexString(publicKey.AsSpan()); + } - public InteractionController(IGlobalSettings globalSettings, IInteractionCommandManager interactionCommandManager) + [HttpPost] + [Route("/interactions")] + public async Task HandleInteraction( + [FromHeader(Name = "X-Signature-Ed25519")] + string signature, + [FromHeader(Name = "X-Signature-Timestamp")] + string timestamp + ) + { + if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp)) { - _interactionCommandManager = interactionCommandManager; - var publicKey = globalSettings.GetKey("DiscordPublicKey"); - _publicKeyBytes = Convert.FromHexString(publicKey.AsSpan()); + return BadRequest(); } - [HttpPost] - [Route("/interactions")] - public async Task HandleInteraction( - [FromHeader(Name = "X-Signature-Ed25519")] string signature, - [FromHeader(Name = "X-Signature-Timestamp")] string timestamp - ) + Request.EnableBuffering(); + if (!(await HasValidSignature(signature, timestamp))) { - if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp)) - { - return BadRequest(); - } - - Request.EnableBuffering(); - if (!(await HasValidSignature(signature, timestamp))) - { - return Unauthorized(); - } - - if (Request.Body.CanSeek) Request.Body.Seek(0, SeekOrigin.Begin); - var interaction = await JsonSerializer.DeserializeAsync(Request.Body, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }).ConfigureAwait(false); - if (interaction is null) throw new JsonException("Failed to deserialize JSON body"); - - return (interaction.Type, interaction.Version) switch - { - (InteractionType.Ping, 1) => Ping(), - (InteractionType.ApplicationCommand, 1) => await ApplicationCommand(interaction), - (InteractionType.MessageComponent, 1) => MessageComponent(interaction), - _ => StatusCode(501) - }; + return Unauthorized(); } - private IActionResult Ping() + if (Request.Body.CanSeek) Request.Body.Seek(0, SeekOrigin.Begin); + var interaction = await JsonSerializer.DeserializeAsync(Request.Body, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }).ConfigureAwait(false); + if (interaction is null) throw new JsonException("Failed to deserialize JSON body"); + + return (interaction.Type, interaction.Version) switch { - var response = new InteractionResponse() - { - Type = InteractionResponseType.Pong, - }; - return Ok(response); - } + (InteractionType.Ping, 1) => Ping(), + (InteractionType.ApplicationCommand, 1) => await ApplicationCommand(interaction), + (InteractionType.MessageComponent, 1) => MessageComponent(interaction), + _ => StatusCode(501) + }; + } - private async Task ApplicationCommand(Interaction interaction) + private IActionResult Ping() + { + var response = new InteractionResponse() { - var result = await _interactionCommandManager.RunCommand(interaction); + Type = InteractionResponseType.Pong, + }; + return Ok(response); + } - if (result == null) - { - return StatusCode(501); - } + private async Task ApplicationCommand(Interaction interaction) + { + var result = await _interactionCommandManager.RunCommand(interaction); - return Ok(result); - } - - private IActionResult MessageComponent(Interaction interaction) + if (result == null) { return StatusCode(501); } - private async Task HasValidSignature(string signature, string timestamp) - { - var timestampBytes = Encoding.Default.GetBytes(timestamp); - var signatureBytes = Convert.FromHexString(signature.AsSpan()); + return Ok(result); + } - var memoryStream = new MemoryStream(); - await Request.Body.CopyToAsync(memoryStream).ConfigureAwait(false); - var body = memoryStream.ToArray(); + private IActionResult MessageComponent(Interaction interaction) + { + return StatusCode(501); + } - var timestampLength = timestampBytes.Length; - Array.Resize(ref timestampBytes, timestampLength + body.Length); - Array.Copy(body, 0, timestampBytes, timestampLength, body.Length); + private async Task HasValidSignature(string signature, string timestamp) + { + var timestampBytes = Encoding.Default.GetBytes(timestamp); + var signatureBytes = Convert.FromHexString(signature.AsSpan()); - return PublicKeyAuth.VerifyDetached(signatureBytes, timestampBytes, _publicKeyBytes); - } + var memoryStream = new MemoryStream(); + await Request.Body.CopyToAsync(memoryStream).ConfigureAwait(false); + var body = memoryStream.ToArray(); + + var timestampLength = timestampBytes.Length; + Array.Resize(ref timestampBytes, timestampLength + body.Length); + Array.Copy(body, 0, timestampBytes, timestampLength, body.Length); + + return PublicKeyAuth.VerifyDetached(signatureBytes, timestampBytes, _publicKeyBytes); } } \ No newline at end of file diff --git a/src/Web/Controllers/Interactions/InteractionRegistrarController.cs b/src/Web/Controllers/Interactions/InteractionRegistrarController.cs index 41c37ed..5673ca3 100644 --- a/src/Web/Controllers/Interactions/InteractionRegistrarController.cs +++ b/src/Web/Controllers/Interactions/InteractionRegistrarController.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Net.Http.Headers; -using System.Threading.Tasks; using Geekbot.Core; using Geekbot.Core.GlobalSettings; using Geekbot.Core.Interactions; @@ -11,129 +6,128 @@ using Geekbot.Core.Interactions.ApplicationCommand; using Geekbot.Core.Logger; using Geekbot.Web.Controllers.Interactions.Model; using Microsoft.AspNetCore.Mvc; -using Sentry.Protocol; -namespace Geekbot.Web.Controllers.Interactions +namespace Geekbot.Web.Controllers.Interactions; + +[ApiController] +public class InteractionRegistrarController : ControllerBase { - public class InteractionRegistrarController : Controller + private readonly IGeekbotLogger _logger; + private readonly IInteractionCommandManager _interactionCommandManager; + private readonly string _discordToken; + private readonly Uri _guildCommandUri; + + public InteractionRegistrarController(IGlobalSettings globalSettings, IGeekbotLogger logger, IInteractionCommandManager interactionCommandManager) { - private readonly IGeekbotLogger _logger; - private readonly IInteractionCommandManager _interactionCommandManager; - private readonly string _discordToken; - private readonly Uri _guildCommandUri; - - public InteractionRegistrarController(IGlobalSettings globalSettings, IGeekbotLogger logger, IInteractionCommandManager interactionCommandManager) + _logger = logger; + _interactionCommandManager = interactionCommandManager; + _discordToken = globalSettings.GetKey("DiscordToken"); + var applicationId = globalSettings.GetKey("DiscordApplicationId"); + var betaGuilds = new Dictionary() { - _logger = logger; - _interactionCommandManager = interactionCommandManager; - _discordToken = globalSettings.GetKey("DiscordToken"); - var applicationId = globalSettings.GetKey("DiscordApplicationId"); - var betaGuilds = new Dictionary() - { - { "171249478546882561", "93070552863870976" }, // Prod / Swiss Geeks - { "181092911243329537", "131827972083548160" }, // Dev / Rune's Playground - }; - _guildCommandUri = new Uri($"https://discord.com/api/v8/applications/{applicationId}/guilds/{betaGuilds[applicationId]}/commands"); - } - - [HttpPost] - [Route("/interactions/register")] - public async Task RegisterInteractions() - { - var registeredInteractions = await GetRegisteredInteractions(); - var operations = new InteractionRegistrationOperations(); + { "171249478546882561", "93070552863870976" }, // Prod / Swiss Geeks + { "181092911243329537", "131827972083548160" }, // Dev / Rune's Playground + }; + _guildCommandUri = new Uri($"https://discord.com/api/v8/applications/{applicationId}/guilds/{betaGuilds[applicationId]}/commands"); + } - foreach (var (_, command) in _interactionCommandManager.CommandsInfo) + [HttpPost] + [Route("/interactions/register")] + public async Task RegisterInteractions() + { + var registeredInteractions = await GetRegisteredInteractions(); + var operations = new InteractionRegistrationOperations(); + + foreach (var (_, command) in _interactionCommandManager.CommandsInfo) + { + var existing = registeredInteractions.FirstOrDefault(i => i.Name == command.Name); + if (existing == null) { - var existing = registeredInteractions.FirstOrDefault(i => i.Name == command.Name); - if (existing == null) - { - operations.Create.Add(command); - } - else - { - operations.Update.Add(existing.Id, command); - } + operations.Create.Add(command); } - - foreach (var registeredInteraction in registeredInteractions.Where(registeredInteraction => !_interactionCommandManager.CommandsInfo.Values.Any(c => c.Name == registeredInteraction.Name))) + else { - operations.Remove.Add(registeredInteraction.Id); - } - - await Task.WhenAll(new[] - { - Create(operations.Create), - Update(operations.Update), - Remove(operations.Remove) - }); - return Ok(operations); - } - - private HttpClient CreateClientWithToken() - { - var httpClient = HttpAbstractions.CreateDefaultClient(); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken); - - return httpClient; - } - - private async Task Create(List commands) - { - foreach (var command in commands) - { - try - { - await HttpAbstractions.Post(_guildCommandUri, command, CreateClientWithToken()); - _logger.Information(LogSource.Interaction, $"Registered interaction: {command.Name}"); - } - catch (Exception e) - { - _logger.Error(LogSource.Interaction, $"Failed to register interaction: {command.Name}", e); - } + operations.Update.Add(existing.Id, command); } } - private async Task Update(Dictionary commands) + foreach (var registeredInteraction in registeredInteractions.Where(registeredInteraction => !_interactionCommandManager.CommandsInfo.Values.Any(c => c.Name == registeredInteraction.Name))) { - foreach (var (id, command) in commands) - { - try - { - var updateUri = new Uri(_guildCommandUri.AbsoluteUri + $"/{id}"); - await HttpAbstractions.Patch(updateUri, command, CreateClientWithToken()); - _logger.Information(LogSource.Interaction, $"Updated Interaction: {command.Name}"); - } - catch (Exception e) - { - _logger.Error(LogSource.Interaction, $"Failed to update Interaction: {command.Name}", e); - } - } + operations.Remove.Add(registeredInteraction.Id); } - private async Task Remove(List commands) + await Task.WhenAll(new[] { - foreach (var id in commands) + Create(operations.Create), + Update(operations.Update), + Remove(operations.Remove) + }); + return Ok(operations); + } + + private HttpClient CreateClientWithToken() + { + var httpClient = HttpAbstractions.CreateDefaultClient(); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken); + + return httpClient; + } + + private async Task Create(List commands) + { + foreach (var command in commands) + { + try { - try - { - var updateUri = new Uri(_guildCommandUri.AbsoluteUri + $"/{id}"); - await HttpAbstractions.Delete(updateUri, CreateClientWithToken()); - _logger.Information(LogSource.Interaction, $"Deleted interaction with ID: {id}"); - } - catch (Exception e) - { - _logger.Error(LogSource.Interaction, $"Failed to delete interaction with id: {id}", e); - } + await HttpAbstractions.Post(_guildCommandUri, command, CreateClientWithToken()); + _logger.Information(LogSource.Interaction, $"Registered interaction: {command.Name}"); + } + catch (Exception e) + { + _logger.Error(LogSource.Interaction, $"Failed to register interaction: {command.Name}", e); } - } - - private async Task> GetRegisteredInteractions() - { - var httpClient = HttpAbstractions.CreateDefaultClient(); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken); - - return await HttpAbstractions.Get>(_guildCommandUri, httpClient); } } + + private async Task Update(Dictionary commands) + { + foreach (var (id, command) in commands) + { + try + { + var updateUri = new Uri(_guildCommandUri.AbsoluteUri + $"/{id}"); + await HttpAbstractions.Patch(updateUri, command, CreateClientWithToken()); + _logger.Information(LogSource.Interaction, $"Updated Interaction: {command.Name}"); + } + catch (Exception e) + { + _logger.Error(LogSource.Interaction, $"Failed to update Interaction: {command.Name}", e); + } + } + } + + private async Task Remove(List commands) + { + foreach (var id in commands) + { + try + { + var updateUri = new Uri(_guildCommandUri.AbsoluteUri + $"/{id}"); + await HttpAbstractions.Delete(updateUri, CreateClientWithToken()); + _logger.Information(LogSource.Interaction, $"Deleted interaction with ID: {id}"); + } + catch (Exception e) + { + _logger.Error(LogSource.Interaction, $"Failed to delete interaction with id: {id}", e); + } + } + } + + private async Task> GetRegisteredInteractions() + { + var httpClient = HttpAbstractions.CreateDefaultClient(); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken); + + return await HttpAbstractions.Get>(_guildCommandUri, httpClient); + } } \ No newline at end of file diff --git a/src/Web/Controllers/Interactions/Model/InteractionRegistrationOperations.cs b/src/Web/Controllers/Interactions/Model/InteractionRegistrationOperations.cs index 97dabd0..2eb6cd5 100644 --- a/src/Web/Controllers/Interactions/Model/InteractionRegistrationOperations.cs +++ b/src/Web/Controllers/Interactions/Model/InteractionRegistrationOperations.cs @@ -1,12 +1,12 @@ -using System.Collections.Generic; using Geekbot.Core.Interactions.ApplicationCommand; -namespace Geekbot.Web.Controllers.Interactions.Model +namespace Geekbot.Web.Controllers.Interactions.Model; + +public record InteractionRegistrationOperations { - public record InteractionRegistrationOperations - { - public List Create { get; set; } = new(); - public Dictionary Update { get; set; } = new(); - public List Remove { get; set; } = new(); - } + public List Create { get; set; } = new(); + + public Dictionary Update { get; set; } = new(); + + public List Remove { get; set; } = new(); } \ No newline at end of file diff --git a/src/Web/Controllers/Interactions/Model/RegisteredInteraction.cs b/src/Web/Controllers/Interactions/Model/RegisteredInteraction.cs index 63586bc..702f75f 100644 --- a/src/Web/Controllers/Interactions/Model/RegisteredInteraction.cs +++ b/src/Web/Controllers/Interactions/Model/RegisteredInteraction.cs @@ -1,32 +1,31 @@ using System.Text.Json.Serialization; using Geekbot.Core.Interactions.Request; -namespace Geekbot.Web.Controllers.Interactions.Model +namespace Geekbot.Web.Controllers.Interactions.Model; + +public record RegisteredInteraction { - public record RegisteredInteraction - { - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("application_id")] - public string ApplicationId { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("version")] - public string Version { get; set; } - - [JsonPropertyName("default_permission")] - public bool DefaultPermission { get; set; } - - [JsonPropertyName("type")] - public InteractionType Type { get; set; } - - [JsonPropertyName("guild_id")] - public string GuildId { get; set; } - } + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("application_id")] + public string ApplicationId { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("version")] + public string Version { get; set; } + + [JsonPropertyName("default_permission")] + public bool DefaultPermission { get; set; } + + [JsonPropertyName("type")] + public InteractionType Type { get; set; } + + [JsonPropertyName("guild_id")] + public string GuildId { get; set; } } \ No newline at end of file diff --git a/src/Web/Controllers/KillController.cs b/src/Web/Controllers/KillController.cs index 2492b0d..895a9b1 100644 --- a/src/Web/Controllers/KillController.cs +++ b/src/Web/Controllers/KillController.cs @@ -1,64 +1,63 @@ -using System; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Geekbot.Core; using Geekbot.Core.GlobalSettings; using Microsoft.AspNetCore.Mvc; -namespace Geekbot.Web.Controllers +namespace Geekbot.Web.Controllers; + +/* + * Sometimes for some unknown reason command responses may become incredibly slow. + * Because i don't have time to debug it and may not always know directly when it happens, + * i want to give some trusted people the possibility to restart the bot. + * The kill code must be set manually in the db, it should end it `---1' + * + * ToDo: Actually fix the underlying issue + */ + +[ApiController] +public class KillController : ControllerBase { - /* - * Sometimes for some unknown reason command responses may become incredibly slow. - * Because i don't have time to debug it and may not always know directly when it happens, - * i want to give some trusted people the possibility to restart the bot. - * The kill code must be set manually in the db, it should end it `---1' - * - * ToDo: Actually fix the underlying issue - */ - public class KillController : Controller + private readonly IGlobalSettings _globalSettings; + private const string KillCodeDbKey = "KillCode"; + + public KillController(IGlobalSettings globalSettings) { - private readonly IGlobalSettings _globalSettings; - private const string KillCodeDbKey = "KillCode"; + _globalSettings = globalSettings; + } - public KillController(IGlobalSettings globalSettings) + [HttpGet("/v1/kill/{providedKillCode}")] + public async Task KillTheBot([FromRoute] string providedKillCode) + { + var killCode = _globalSettings.GetKey(KillCodeDbKey); + if (providedKillCode != killCode) { - _globalSettings = globalSettings; + return Unauthorized(new ApiError { Message = $"Go Away ({GetKillCodeInt(killCode)})" }); } - [HttpGet("/v1/kill/{providedKillCode}")] - public async Task KillTheBot([FromRoute] string providedKillCode) - { - var killCode = _globalSettings.GetKey(KillCodeDbKey); - if (providedKillCode != killCode) - { - return Unauthorized(new ApiError { Message = $"Go Away ({GetKillCodeInt(killCode)})" }); - } - - await UpdateKillCode(killCode); + await UpdateKillCode(killCode); #pragma warning disable CS4014 - Kill(); + Kill(); #pragma warning restore CS4014 - return Ok(); - } + return Ok(); + } - private int GetKillCodeInt(string code) - { - var group = Regex.Match(code, @".+(\-\-\-(?\d+))").Groups["int"]; - return group.Success ? int.Parse(group.Value) : 0; - } - - private async Task UpdateKillCode(string oldCode) - { - var newKillCodeInt = new Random().Next(1, 100); - var newCode = oldCode.Replace($"---{GetKillCodeInt(oldCode)}", $"---{newKillCodeInt}"); - await _globalSettings.SetKey(KillCodeDbKey, newCode); - } + private int GetKillCodeInt(string code) + { + var group = Regex.Match(code, @".+(\-\-\-(?\d+))").Groups["int"]; + return group.Success ? int.Parse(group.Value) : 0; + } - private async Task Kill() - { - // wait a second so the http response can still be sent successfully - await Task.Delay(1000); - Environment.Exit(GeekbotExitCode.KilledByApiCall.GetHashCode()); - } + private async Task UpdateKillCode(string oldCode) + { + var newKillCodeInt = new Random().Next(1, 100); + var newCode = oldCode.Replace($"---{GetKillCodeInt(oldCode)}", $"---{newKillCodeInt}"); + await _globalSettings.SetKey(KillCodeDbKey, newCode); + } + + private async Task Kill() + { + // wait a second so the http response can still be sent successfully + await Task.Delay(1000); + Environment.Exit(GeekbotExitCode.KilledByApiCall.GetHashCode()); } } \ No newline at end of file diff --git a/src/Web/Controllers/Status/ApiStatus.cs b/src/Web/Controllers/Status/ApiStatus.cs new file mode 100644 index 0000000..50b9500 --- /dev/null +++ b/src/Web/Controllers/Status/ApiStatus.cs @@ -0,0 +1,10 @@ +namespace Geekbot.Web.Controllers.Status; + +public record ApiStatus +{ + public string GeekbotVersion { get; set; } + + public string ApiVersion { get; set; } + + public string Status { get; set; } +} \ No newline at end of file diff --git a/src/Web/Controllers/Status/ApiStatusDto.cs b/src/Web/Controllers/Status/ApiStatusDto.cs deleted file mode 100644 index cb2e5c7..0000000 --- a/src/Web/Controllers/Status/ApiStatusDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Geekbot.Web.Controllers.Status -{ - public class ApiStatusDto - { - public string GeekbotVersion { get; set; } - public string ApiVersion { get; set; } - public string Status { get; set; } - } -} \ No newline at end of file diff --git a/src/Web/Controllers/Status/StatusController.cs b/src/Web/Controllers/Status/StatusController.cs index 881462a..fe8a5d0 100644 --- a/src/Web/Controllers/Status/StatusController.cs +++ b/src/Web/Controllers/Status/StatusController.cs @@ -3,21 +3,21 @@ using Geekbot.Core; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; -namespace Geekbot.Web.Controllers.Status +namespace Geekbot.Web.Controllers.Status; + +[ApiController] +[EnableCors("AllowSpecificOrigin")] +public class StatusController : ControllerBase { - [EnableCors("AllowSpecificOrigin")] - public class StatusController : Controller + [Route("/")] + public IActionResult GetCommands() { - [Route("/")] - public IActionResult GetCommands() + var responseBody = new ApiStatus { - var responseBody = new ApiStatusDto - { - GeekbotVersion = Constants.BotVersion(), - ApiVersion = Constants.ApiVersion.ToString(CultureInfo.InvariantCulture), - Status = "Online" - }; - return Ok(responseBody); - } + GeekbotVersion = Constants.BotVersion(), + ApiVersion = Constants.ApiVersion.ToString(CultureInfo.InvariantCulture), + Status = "Online" + }; + return Ok(responseBody); } -} \ No newline at end of file +}