signing up through the discord bot works now

This commit is contained in:
Sarah Faey 2022-11-07 00:16:49 +01:00
parent e445b2a181
commit 69337e69ae
11 changed files with 327 additions and 42 deletions

View file

@ -2,6 +2,9 @@
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using System.Reflection; using System.Reflection;
using DiscordBot.Services;
using SharedClasses.SharedModels;
using DiscordBot.Messages;
namespace DiscordBot namespace DiscordBot
{ {
@ -9,18 +12,21 @@ namespace DiscordBot
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly CommandService _commands; private readonly CommandService _commands;
private readonly HttpService _httpService;
// Retrieve client and CommandService instance via ctor // Retrieve client and CommandService instance via ctor
public CommandHandler(DiscordSocketClient client, CommandService commands) public CommandHandler(DiscordSocketClient client, CommandService commands, HttpService httpService)
{ {
_commands = commands; _commands = commands;
_client = client; _client = client;
_httpService = httpService;
} }
public async Task InstallCommandsAsync() public async Task InstallCommandsAsync()
{ {
_client.SlashCommandExecuted += SlashCommandHandler; _client.SlashCommandExecuted += SlashCommandHandler;
_client.ButtonExecuted += MyButtonHandler; _client.ButtonExecuted += ButtonHandler;
_client.SelectMenuExecuted += SelectMenuHandler;
} }
private async Task SlashCommandHandler(SocketSlashCommand command) private async Task SlashCommandHandler(SocketSlashCommand command)
@ -54,28 +60,95 @@ namespace DiscordBot
await command.RespondAsync(embed: embedBuiler.Build()); await command.RespondAsync(embed: embedBuiler.Build());
} }
public async Task MyButtonHandler(SocketMessageComponent component) public async Task ButtonHandler(SocketMessageComponent component)
{ {
string[] ids = component.Data.CustomId.Split('-'); string[] ids = component.Data.CustomId.Split('-');
List<ApiRole> roles = new List<ApiRole>();
int parsedRaidId = 0;
if(ids.Length > 1)
{
int.TryParse(ids[1],out parsedRaidId);
}
switch(ids[0]) switch(ids[0])
{ {
case Constants.ComponentIds.SIGN_UP: case Constants.ComponentIds.SIGN_UP_BUTTON:
//await component.RespondAsync($"{component.User.Mention} has clicked the SignUp button!"); roles = await _httpService.GetRoles(parsedRaidId, component.User.Id);
await component.RespondAsync("Please choose a role.", components: SignUpMessage.buildMessage(roles, parsedRaidId, ids[0], false) , ephemeral: true);
var mb = new ModalBuilder()
.WithTitle("Fav Food")
.WithCustomId("food_menu")
.AddTextInput("What??", "food_name", placeholder:"Pizza")
.AddTextInput("Why??", "food_reason", TextInputStyle.Paragraph,
"Kus it's so tasty");
//await component.RespondWithModalAsync(mb.Build());
await component.RespondAsync("hi", ephemeral: true);
break; break;
case Constants.ComponentIds.SIGN_OFF: case Constants.ComponentIds.MAYBE_BUTTON:
//await component.RespondAsync($"{component.User.Mention} has clicked the SignOff button!"); case Constants.ComponentIds.BACKUP_BUTTON:
case Constants.ComponentIds.FLEX_BUTTON:
roles = await _httpService.GetRoles(parsedRaidId, component.User.Id);
await component.RespondAsync("Please choose a role.", components: SignUpMessage.buildMessage(roles, parsedRaidId, ids[0], true) , ephemeral: true);
break; break;
case Constants.ComponentIds.SIGN_OFF_BUTTON:
ApiSignUp signOff = new ApiSignUp()
{
raidId = parsedRaidId,
userId = component.User.Id
};
await _httpService.SignOff(signOff);
await Respond(component);
break;
}
}
public async Task SelectMenuHandler(SocketMessageComponent component)
{
string[] ids = component.Data.CustomId.Split('-');
List<ApiRole> roles = new List<ApiRole>();
int parsedRaidId = 0;
if(ids.Length > 1)
{
int.TryParse(ids[1],out parsedRaidId);
}
switch(ids[0])
{
case Constants.ComponentIds.SIGN_UP_DROP_DOWN:
await ManageSignUp(ids[2], parsedRaidId, component);
await Respond(component);
break;
}
}
private async Task ManageSignUp(string buttonType, int raidId, SocketMessageComponent component)
{
if(! int.TryParse(component.Data.Values.First(), out int parsedRoleId)) return;
ApiSignUp signUp = new ApiSignUp()
{
raidId = raidId,
userId = component.User.Id,
roleId = parsedRoleId
};
switch(buttonType)
{
case Constants.ComponentIds.SIGN_UP_BUTTON:
await _httpService.SignUp(signUp);
break;
case Constants.ComponentIds.MAYBE_BUTTON:
await _httpService.SignUpMaybe(signUp);
break;
case Constants.ComponentIds.BACKUP_BUTTON:
await _httpService.SignUpBackup(signUp);
break;
case Constants.ComponentIds.FLEX_BUTTON:
await _httpService.SignUpFlex(signUp);
break;
}
}
//to avoid error messages because of no response...
private async Task Respond(SocketMessageComponent component)
{
try
{
await component.RespondAsync();
}
catch(Discord.Net.HttpException e)
{
} }
} }
} }

View file

@ -2,10 +2,16 @@
{ {
public class Constants public class Constants
{ {
public const string HTTP_CLIENT_NAME = "LiebWebsite";
public class ComponentIds public class ComponentIds
{ {
public const string SIGN_UP = "su"; public const string SIGN_UP_BUTTON = "signUpButton";
public const string SIGN_OFF = "so"; public const string MAYBE_BUTTON = "maybeButton";
public const string BACKUP_BUTTON = "backupButton";
public const string FLEX_BUTTON = "flexButton";
public const string SIGN_OFF_BUTTON = "signOffButton";
public const string SIGN_UP_DROP_DOWN = "signUpDropDown";
} }
public class SlashCommands public class SlashCommands

View file

@ -33,18 +33,12 @@ namespace DiscordBot.Messages
public async Task<ApiRaid> PostRaidMessage(ApiRaid raid) public async Task<ApiRaid> PostRaidMessage(ApiRaid raid)
{ {
var menuBuilder = new SelectMenuBuilder()
.WithPlaceholder("Select an option")
.WithCustomId("menu-1")
.WithMinValues(1)
.WithMaxValues(1)
.AddOption("Option A", "opt-a", "Option B is lying!")
.AddOption("Option B", "opt-b", "Option A is telling the truth!");
var builder = new ComponentBuilder() var builder = new ComponentBuilder()
.WithButton("SignUp", $"{Constants.ComponentIds.SIGN_UP}-{raid.RaidId.ToString()}", ButtonStyle.Secondary) .WithButton("SignUp", $"{Constants.ComponentIds.SIGN_UP_BUTTON}-{raid.RaidId.ToString()}", ButtonStyle.Success)
.WithButton("SignOff", $"{Constants.ComponentIds.SIGN_OFF}-{raid.RaidId.ToString()}", ButtonStyle.Secondary); .WithButton("Maybe", $"{Constants.ComponentIds.MAYBE_BUTTON}-{raid.RaidId.ToString()}", ButtonStyle.Secondary)
//.WithSelectMenu(menuBuilder); .WithButton("Backup", $"{Constants.ComponentIds.BACKUP_BUTTON}-{raid.RaidId.ToString()}", ButtonStyle.Secondary)
.WithButton("Flex", $"{Constants.ComponentIds.FLEX_BUTTON}-{raid.RaidId.ToString()}", ButtonStyle.Secondary)
.WithButton("SignOff", $"{Constants.ComponentIds.SIGN_OFF_BUTTON}-{raid.RaidId.ToString()}", ButtonStyle.Danger);
MessageComponent components = builder.Build(); MessageComponent components = builder.Build();
Embed raidMessage = CreateRaidMessage(raid); Embed raidMessage = CreateRaidMessage(raid);
@ -62,7 +56,8 @@ namespace DiscordBot.Messages
Embed = raidMessage, Embed = raidMessage,
Components = components Components = components
}; };
await messageChannel.ModifyMessageAsync(message.MessageId, new Action<MessageProperties>(x => x = properties)); IUserMessage discordMessage = (IUserMessage)await messageChannel.GetMessageAsync(message.MessageId);
await discordMessage.ModifyAsync(msg => msg.Embed = raidMessage);
} }
else else
{ {
@ -105,7 +100,7 @@ namespace DiscordBot.Messages
Dictionary<string, string> fieldList = new Dictionary<string, string>(); Dictionary<string, string> fieldList = new Dictionary<string, string>();
embed.AddField("Signed up", $"({raid.Roles.Sum(r => r.Users.Count)}/{raid.Roles.Sum(r => r.Spots)}):"); embed.AddField("Signed up", $"({raid.Roles.Sum(r => r.Users.Count)}/{raid.Roles.Sum(r => r.Spots)}):");
foreach (ApiRaid.Role role in raid.Roles) foreach (ApiRaid.Role role in raid.Roles.OrderBy(x => x.RoleId))
{ {
//print signed up users //print signed up users
string signedUpUsers = PrintUsers(role); string signedUpUsers = PrintUsers(role);

View file

@ -0,0 +1,68 @@
using Discord;
using Discord.WebSocket;
using System;
using System.ComponentModel.DataAnnotations;
using SharedClasses.SharedModels;
namespace DiscordBot.Messages
{
public class SignUpMessage
{
public static MessageComponent buildMessage(List<ApiRole> roles, int raidId, string buttonType, bool allRoles)
{
var signUpSelect = new SelectMenuBuilder()
.WithPlaceholder("Select an option")
.WithCustomId($"{Constants.ComponentIds.SIGN_UP_DROP_DOWN}-{raidId}-{buttonType}")
.WithMinValues(1)
.WithMaxValues(1);
foreach(ApiRole role in roles)
{
if(allRoles || role.IsSignUpAllowed)
signUpSelect.AddOption(role.Name, role.roleId.ToString(), role.Description);
}
var builder = new ComponentBuilder()
.WithSelectMenu(signUpSelect, 0);
return builder.Build();
}
/*
public static MessageComponent buildMessage(List<ApiRole> roles, int raidId)
{
var signUpSelect = new SelectMenuBuilder()
.WithPlaceholder("Select an option")
.WithCustomId(Constants.ComponentIds.SIGN_UP_DROP_DOWN)
.WithMinValues(1)
.WithMaxValues(1);
foreach(ApiRole role in roles)
{
if(role.IsSignUpAllowed)
signUpSelect.AddOption(role.Name, role.roleId.ToString(), role.Description);
}
var flexSelect = new SelectMenuBuilder()
.WithPlaceholder("Select an option")
.WithCustomId(Constants.ComponentIds.FLEX_DROP_DOWN)
.WithMinValues(1)
.WithMaxValues(1);
foreach(ApiRole role in roles)
{
flexSelect.AddOption(role.Name, role.roleId.ToString(), role.Description);
}
var builder = new ComponentBuilder()
.WithSelectMenu(signUpSelect, 0)
.WithButton("SignUp", $"{Constants.ComponentIds.SIGN_UP_BUTTON}-{raidId.ToString()}", ButtonStyle.Success, row: 1)
.WithSelectMenu(flexSelect, 2)
.WithButton("Maybe", $"{Constants.ComponentIds.MAYBE_BUTTON}-{raidId.ToString()}", ButtonStyle.Success, row: 3)
.WithButton("Backup", $"{Constants.ComponentIds.BACKUP_BUTTON}-{raidId.ToString()}", ButtonStyle.Success, row: 3)
.WithButton("Flex", $"{Constants.ComponentIds.FLEX_BUTTON}-{raidId.ToString()}", ButtonStyle.Success, row: 3);
return builder.Build();
}*/
}
}

View file

@ -2,9 +2,11 @@ using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.Net; using Discord.Net;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Reflection; using System.Reflection;
using DiscordBot.Services;
namespace DiscordBot namespace DiscordBot
{ {
@ -28,6 +30,7 @@ namespace DiscordBot
builder.Services.AddSingleton<DiscordSocketClient>(); builder.Services.AddSingleton<DiscordSocketClient>();
builder.Services.AddSingleton<CommandService>(); builder.Services.AddSingleton<CommandService>();
builder.Services.AddSingleton<CommandHandler>(); builder.Services.AddSingleton<CommandHandler>();
builder.Services.AddSingleton<HttpService>();
builder.Services.AddControllers(); builder.Services.AddControllers();
@ -35,6 +38,24 @@ namespace DiscordBot
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(Constants.HTTP_CLIENT_NAME , httpClient =>
{
httpClient.BaseAddress = new Uri("https://localhost:7216/");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
}).ConfigurePrimaryHttpMessageHandler(() => {
var handler = new HttpClientHandler();
if (builder.Environment.IsDevelopment())
{
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
}
return handler;
});
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.

View file

@ -0,0 +1,77 @@
using SharedClasses.SharedModels;
using System.Net.Http;
using static System.Net.Mime.MediaTypeNames;
using System.Text.Json;
using System.Text;
namespace DiscordBot.Services
{
public class HttpService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly JsonSerializerOptions _serializerOptions;
public HttpService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
_serializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
}
public async Task<List<ApiRole>> GetRoles(int raidId, ulong userId)
{
var httpClient = _httpClientFactory.CreateClient(Constants.HTTP_CLIENT_NAME);
var httpResponseMessage = await httpClient.GetAsync($"DiscordBot/GetRoles/{raidId}/{userId}");
if (httpResponseMessage.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<List<ApiRole>>(contentStream, _serializerOptions);
}
return new List<ApiRole>();
}
public async Task SignUp(ApiSignUp signUp)
{
await SendSignUp(signUp, "DiscordBot/SignUp");
}
public async Task SignUpMaybe(ApiSignUp signUp)
{
await SendSignUp(signUp, "DiscordBot/SignUpMaybe");
}
public async Task SignUpBackup(ApiSignUp signUp)
{
await SendSignUp(signUp, "DiscordBot/SignUpBackup");
}
public async Task SignUpFlex(ApiSignUp signUp)
{
await SendSignUp(signUp, "DiscordBot/SignUpFlex");
}
public async Task SignOff(ApiSignUp signUp)
{
await SendSignUp(signUp, "DiscordBot/SignOff");
}
private async Task SendSignUp(ApiSignUp signUp, string requestUri)
{
var httpClient = _httpClientFactory.CreateClient(Constants.HTTP_CLIENT_NAME);
var raidItemJson = new StringContent(
JsonSerializer.Serialize(signUp),
Encoding.UTF8,
Application.Json);
var httpResponseMessage = await httpClient.PostAsync(requestUri, raidItemJson);
}
}
}

View file

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Lieb.Data; using Lieb.Data;
using Lieb.Models.GuildWars2.Raid; using Lieb.Models.GuildWars2.Raid;
using Lieb.Models.GuildWars2;
using SharedClasses.SharedModels; using SharedClasses.SharedModels;
namespace Lieb.Controllers namespace Lieb.Controllers
@ -13,20 +14,22 @@ namespace Lieb.Controllers
public class DiscordBotController : ControllerBase public class DiscordBotController : ControllerBase
{ {
RaidService _raidService; RaidService _raidService;
UserService _userService;
public DiscordBotController(RaidService raidService) public DiscordBotController(RaidService raidService, UserService userService)
{ {
_raidService = raidService; _raidService = raidService;
_userService = userService;
} }
[HttpGet] [HttpGet]
[Route("[action]/{raidId}/{userId}")] [Route("[action]/{raidId}/{userId}")]
public List<ApiRole> GetRoles(int raidId, ulong userId) public ActionResult<List<ApiRole>> GetRoles(int raidId, ulong userId)
{ {
Raid raid = _raidService.GetRaid(raidId); Raid raid = _raidService.GetRaid(raidId);
if(!_raidService.IsRaidSignUpAllowed(userId, raidId, out string errorMessage)) if(!_raidService.IsRaidSignUpAllowed(userId, raidId, out string errorMessage))
{ {
//TODO: send error message return Problem(errorMessage);
} }
List<ApiRole> apiRoles = new List<ApiRole>(); List<ApiRole> apiRoles = new List<ApiRole>();
@ -35,7 +38,8 @@ namespace Lieb.Controllers
apiRoles.Add(new ApiRole(){ apiRoles.Add(new ApiRole(){
Name = role.Name, Name = role.Name,
Description = role.Description, Description = role.Description,
IsSignUpAllowed = _raidService.IsRoleSignUpAllowed(userId, role.RaidRoleId, SignUpType.SignedUp) IsSignUpAllowed = _raidService.IsRoleSignUpAllowed(userId, role.RaidRoleId, SignUpType.SignedUp),
roleId = role.RaidRoleId
}); });
} }
return apiRoles; return apiRoles;
@ -45,7 +49,40 @@ namespace Lieb.Controllers
[Route("[action]")] [Route("[action]")]
public async Task SignUp(ApiSignUp signUp) public async Task SignUp(ApiSignUp signUp)
{ {
_raidService.SignUp(signUp.raidId, signUp.userId, signUp.gw2AccountId, signUp.roleId, ) int accountId = _userService.GetLiebUserGW2AccountOnly(signUp.userId).GuildWars2Accounts.FirstOrDefault(new GuildWars2Account()).GuildWars2AccountId;
await _raidService.SignUp(signUp.raidId, signUp.userId, accountId, signUp.roleId, SignUpType.SignedUp);
}
[HttpPost]
[Route("[action]")]
public async Task SignUpMaybe(ApiSignUp signUp)
{
int accountId = _userService.GetLiebUserGW2AccountOnly(signUp.userId).GuildWars2Accounts.FirstOrDefault(new GuildWars2Account()).GuildWars2AccountId;
await _raidService.SignUp(signUp.raidId, signUp.userId, signUp.gw2AccountId, signUp.roleId, SignUpType.Maybe);
}
[HttpPost]
[Route("[action]")]
public async Task SignUpBackup(ApiSignUp signUp)
{
int accountId = _userService.GetLiebUserGW2AccountOnly(signUp.userId).GuildWars2Accounts.FirstOrDefault(new GuildWars2Account()).GuildWars2AccountId;
await _raidService.SignUp(signUp.raidId, signUp.userId, signUp.gw2AccountId, signUp.roleId, SignUpType.Backup);
}
[HttpPost]
[Route("[action]")]
public async Task SignUpFlex(ApiSignUp signUp)
{
int accountId = _userService.GetLiebUserGW2AccountOnly(signUp.userId).GuildWars2Accounts.FirstOrDefault(new GuildWars2Account()).GuildWars2AccountId;
await _raidService.SignUp(signUp.raidId, signUp.userId, signUp.gw2AccountId, signUp.roleId, SignUpType.Flex);
}
[HttpPost]
[Route("[action]")]
public async Task SignOff(ApiSignUp signUp)
{
int accountId = _userService.GetLiebUserGW2AccountOnly(signUp.userId).GuildWars2Accounts.FirstOrDefault(new GuildWars2Account()).GuildWars2AccountId;
await _raidService.SignOff(signUp.raidId, signUp.userId);
} }
} }
} }

View file

@ -43,7 +43,7 @@ namespace Lieb.Data
//new LiebUser{Id=0, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} }, //new LiebUser{Id=0, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} },
new LiebUser{Id=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} }, new LiebUser{Id=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} },
#if DEBUG #if DEBUG
new LiebUser{Id=1, Name="Lisa", GuildWars2Accounts = new List<GuildWars2Account>(){ hierpiepts}}, new LiebUser{Id=194455125769715713, Name="Lisa", GuildWars2Accounts = new List<GuildWars2Account>(){ hierpiepts}},
new LiebUser{Id=2, Name="Simon", GuildWars2Accounts = new List<GuildWars2Account>(){ bloodseeker}} new LiebUser{Id=2, Name="Simon", GuildWars2Accounts = new List<GuildWars2Account>(){ bloodseeker}}
#endif #endif
}; };

View file

@ -171,7 +171,10 @@ namespace Lieb.Data
} }
using var context = _contextFactory.CreateDbContext(); using var context = _contextFactory.CreateDbContext();
context.Update(raid.DiscordRaidMessages); foreach(DiscordRaidMessage message in raid.DiscordRaidMessages)
{
context.Update(message);
}
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
@ -192,6 +195,7 @@ namespace Lieb.Data
foreach(RaidRole role in raid.Roles) foreach(RaidRole role in raid.Roles)
{ {
ApiRaid.Role apiRole = new ApiRaid.Role(){ ApiRaid.Role apiRole = new ApiRaid.Role(){
RoleId = role.RaidRoleId,
Description = role.Description, Description = role.Description,
Name = role.Name, Name = role.Name,
Spots = role.Spots Spots = role.Spots

View file

@ -36,6 +36,8 @@ namespace SharedClasses.SharedModels
public class Role public class Role
{ {
public int RoleId { get; set; }
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;

View file

@ -7,6 +7,8 @@ namespace SharedClasses.SharedModels
public string Description { get; set; } = String.Empty; public string Description { get; set; } = String.Empty;
public bool IsSignUpAllowed{ get; set; } = false; public bool IsSignUpAllowed { get; set; } = false;
public int roleId {get; set;}
} }
} }