Refactor WebApi Controllers to take advantage of new .net6 features
This commit is contained in:
parent
6b3a3a9ec2
commit
e01a066920
21 changed files with 452 additions and 472 deletions
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,54 +1,50 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Geekbot.Core.GlobalSettings;
|
using Geekbot.Core.GlobalSettings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
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<IActionResult> DoCallback([FromQuery] string code)
|
||||||
|
{
|
||||||
|
var token = "";
|
||||||
|
using (var client = new HttpClient())
|
||||||
{
|
{
|
||||||
_globalSettings = globalSettings;
|
client.BaseAddress = new Uri("https://discordapp.com");
|
||||||
}
|
var appId = _globalSettings.GetKey("DiscordApplicationId");
|
||||||
|
var accessToken = _globalSettings.GetKey("OAuthToken");
|
||||||
|
var callbackUrl = _globalSettings.GetKey("OAuthCallbackUrl");
|
||||||
|
|
||||||
[Route("/callback")]
|
var form = new Dictionary<string, string>
|
||||||
public async Task<IActionResult> DoCallback([FromQuery] string code)
|
|
||||||
{
|
|
||||||
var token = "";
|
|
||||||
using (var client = new HttpClient())
|
|
||||||
{
|
{
|
||||||
client.BaseAddress = new Uri("https://discordapp.com");
|
{ "client_id", appId },
|
||||||
var appId = _globalSettings.GetKey("DiscordApplicationId");
|
{ "client_secret", accessToken },
|
||||||
var accessToken = _globalSettings.GetKey("OAuthToken");
|
{ "grant_type", "authorization_code" },
|
||||||
var callbackUrl = _globalSettings.GetKey("OAuthCallbackUrl");
|
{ "code", code },
|
||||||
|
{ "scope", "identify email guilds" },
|
||||||
|
{ "redirect_uri", callbackUrl }
|
||||||
|
};
|
||||||
|
|
||||||
var form = new Dictionary<string, string>
|
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
|
||||||
{
|
var result = await client.PostAsync("/api/oauth2/token", new FormUrlEncodedContent(form));
|
||||||
{"client_id", appId},
|
result.EnsureSuccessStatusCode();
|
||||||
{"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 stringResponse = await result.Content.ReadAsStringAsync();
|
||||||
var result = await client.PostAsync("/api/oauth2/token", new FormUrlEncodedContent(form));
|
var responseData = JsonSerializer.Deserialize<CallbackTokenResponse>(stringResponse);
|
||||||
result.EnsureSuccessStatusCode();
|
token = responseData.AccessToken;
|
||||||
|
|
||||||
var stringResponse = await result.Content.ReadAsStringAsync();
|
|
||||||
var responseData = JsonSerializer.Deserialize<CallbackTokenResponseDto>(stringResponse);
|
|
||||||
token = responseData.AccessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RedirectResult($"https://geekbot.pizzaandcoffee.rocks/login?token={token}", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new RedirectResult($"https://geekbot.pizzaandcoffee.rocks/login?token={token}", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
21
src/Web/Controllers/Callback/CallbackTokenResponse.cs
Normal file
21
src/Web/Controllers/Callback/CallbackTokenResponse.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +1,40 @@
|
||||||
using System.Linq;
|
using Discord.Commands;
|
||||||
using Discord.Commands;
|
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Geekbot.Web.Controllers.Commands
|
namespace Geekbot.Web.Controllers.Commands;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[EnableCors("AllowSpecificOrigin")]
|
||||||
|
public class CommandController : ControllerBase
|
||||||
{
|
{
|
||||||
[EnableCors("AllowSpecificOrigin")]
|
private readonly CommandService _commands;
|
||||||
public class CommandController : Controller
|
|
||||||
|
public CommandController(CommandService commands)
|
||||||
{
|
{
|
||||||
private readonly CommandService _commands;
|
_commands = commands;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandController(CommandService commands)
|
[HttpGet("/v1/commands")]
|
||||||
{
|
public IActionResult GetCommands()
|
||||||
_commands = commands;
|
{
|
||||||
}
|
var commandList = (from cmd in _commands.Commands
|
||||||
|
let cmdParamsObj = cmd.Parameters.Select(cmdParam => new ResponseCommandParam
|
||||||
[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
|
|
||||||
{
|
{
|
||||||
Name = cmd.Name,
|
Summary = cmdParam.Summary,
|
||||||
Summary = cmd.Summary,
|
Default = cmdParam.DefaultValue?.ToString(),
|
||||||
IsAdminCommand = param.Contains("admin") || param.Contains("owner"),
|
Type = cmdParam.Type?.ToString()
|
||||||
Aliases = cmd.Aliases.ToList(),
|
})
|
||||||
Params = cmdParamsObj
|
.ToList()
|
||||||
}).ToList();
|
let param = string.Join(", !", cmd.Aliases)
|
||||||
return Ok(commandList.FindAll(e => !e.Aliases[0].StartsWith("owner")));
|
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")));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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<string> Aliases { get; set; }
|
|
||||||
public List<CommandParamDto> Params { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
10
src/Web/Controllers/Commands/ResponseCommand.cs
Normal file
10
src/Web/Controllers/Commands/ResponseCommand.cs
Normal file
|
@ -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<string> Aliases { get; set; }
|
||||||
|
public List<ResponseCommandParam> Params { get; set; }
|
||||||
|
}
|
8
src/Web/Controllers/Commands/ResponseCommandParam.cs
Normal file
8
src/Web/Controllers/Commands/ResponseCommandParam.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
|
@ -1,56 +1,56 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Geekbot.Core.Highscores;
|
using Geekbot.Core.Highscores;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Geekbot.Web.Controllers.Highscores
|
namespace Geekbot.Web.Controllers.Highscores;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[EnableCors("AllowSpecificOrigin")]
|
||||||
|
public class HighscoreController : ControllerBase
|
||||||
{
|
{
|
||||||
[EnableCors("AllowSpecificOrigin")]
|
private readonly IHighscoreManager _highscoreManager;
|
||||||
public class HighscoreController : Controller
|
|
||||||
|
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]
|
Dictionary<HighscoreUserDto, int> list;
|
||||||
[Route("/v1/highscore")]
|
try
|
||||||
public IActionResult GetHighscores([FromBody] HighscoreControllerPostBodyDto body)
|
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid || body == null)
|
list = _highscoreManager.GetHighscoresWithUserData(body.Type, body.GuildId, body.Amount);
|
||||||
{
|
|
||||||
var error = new SerializableError(ModelState);
|
|
||||||
return BadRequest(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<HighscoreUserDto, int> 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<HighscoreControllerReponseBody>();
|
|
||||||
var counter = 1;
|
|
||||||
foreach (var item in list)
|
|
||||||
{
|
|
||||||
response.Add(new HighscoreControllerReponseBody
|
|
||||||
{
|
|
||||||
count = item.Value,
|
|
||||||
rank = counter,
|
|
||||||
user = item.Key
|
|
||||||
});
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
return Ok(response);
|
|
||||||
}
|
}
|
||||||
|
catch (HighscoreListEmptyException)
|
||||||
|
{
|
||||||
|
return NotFound(new ApiError
|
||||||
|
{
|
||||||
|
Message = $"No {body.Type} found on this server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = new List<HighscoreControllerReponseBody>();
|
||||||
|
var counter = 1;
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
response.Add(new HighscoreControllerReponseBody
|
||||||
|
{
|
||||||
|
count = item.Value,
|
||||||
|
rank = counter,
|
||||||
|
user = item.Key
|
||||||
|
});
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,12 @@
|
||||||
using Geekbot.Core.Highscores;
|
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 int rank { get; set; }
|
public HighscoreUserDto user { get; set; }
|
||||||
public HighscoreUserDto user { get; set; }
|
|
||||||
public int count { get; set; }
|
public int count { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,101 +1,99 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Geekbot.Core.GlobalSettings;
|
using Geekbot.Core.GlobalSettings;
|
||||||
using Geekbot.Core.Interactions;
|
using Geekbot.Core.Interactions;
|
||||||
using Geekbot.Core.Interactions.Request;
|
using Geekbot.Core.Interactions.Request;
|
||||||
using Geekbot.Core.Interactions.Response;
|
using Geekbot.Core.Interactions.Response;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Sodium;
|
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;
|
_interactionCommandManager = interactionCommandManager;
|
||||||
private readonly byte[] _publicKeyBytes;
|
var publicKey = globalSettings.GetKey("DiscordPublicKey");
|
||||||
|
_publicKeyBytes = Convert.FromHexString(publicKey.AsSpan());
|
||||||
|
}
|
||||||
|
|
||||||
public InteractionController(IGlobalSettings globalSettings, IInteractionCommandManager interactionCommandManager)
|
[HttpPost]
|
||||||
|
[Route("/interactions")]
|
||||||
|
public async Task<IActionResult> HandleInteraction(
|
||||||
|
[FromHeader(Name = "X-Signature-Ed25519")]
|
||||||
|
string signature,
|
||||||
|
[FromHeader(Name = "X-Signature-Timestamp")]
|
||||||
|
string timestamp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp))
|
||||||
{
|
{
|
||||||
_interactionCommandManager = interactionCommandManager;
|
return BadRequest();
|
||||||
var publicKey = globalSettings.GetKey("DiscordPublicKey");
|
|
||||||
_publicKeyBytes = Convert.FromHexString(publicKey.AsSpan());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
Request.EnableBuffering();
|
||||||
[Route("/interactions")]
|
if (!(await HasValidSignature(signature, timestamp)))
|
||||||
public async Task<IActionResult> HandleInteraction(
|
|
||||||
[FromHeader(Name = "X-Signature-Ed25519")] string signature,
|
|
||||||
[FromHeader(Name = "X-Signature-Timestamp")] string timestamp
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp))
|
return Unauthorized();
|
||||||
{
|
|
||||||
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<Interaction>(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)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IActionResult Ping()
|
if (Request.Body.CanSeek) Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
var interaction = await JsonSerializer.DeserializeAsync<Interaction>(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()
|
(InteractionType.Ping, 1) => Ping(),
|
||||||
{
|
(InteractionType.ApplicationCommand, 1) => await ApplicationCommand(interaction),
|
||||||
Type = InteractionResponseType.Pong,
|
(InteractionType.MessageComponent, 1) => MessageComponent(interaction),
|
||||||
};
|
_ => StatusCode(501)
|
||||||
return Ok(response);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> 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)
|
private async Task<IActionResult> ApplicationCommand(Interaction interaction)
|
||||||
{
|
{
|
||||||
return StatusCode(501);
|
var result = await _interactionCommandManager.RunCommand(interaction);
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(result);
|
if (result == null)
|
||||||
}
|
|
||||||
|
|
||||||
private IActionResult MessageComponent(Interaction interaction)
|
|
||||||
{
|
{
|
||||||
return StatusCode(501);
|
return StatusCode(501);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> HasValidSignature(string signature, string timestamp)
|
return Ok(result);
|
||||||
{
|
}
|
||||||
var timestampBytes = Encoding.Default.GetBytes(timestamp);
|
|
||||||
var signatureBytes = Convert.FromHexString(signature.AsSpan());
|
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
private IActionResult MessageComponent(Interaction interaction)
|
||||||
await Request.Body.CopyToAsync(memoryStream).ConfigureAwait(false);
|
{
|
||||||
var body = memoryStream.ToArray();
|
return StatusCode(501);
|
||||||
|
}
|
||||||
|
|
||||||
var timestampLength = timestampBytes.Length;
|
private async Task<bool> HasValidSignature(string signature, string timestamp)
|
||||||
Array.Resize(ref timestampBytes, timestampLength + body.Length);
|
{
|
||||||
Array.Copy(body, 0, timestampBytes, timestampLength, body.Length);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Geekbot.Core;
|
using Geekbot.Core;
|
||||||
using Geekbot.Core.GlobalSettings;
|
using Geekbot.Core.GlobalSettings;
|
||||||
using Geekbot.Core.Interactions;
|
using Geekbot.Core.Interactions;
|
||||||
|
@ -11,129 +6,128 @@ using Geekbot.Core.Interactions.ApplicationCommand;
|
||||||
using Geekbot.Core.Logger;
|
using Geekbot.Core.Logger;
|
||||||
using Geekbot.Web.Controllers.Interactions.Model;
|
using Geekbot.Web.Controllers.Interactions.Model;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
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;
|
_logger = logger;
|
||||||
private readonly IInteractionCommandManager _interactionCommandManager;
|
_interactionCommandManager = interactionCommandManager;
|
||||||
private readonly string _discordToken;
|
_discordToken = globalSettings.GetKey("DiscordToken");
|
||||||
private readonly Uri _guildCommandUri;
|
var applicationId = globalSettings.GetKey("DiscordApplicationId");
|
||||||
|
var betaGuilds = new Dictionary<string, string>()
|
||||||
public InteractionRegistrarController(IGlobalSettings globalSettings, IGeekbotLogger logger, IInteractionCommandManager interactionCommandManager)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
{ "171249478546882561", "93070552863870976" }, // Prod / Swiss Geeks
|
||||||
_interactionCommandManager = interactionCommandManager;
|
{ "181092911243329537", "131827972083548160" }, // Dev / Rune's Playground
|
||||||
_discordToken = globalSettings.GetKey("DiscordToken");
|
};
|
||||||
var applicationId = globalSettings.GetKey("DiscordApplicationId");
|
_guildCommandUri = new Uri($"https://discord.com/api/v8/applications/{applicationId}/guilds/{betaGuilds[applicationId]}/commands");
|
||||||
var betaGuilds = new Dictionary<string, string>()
|
}
|
||||||
{
|
|
||||||
{ "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]
|
[HttpPost]
|
||||||
[Route("/interactions/register")]
|
[Route("/interactions/register")]
|
||||||
public async Task<IActionResult> RegisterInteractions()
|
public async Task<IActionResult> RegisterInteractions()
|
||||||
|
{
|
||||||
|
var registeredInteractions = await GetRegisteredInteractions();
|
||||||
|
var operations = new InteractionRegistrationOperations();
|
||||||
|
|
||||||
|
foreach (var (_, command) in _interactionCommandManager.CommandsInfo)
|
||||||
{
|
{
|
||||||
var registeredInteractions = await GetRegisteredInteractions();
|
var existing = registeredInteractions.FirstOrDefault(i => i.Name == command.Name);
|
||||||
var operations = new InteractionRegistrationOperations();
|
if (existing == null)
|
||||||
|
|
||||||
foreach (var (_, command) in _interactionCommandManager.CommandsInfo)
|
|
||||||
{
|
{
|
||||||
var existing = registeredInteractions.FirstOrDefault(i => i.Name == command.Name);
|
operations.Create.Add(command);
|
||||||
if (existing == null)
|
|
||||||
{
|
|
||||||
operations.Create.Add(command);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operations.Update.Add(existing.Id, command);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
foreach (var registeredInteraction in registeredInteractions.Where(registeredInteraction => !_interactionCommandManager.CommandsInfo.Values.Any(c => c.Name == registeredInteraction.Name)))
|
|
||||||
{
|
{
|
||||||
operations.Remove.Add(registeredInteraction.Id);
|
operations.Update.Add(existing.Id, command);
|
||||||
}
|
|
||||||
|
|
||||||
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<Command> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Update(Dictionary<string, Command> commands)
|
foreach (var registeredInteraction in registeredInteractions.Where(registeredInteraction => !_interactionCommandManager.CommandsInfo.Values.Any(c => c.Name == registeredInteraction.Name)))
|
||||||
{
|
{
|
||||||
foreach (var (id, command) in commands)
|
operations.Remove.Add(registeredInteraction.Id);
|
||||||
{
|
|
||||||
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<string> 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<Command> commands)
|
||||||
|
{
|
||||||
|
foreach (var command in commands)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
await HttpAbstractions.Post(_guildCommandUri, command, CreateClientWithToken());
|
||||||
{
|
_logger.Information(LogSource.Interaction, $"Registered interaction: {command.Name}");
|
||||||
var updateUri = new Uri(_guildCommandUri.AbsoluteUri + $"/{id}");
|
}
|
||||||
await HttpAbstractions.Delete(updateUri, CreateClientWithToken());
|
catch (Exception e)
|
||||||
_logger.Information(LogSource.Interaction, $"Deleted interaction with ID: {id}");
|
{
|
||||||
}
|
_logger.Error(LogSource.Interaction, $"Failed to register interaction: {command.Name}", e);
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.Error(LogSource.Interaction, $"Failed to delete interaction with id: {id}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<List<RegisteredInteraction>> GetRegisteredInteractions()
|
|
||||||
{
|
|
||||||
var httpClient = HttpAbstractions.CreateDefaultClient();
|
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken);
|
|
||||||
|
|
||||||
return await HttpAbstractions.Get<List<RegisteredInteraction>>(_guildCommandUri, httpClient);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Update(Dictionary<string, Command> 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<string> 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<List<RegisteredInteraction>> GetRegisteredInteractions()
|
||||||
|
{
|
||||||
|
var httpClient = HttpAbstractions.CreateDefaultClient();
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bot", _discordToken);
|
||||||
|
|
||||||
|
return await HttpAbstractions.Get<List<RegisteredInteraction>>(_guildCommandUri, httpClient);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Geekbot.Core.Interactions.ApplicationCommand;
|
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<Command> Create { get; set; } = new();
|
||||||
{
|
|
||||||
public List<Command> Create { get; set; } = new();
|
public Dictionary<string, Command> Update { get; set; } = new();
|
||||||
public Dictionary<string, Command> Update { get; set; } = new();
|
|
||||||
public List<string> Remove { get; set; } = new();
|
public List<string> Remove { get; set; } = new();
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,32 +1,31 @@
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Geekbot.Core.Interactions.Request;
|
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("id")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("application_id")]
|
[JsonPropertyName("application_id")]
|
||||||
public string ApplicationId { get; set; }
|
public string ApplicationId { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("description")]
|
[JsonPropertyName("description")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("version")]
|
[JsonPropertyName("version")]
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("default_permission")]
|
[JsonPropertyName("default_permission")]
|
||||||
public bool DefaultPermission { get; set; }
|
public bool DefaultPermission { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("type")]
|
[JsonPropertyName("type")]
|
||||||
public InteractionType Type { get; set; }
|
public InteractionType Type { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("guild_id")]
|
[JsonPropertyName("guild_id")]
|
||||||
public string GuildId { get; set; }
|
public string GuildId { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,64 +1,63 @@
|
||||||
using System;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Geekbot.Core;
|
using Geekbot.Core;
|
||||||
using Geekbot.Core.GlobalSettings;
|
using Geekbot.Core.GlobalSettings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
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
|
||||||
{
|
{
|
||||||
/*
|
private readonly IGlobalSettings _globalSettings;
|
||||||
* Sometimes for some unknown reason command responses may become incredibly slow.
|
private const string KillCodeDbKey = "KillCode";
|
||||||
* 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.
|
public KillController(IGlobalSettings globalSettings)
|
||||||
* 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;
|
_globalSettings = globalSettings;
|
||||||
private const string KillCodeDbKey = "KillCode";
|
}
|
||||||
|
|
||||||
public KillController(IGlobalSettings globalSettings)
|
[HttpGet("/v1/kill/{providedKillCode}")]
|
||||||
|
public async Task<IActionResult> 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}")]
|
await UpdateKillCode(killCode);
|
||||||
public async Task<IActionResult> KillTheBot([FromRoute] string providedKillCode)
|
|
||||||
{
|
|
||||||
var killCode = _globalSettings.GetKey(KillCodeDbKey);
|
|
||||||
if (providedKillCode != killCode)
|
|
||||||
{
|
|
||||||
return Unauthorized(new ApiError { Message = $"Go Away ({GetKillCodeInt(killCode)})" });
|
|
||||||
}
|
|
||||||
|
|
||||||
await UpdateKillCode(killCode);
|
|
||||||
#pragma warning disable CS4014
|
#pragma warning disable CS4014
|
||||||
Kill();
|
Kill();
|
||||||
#pragma warning restore CS4014
|
#pragma warning restore CS4014
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetKillCodeInt(string code)
|
private int GetKillCodeInt(string code)
|
||||||
{
|
{
|
||||||
var group = Regex.Match(code, @".+(\-\-\-(?<int>\d+))").Groups["int"];
|
var group = Regex.Match(code, @".+(\-\-\-(?<int>\d+))").Groups["int"];
|
||||||
return group.Success ? int.Parse(group.Value) : 0;
|
return group.Success ? int.Parse(group.Value) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateKillCode(string oldCode)
|
private async Task UpdateKillCode(string oldCode)
|
||||||
{
|
{
|
||||||
var newKillCodeInt = new Random().Next(1, 100);
|
var newKillCodeInt = new Random().Next(1, 100);
|
||||||
var newCode = oldCode.Replace($"---{GetKillCodeInt(oldCode)}", $"---{newKillCodeInt}");
|
var newCode = oldCode.Replace($"---{GetKillCodeInt(oldCode)}", $"---{newKillCodeInt}");
|
||||||
await _globalSettings.SetKey(KillCodeDbKey, newCode);
|
await _globalSettings.SetKey(KillCodeDbKey, newCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Kill()
|
private async Task Kill()
|
||||||
{
|
{
|
||||||
// wait a second so the http response can still be sent successfully
|
// wait a second so the http response can still be sent successfully
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
Environment.Exit(GeekbotExitCode.KilledByApiCall.GetHashCode());
|
Environment.Exit(GeekbotExitCode.KilledByApiCall.GetHashCode());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
10
src/Web/Controllers/Status/ApiStatus.cs
Normal file
10
src/Web/Controllers/Status/ApiStatus.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,21 +3,21 @@ using Geekbot.Core;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Geekbot.Web.Controllers.Status
|
namespace Geekbot.Web.Controllers.Status;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[EnableCors("AllowSpecificOrigin")]
|
||||||
|
public class StatusController : ControllerBase
|
||||||
{
|
{
|
||||||
[EnableCors("AllowSpecificOrigin")]
|
[Route("/")]
|
||||||
public class StatusController : Controller
|
public IActionResult GetCommands()
|
||||||
{
|
{
|
||||||
[Route("/")]
|
var responseBody = new ApiStatus
|
||||||
public IActionResult GetCommands()
|
|
||||||
{
|
{
|
||||||
var responseBody = new ApiStatusDto
|
GeekbotVersion = Constants.BotVersion(),
|
||||||
{
|
ApiVersion = Constants.ApiVersion.ToString(CultureInfo.InvariantCulture),
|
||||||
GeekbotVersion = Constants.BotVersion(),
|
Status = "Online"
|
||||||
ApiVersion = Constants.ApiVersion.ToString(CultureInfo.InvariantCulture),
|
};
|
||||||
Status = "Online"
|
return Ok(responseBody);
|
||||||
};
|
|
||||||
return Ok(responseBody);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue