diff --git a/.gitignore b/.gitignore index 9491a2f..9bde008 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ # Mono auto generated files mono_crash.* +# Sqlite Database +Database/ + # Build results [Dd]ebug/ [Dd]ebugPublic/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..cd5c84b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,60 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": "Lieb Website", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/Lieb/bin/Debug/net6.0/Lieb.dll", + "args": [], + "cwd": "${workspaceFolder}/Lieb", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": "DiscordBot", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/DiscordBot/bin/Debug/net6.0/DiscordBot.dll", + "args": [], + "cwd": "${workspaceFolder}/DiscordBot", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e61705a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Lieb/Lieb.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Lieb/Lieb.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/Lieb/Lieb.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/DiscordBot/CommandHandler.cs b/DiscordBot/CommandHandler.cs new file mode 100644 index 0000000..cf734f9 --- /dev/null +++ b/DiscordBot/CommandHandler.cs @@ -0,0 +1,82 @@ +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using System.Reflection; + +namespace DiscordBot +{ + public class CommandHandler + { + private readonly DiscordSocketClient _client; + private readonly CommandService _commands; + + // Retrieve client and CommandService instance via ctor + public CommandHandler(DiscordSocketClient client, CommandService commands) + { + _commands = commands; + _client = client; + } + + public async Task InstallCommandsAsync() + { + _client.SlashCommandExecuted += SlashCommandHandler; + _client.ButtonExecuted += MyButtonHandler; + } + + private async Task SlashCommandHandler(SocketSlashCommand command) + { + switch (command.Data.Name) + { + case Constants.SlashCommands.FIRST_COMMAND: + await HandleFirstCommand(command); + break; + } + await command.RespondAsync($"You executed {command.Data.Name}"); + } + + + private async Task HandleFirstCommand(SocketSlashCommand command) + { + // We need to extract the user parameter from the command. since we only have one option and it's required, we can just use the first option. + var guildUser = (SocketGuildUser)command.Data.Options.First().Value; + + // We remove the everyone role and select the mention of each role. + var roleList = string.Join(",\n", guildUser.Roles.Where(x => !x.IsEveryone).Select(x => x.Mention)); + + var embedBuiler = new EmbedBuilder() + .WithAuthor(guildUser.ToString(), guildUser.GetAvatarUrl() ?? guildUser.GetDefaultAvatarUrl()) + .WithTitle("Roles") + .WithDescription(roleList) + .WithColor(Color.Green) + .WithCurrentTimestamp(); + + // Now, Let's respond with the embed. + await command.RespondAsync(embed: embedBuiler.Build()); + } + + public async Task MyButtonHandler(SocketMessageComponent component) + { + string[] ids = component.Data.CustomId.Split('-'); + switch(ids[0]) + { + case Constants.ComponentIds.SIGN_UP: + //await component.RespondAsync($"{component.User.Mention} has clicked the SignUp button!"); + + 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; + case Constants.ComponentIds.SIGN_OFF: + //await component.RespondAsync($"{component.User.Mention} has clicked the SignOff button!"); + break; + } + } + } +} diff --git a/DiscordBot/Constants.cs b/DiscordBot/Constants.cs new file mode 100644 index 0000000..b4160fe --- /dev/null +++ b/DiscordBot/Constants.cs @@ -0,0 +1,16 @@ +namespace DiscordBot +{ + public class Constants + { + public class ComponentIds + { + public const string SIGN_UP = "su"; + public const string SIGN_OFF = "so"; + } + + public class SlashCommands + { + public const string FIRST_COMMAND = "first-command"; + } + } +} diff --git a/DiscordBot/Controllers/RaidController.cs b/DiscordBot/Controllers/RaidController.cs new file mode 100644 index 0000000..df4aa93 --- /dev/null +++ b/DiscordBot/Controllers/RaidController.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Mvc; +using SharedClasses.SharedModels; +using DiscordBot.Messages; +using Discord; +using Discord.WebSocket; +using System.Linq; + +namespace DiscordBot.Controllers +{ + [ApiController] + [Route("[controller]")] + public class RaidController : ControllerBase + { + + DiscordSocketClient _client; + + public RaidController(DiscordSocketClient client) + { + _client = client; + } + +/*{"Title":"Testraid","Description":"This is a test raidnwith multiple lines?","Organizer":"Sarah","Guild":"LIEB","VoiceChat":"ts.lieb.games","RaidId":4,"StartTimeUTC":"2022-11-13T10:24:58.3955622+00:00","EndTimeUTC":"2022-11-14T12:24:58.3955622+00:00","Roles":[{"Name":"Random","Description":"RandomWithBoons","Spots":10,"Users":[]}],"DisocrdMessages":[{"GuildId":666953424734257182,"ChannelId":666954070388637697,"MessageId":0,"WebsiteDatabaseId":2}]}*/ + [HttpPost] + [Route("[action]")] + public async Task PostRaidMessage(ApiRaid raid) + { + RaidMessage message = new RaidMessage(_client); + return await message.PostRaidMessage(raid); + } + + [HttpPost] + [Route("[action]")] + public async Task DeleteRaidMessage(IEnumerable messages) + { + RaidMessage message = new RaidMessage(_client); + await message.DeleteRaidMessage(messages); + } + + [HttpGet] + [Route("[action]")] + public List GetServers() + { + List servers = new List(); + foreach(var guild in _client.Guilds) + { + DiscordServer server = new DiscordServer(){ + Name = guild.Name, + Id = guild.Id + }; + + foreach(var channel in guild.Channels) + { + if( channel is SocketTextChannel && channel.Users.Where(u => u.Id == _client.CurrentUser.Id).Any()) + { + server.Channels.Add(new DiscordChannel(){ + Name = channel.Name, + Id = channel.Id + }); + } + } + servers.Add(server); + } + return servers; + } + + [HttpPost] + [Route("[action]")] + public async Task SendUserReminder(ApiUserReminder reminder) + { + foreach(ulong userId in reminder.UserIds) + { + await _client.GetUser(userId).SendMessageAsync(reminder.Message); + } + } + + [HttpPost] + [Route("[action]")] + public async Task SendChannelReminder(ApiChannelReminder reminder) + { + var channel = _client.GetGuild(reminder.DiscordServerId).GetChannel(reminder.DiscordChannelId); + if (channel != null && channel is IMessageChannel) + { + IMessageChannel messageChannel = channel as IMessageChannel; + await messageChannel.SendMessageAsync(reminder.Message); + } + } + } +} \ No newline at end of file diff --git a/DiscordBot/Controllers/WeatherForecastController.cs b/DiscordBot/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..8ca320e --- /dev/null +++ b/DiscordBot/Controllers/WeatherForecastController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; + +namespace DiscordBot.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/DiscordBot/DiscordBot.csproj b/DiscordBot/DiscordBot.csproj new file mode 100644 index 0000000..f13098a --- /dev/null +++ b/DiscordBot/DiscordBot.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/DiscordBot/Messages/RaidMessage.cs b/DiscordBot/Messages/RaidMessage.cs new file mode 100644 index 0000000..93b2042 --- /dev/null +++ b/DiscordBot/Messages/RaidMessage.cs @@ -0,0 +1,131 @@ +using Discord; +using Discord.WebSocket; +using System; +using System.ComponentModel.DataAnnotations; +using SharedClasses.SharedModels; + +namespace DiscordBot.Messages +{ + public class RaidMessage + { + DiscordSocketClient _client; + + public RaidMessage(DiscordSocketClient client) + { + _client = client; + } + + public async Task DeleteRaidMessage(IEnumerable messages) + { + foreach (ApiRaid.DiscordMessage message in messages) + { + var channel = _client.GetGuild(message.GuildId).GetChannel(message.ChannelId); + if (channel != null && channel is IMessageChannel) + { + IMessageChannel messageChannel = channel as IMessageChannel; + if (message.MessageId != 0) + { + await messageChannel.DeleteMessageAsync(message.MessageId); + } + } + } + } + + public async Task 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() + .WithButton("SignUp", $"{Constants.ComponentIds.SIGN_UP}-{raid.RaidId.ToString()}", ButtonStyle.Secondary) + .WithButton("SignOff", $"{Constants.ComponentIds.SIGN_OFF}-{raid.RaidId.ToString()}", ButtonStyle.Secondary); + //.WithSelectMenu(menuBuilder); + + MessageComponent components = builder.Build(); + Embed raidMessage = CreateRaidMessage(raid); + + foreach (ApiRaid.DiscordMessage message in raid.DisocrdMessages) + { + var channel = _client.GetGuild(message.GuildId).GetChannel(message.ChannelId); + if (channel != null && channel is IMessageChannel) + { + IMessageChannel messageChannel = channel as IMessageChannel; + if (message.MessageId != 0) + { + MessageProperties properties = new MessageProperties() + { + Embed = raidMessage, + Components = components + }; + await messageChannel.ModifyMessageAsync(message.MessageId, new Action(x => x = properties)); + } + else + { + IUserMessage sentMessage = await messageChannel.SendMessageAsync(embed: raidMessage, components: components); + message.MessageId = sentMessage.Id; + } + } + } + return raid; + } + + public Embed CreateRaidMessage(ApiRaid raid) + { + var embed = new EmbedBuilder() + { + Title = raid.Title, + Description = raid.Description, + Footer = new EmbedFooterBuilder() + { + Text = $"RaidId: {raid.RaidId}" + } + }; + AddMessageDetails(raid, ref embed); + AddMessageRoles(raid, ref embed); + + return embed.Build(); + } + + private void AddMessageDetails(ApiRaid raid, ref EmbedBuilder embed) + { + embed.AddField("Date", $"{raid.StartTimeUTC.ToLocalTime().DateTime.ToLongDateString()}"); + embed.AddField("Time", $"from: {raid.StartTimeUTC.ToLocalTime().DateTime.ToShortTimeString()} to: {raid.EndTimeUTC.ToLocalTime().DateTime.ToShortTimeString()}"); + embed.AddField("Organisator", raid.Organizer, true); + embed.AddField("Guild", raid.Guild, true); + embed.AddField("Voice chat", raid.VoiceChat, true); + } + + private void AddMessageRoles(ApiRaid raid, ref EmbedBuilder embed) + { + Dictionary fieldList = new Dictionary(); + + embed.AddField("Signed up", $"({raid.Roles.Sum(r => r.Users.Count)}/{raid.Roles.Sum(r => r.Spots)}):"); + foreach (ApiRaid.Role role in raid.Roles) + { + //print signed up users + string signedUpUsers = PrintUsers(role); + + if (string.IsNullOrEmpty(signedUpUsers)) signedUpUsers = "-"; + string fieldName = $"{role.Name}: {role.Description} ({role.Users.Where(u => string.IsNullOrWhiteSpace(u.Status)).Count()}/{role.Spots})"; + + embed.AddField(fieldName, signedUpUsers); + } + } + + private string PrintUsers(ApiRaid.Role role) + { + string rolesString = string.Empty; + foreach (ApiRaid.Role.User user in role.Users.OrderBy(u => u.Status)) + { + string status = !string.IsNullOrWhiteSpace(user.Status) ? $" - {user.Status}" : string.Empty; + rolesString += $"\t{user.UserName} ({user.AccountName}) {status}\n"; + } + return rolesString; + } + } +} diff --git a/DiscordBot/Program.cs b/DiscordBot/Program.cs new file mode 100644 index 0000000..563f4a9 --- /dev/null +++ b/DiscordBot/Program.cs @@ -0,0 +1,123 @@ +using Discord; +using Discord.Commands; +using Discord.Net; +using Discord.WebSocket; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using System.Reflection; + +namespace DiscordBot +{ + internal class Program + { + DiscordSocketClient _client; + + public static Task Main(string[] args) => new Program().MainAsync(args); + + public async Task MainAsync(string[] args) + { + var dicordConfig = new DiscordSocketConfig() + { + + }; + + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + builder.Services.AddSingleton(dicordConfig); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + + builder.Services.AddControllers(); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + app.MapControllers(); + + + _client = app.Services.GetRequiredService(); + var commandHandler = app.Services.GetRequiredService(); + await commandHandler.InstallCommandsAsync(); + + _client.Log += Log; + //_client.Ready += Client_Ready; + + // You can assign your bot token to a string, and pass that in to connect. + // This is, however, insecure, particularly if you plan to have your code hosted in a public repository. + var token = "MTAxODE3MDczMDU0Mzg1Nzc3NA.GA2Qzu.__kLICXm-8VLTQD6KzL-uGaPx60Q6fn8K6lE3A"; + //var token = "Njc2NzQ4NjM2NjI5MTA2NzE4.Xtu2Ww._2oF7lQtxLLOUhfAIMxocN52dYo"; + + // Some alternative options would be to keep your token in an Environment Variable or a standalone file. + // var token = Environment.GetEnvironmentVariable("NameOfYourEnvironmentVariable"); + // var token = File.ReadAllText("token.txt"); + // var token = JsonConvert.DeserializeObject(File.ReadAllText("config.json")).Token; + + await _client.LoginAsync(TokenType.Bot, token); + await _client.StartAsync(); + + + + app.Run(); + + // Block this task until the program is closed. + await Task.Delay(-1); + + } + + private Task Log(LogMessage msg) + { + Console.WriteLine(msg.ToString()); + return Task.CompletedTask; + } + + public async Task Client_Ready() + { + // Let's build a guild command! We're going to need a guild so lets just put that in a variable. + var guild = _client.GetGuild(666953424734257182); + + // Next, lets create our slash command builder. This is like the embed builder but for slash commands. + var guildCommand = new SlashCommandBuilder(); + + // Note: Names have to be all lowercase and match the regular expression ^[\w-]{3,32}$ + guildCommand.WithName("first-command"); + + // Descriptions can have a max length of 100. + guildCommand.WithDescription("This is my first guild slash command!"); + + + try + { + // Now that we have our builder, we can call the CreateApplicationCommandAsync method to make our slash command. + await guild.CreateApplicationCommandAsync(guildCommand.Build()); + + // Using the ready event is a simple implementation for the sake of the example. Suitable for testing and development. + // For a production bot, it is recommended to only run the CreateGlobalApplicationCommandAsync() once for each command. + } + catch (ApplicationCommandException exception) + { + // If our command was invalid, we should catch an ApplicationCommandException. This exception contains the path of the error as well as the error message. You can serialize the Error field in the exception to get a visual of where your error is. + //var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented); + + // You can send this error somewhere or just print it to the console, for this example we're just going to print it. + //Console.WriteLine(json); + } + } + + } +} \ No newline at end of file diff --git a/DiscordBot/Properties/launchSettings.json b/DiscordBot/Properties/launchSettings.json new file mode 100644 index 0000000..b356489 --- /dev/null +++ b/DiscordBot/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52668", + "sslPort": 44379 + } + }, + "profiles": { + "DiscordBot": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7240;http://localhost:5240", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/DiscordBot/WeatherForecast.cs b/DiscordBot/WeatherForecast.cs new file mode 100644 index 0000000..49bd2ee --- /dev/null +++ b/DiscordBot/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace DiscordBot +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} \ No newline at end of file diff --git a/DiscordBot/appsettings.Development.json b/DiscordBot/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/DiscordBot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/DiscordBot/appsettings.json b/DiscordBot/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/DiscordBot/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Lieb.sln b/Lieb.sln index 0ad88ef..5c9d18d 100644 --- a/Lieb.sln +++ b/Lieb.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.32126.317 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lieb", "Lieb\Lieb.csproj", "{48554958-F16E-466A-B9B7-F17511FDA415}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{8F30A150-F8B2-4173-B2AA-5A9C1BE1EF51}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBot", "DiscordBot\DiscordBot.csproj", "{73A8E9D9-4997-4D91-8319-F2E51F18F66C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,10 +17,10 @@ Global {48554958-F16E-466A-B9B7-F17511FDA415}.Debug|Any CPU.Build.0 = Debug|Any CPU {48554958-F16E-466A-B9B7-F17511FDA415}.Release|Any CPU.ActiveCfg = Release|Any CPU {48554958-F16E-466A-B9B7-F17511FDA415}.Release|Any CPU.Build.0 = Release|Any CPU - {8F30A150-F8B2-4173-B2AA-5A9C1BE1EF51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F30A150-F8B2-4173-B2AA-5A9C1BE1EF51}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F30A150-F8B2-4173-B2AA-5A9C1BE1EF51}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F30A150-F8B2-4173-B2AA-5A9C1BE1EF51}.Release|Any CPU.Build.0 = Release|Any CPU + {73A8E9D9-4997-4D91-8319-F2E51F18F66C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73A8E9D9-4997-4D91-8319-F2E51F18F66C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73A8E9D9-4997-4D91-8319-F2E51F18F66C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73A8E9D9-4997-4D91-8319-F2E51F18F66C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Lieb/Controllers/GuildWars2RaidController.cs b/Lieb/Controllers/GuildWars2RaidController.cs deleted file mode 100644 index a21fe75..0000000 --- a/Lieb/Controllers/GuildWars2RaidController.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Lieb.Data; -using Lieb.Models.GuildWars2; -using Lieb.Models.GuildWars2.Raid; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Net; - -namespace Lieb.Controllers -{ - public class GuildWars2RaidController : ControllerBase - { - private readonly RaidService _raidService; - private readonly UserService _userService; - private readonly IDbContextFactory _contextFactory; - - public GuildWars2RaidController(RaidService raidService, UserService userService, IDbContextFactory contextFactory) - { - _raidService = raidService; - _userService = userService; - _contextFactory = contextFactory; - } - - - [HttpGet] - public int GetRaidIdByDiscordMessageId(int discordMessageId) - { - using var context = _contextFactory.CreateDbContext(); - DiscordRaidMessage discordMessage = context.DiscordRaidMessages.FirstOrDefault(m => m.DiscordRaidMessageId == discordMessageId); - //TODO retrun 404 - return discordMessage != null ? discordMessage.RaidId : -1; - } - - [HttpGet] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetRaidIdByDiscordMessageId([FromRoute] ulong discordMessageId) - { - using var context = _contextFactory.CreateDbContext(); - DiscordRaidMessage? discordMessage = await context.DiscordRaidMessages.FirstOrDefaultAsync(m => m.DiscordMessageId == discordMessageId); - if (discordMessage == null) - { - return Problem(statusCode: (int)HttpStatusCode.NotFound); - } - return Ok(discordMessage.RaidId); - } - - - [HttpGet] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetFreeRoles([FromRoute] int raidId) - { - Raid raid = _raidService.GetRaid(raidId); - Dictionary roleMap = new Dictionary(); - foreach(RaidSignUp signUp in raid.SignUps) - { - if (signUp.SignUpType == SignUpType.SignedUp) - { - roleMap[signUp.RaidRoleId] += 1; - } - } - return Ok(raid.Roles.Where(r => roleMap.ContainsKey(r.RaidRoleId) && roleMap[r.RaidRoleId] < r.Spots).ToList()); - } - - - - [HttpGet] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetGuildwars2Accounts([FromRoute] ulong userId) - { - using var context = _contextFactory.CreateDbContext(); - List? gw2Accounts = (await context.LiebUsers.FirstOrDefaultAsync(u => u.Id == userId))?.GuildWars2Accounts.ToList(); - if (gw2Accounts == null) - { - return Problem(statusCode: (int)HttpStatusCode.NotFound); - } - return Ok(gw2Accounts); - } - - [HttpPost] - public string SignUp(int raidId, ulong discordUserId, int guildWars2AccountId, int roleId, SignUpType signUpType) - { - int userId = _userService.GetLiebUserId(discordUserId).Result; - _raidService.SignUp(raidId, userId, guildWars2AccountId, roleId, signUpType).Wait(); - return String.Empty; - } - - - [HttpGet("metadata/{fileId}")] - [Consumes("application/json")] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetFileMetadata([FromRoute] Guid fileId) - { - using var context = _contextFactory.CreateDbContext(); - DiscordRaidMessage discordMessage = context.DiscordRaidMessages.FirstOrDefault(m => m.DiscordRaidMessageId == discordMessageId); - if (discordMessage == null) - { - return Problem(statusCode: (int)HttpStatusCode.NotFound); - } - return Ok(discordMessage.RaidId); - } - - - - - - - - [HttpGet("metadata/{fileId}")] - [Authorize(Roles = Roles.TimeBoard)] - [Consumes("application/json")] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetFileMetadata([FromRoute] Guid fileId) - { - using var context = _contextFactory.CreateDbContext(); - DiscordRaidMessage discordMessage = context.DiscordRaidMessages.FirstOrDefault(m => m.DiscordRaidMessageId == discordMessageId); - if(discordMessage == null) - { - return Problem(statusCode: (int)HttpStatusCode.NotFound); - } - return Ok(discordMessage.RaidId); - } - - } -} diff --git a/Lieb/Data/Constants.cs b/Lieb/Data/Constants.cs index 8815a2d..c727318 100644 --- a/Lieb/Data/Constants.cs +++ b/Lieb/Data/Constants.cs @@ -2,6 +2,7 @@ { public static class Constants { + public const string HttpClientName = "Discord"; public const string ClaimType = "Role"; public static readonly int RaidEditPowerLevel = Roles.Moderator.PowerLevel; diff --git a/Lieb/Data/DbInitializer.cs b/Lieb/Data/DbInitializer.cs index defff37..ec0c645 100644 --- a/Lieb/Data/DbInitializer.cs +++ b/Lieb/Data/DbInitializer.cs @@ -40,8 +40,8 @@ namespace Lieb.Data GuildWars2Account bloodseeker = new GuildWars2Account() { AccountName = "Bloodseeker.2043" }; var users = new LiebUser[] { - new LiebUser{Id=0, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List(){ linaith, sarah} }, - //new LiebUser{Id=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List(){ linaith, sarah} }, + //new LiebUser{Id=0, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List(){ linaith, sarah} }, + new LiebUser{Id=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List(){ linaith, sarah} }, #if DEBUG new LiebUser{Id=1, Name="Lisa", GuildWars2Accounts = new List(){ hierpiepts}}, new LiebUser{Id=2, Name="Simon", GuildWars2Accounts = new List(){ bloodseeker}} diff --git a/Lieb/Data/DiscordService.cs b/Lieb/Data/DiscordService.cs new file mode 100644 index 0000000..5380c43 --- /dev/null +++ b/Lieb/Data/DiscordService.cs @@ -0,0 +1,253 @@ +using SharedClasses.SharedModels; +using System.Net.Http; +using static System.Net.Mime.MediaTypeNames; +using System.Text.Json; +using System.Text; +using Lieb.Models.GuildWars2.Raid; +using Microsoft.EntityFrameworkCore; + +namespace Lieb.Data +{ + public class DiscordService + { + private readonly IHttpClientFactory _httpClientFactory; + private readonly IDbContextFactory _contextFactory; + private readonly JsonSerializerOptions _serializerOptions; + + public DiscordService(IHttpClientFactory httpClientFactory, IDbContextFactory contextFactory) + { + _httpClientFactory = httpClientFactory; + _contextFactory = contextFactory; + _serializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + } + + public async Task PostRaidMessage(int raidId) + { + using var context = _contextFactory.CreateDbContext(); + Raid raid = context.Raids + .Include(r => r.Roles) + .Include(r => r.SignUps) + .ThenInclude(s => s.LiebUser) + .Include(r => r.SignUps) + .ThenInclude(s => s.GuildWars2Account) + .Include(r => r.SignUps) + .ThenInclude(s => s.RaidRole) + .Include(r => r.DiscordRaidMessages) + .FirstOrDefault(r => r.RaidId == raidId); + await PostRaidMessage(raid); + } + + public async Task PostRaidMessage(Raid raid) + { + var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName); + + ApiRaid apiRaid = ConvertRaid(raid); + + var raidItemJson = new StringContent( + JsonSerializer.Serialize(apiRaid), + Encoding.UTF8, + Application.Json); + + string json = JsonSerializer.Serialize(apiRaid); + + var httpResponseMessage = await httpClient.PostAsync("raid/PostRaidMessage", raidItemJson); + + if (httpResponseMessage.IsSuccessStatusCode) + { + using var contentStream = + await httpResponseMessage.Content.ReadAsStreamAsync(); + + ApiRaid returnedRaid = await JsonSerializer.DeserializeAsync(contentStream, _serializerOptions); + await UpdateDiscordMessages(returnedRaid.DisocrdMessages, raid); + } + } + + public async Task DeleteRaidMessages(Raid raid) + { + await DeleteRaidMessages(raid.DiscordRaidMessages); + } + + public async Task DeleteRaidMessages(IEnumerable messages) + { + var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName); + + IEnumerable apiMessages = ConvertMessages(messages); + + var messageItemJson = new StringContent( + JsonSerializer.Serialize(apiMessages), + Encoding.UTF8, + Application.Json); + + var httpResponseMessage = await httpClient.PostAsync("raid/DeleteRaidMessage", messageItemJson); + + httpResponseMessage.EnsureSuccessStatusCode(); + } + + public async Task> GetServers() + { + try + { + var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName); + + var httpResponseMessage = await httpClient.GetAsync("raid/GetServers"); + + if (httpResponseMessage.IsSuccessStatusCode) + { + using var contentStream = + await httpResponseMessage.Content.ReadAsStreamAsync(); + + return await JsonSerializer.DeserializeAsync>(contentStream, _serializerOptions); + } + } + catch(Exception e) + { + + } + return new List(); + } + + public async Task SendUserReminder(RaidReminder reminder) + { + using var context = _contextFactory.CreateDbContext(); + var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName); + + Raid raid = context.Raids + .Include(r => r.SignUps) + .ThenInclude(s => s.LiebUser) + .FirstOrDefault(r => r.RaidId == reminder.RaidId); + + if(raid == null) return; + + ApiUserReminder apiReminder = ConvertUserReminder(reminder, raid); + + var raidItemJson = new StringContent( + JsonSerializer.Serialize(apiReminder), + Encoding.UTF8, + Application.Json); + + var httpResponseMessage = await httpClient.PostAsync("raid/SendUserReminder", raidItemJson); + + if (httpResponseMessage.IsSuccessStatusCode) + { + reminder.Sent = true; + context.Update(reminder); + await context.SaveChangesAsync(); + } + } + + public async Task SendChannelReminder(RaidReminder reminder) + { + var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName); + + ApiChannelReminder apiReminder = ConvertChannelReminder(reminder); + + var raidItemJson = new StringContent( + JsonSerializer.Serialize(apiReminder), + Encoding.UTF8, + Application.Json); + + var httpResponseMessage = await httpClient.PostAsync("raid/SendChannelReminder", raidItemJson); + + if (httpResponseMessage.IsSuccessStatusCode) + { + reminder.Sent = true; + using var context = _contextFactory.CreateDbContext(); + context.Update(reminder); + await context.SaveChangesAsync(); + } + } + + private async Task UpdateDiscordMessages(IEnumerable messages, Raid raid) + { + foreach(ApiRaid.DiscordMessage message in messages) + { + if(raid.DiscordRaidMessages.Where(m => m.DiscordRaidMessageId == message.WebsiteDatabaseId).Any()) + { + raid.DiscordRaidMessages.FirstOrDefault(m => m.DiscordRaidMessageId == message.WebsiteDatabaseId).DiscordMessageId = message.MessageId; + } + } + + using var context = _contextFactory.CreateDbContext(); + context.Update(raid.DiscordRaidMessages); + await context.SaveChangesAsync(); + } + + private ApiRaid ConvertRaid(Raid raid) + { + ApiRaid apiRaid = new ApiRaid(){ + Title = raid.Title, + Description = raid.Description, + Guild = raid.Guild, + Organizer = raid.Organizer, + VoiceChat = raid.VoiceChat, + StartTimeUTC = raid.StartTimeUTC, + EndTimeUTC = raid.EndTimeUTC, + RaidId = raid.RaidId + }; + apiRaid.DisocrdMessages = ConvertMessages(raid.DiscordRaidMessages); + apiRaid.Roles = new List(); + foreach(RaidRole role in raid.Roles) + { + ApiRaid.Role apiRole = new ApiRaid.Role(){ + Description = role.Description, + Name = role.Name, + Spots = role.Spots + }; + apiRole.Users = new List(); + + foreach(RaidSignUp signUp in raid.SignUps.Where(x => x.RaidRoleId == role.RaidRoleId)) + { + apiRole.Users.Add(new ApiRaid.Role.User(){ + AccountName = signUp.GuildWars2Account.AccountName, + Status = signUp.SignUpType.ToString(), + UserName = signUp.LiebUser.Name + }); + } + apiRaid.Roles.Add(apiRole); + } + return apiRaid; + } + + private List ConvertMessages(IEnumerable messages) + { + List apiMessages = new List(); + foreach(DiscordRaidMessage message in messages) + { + apiMessages.Add(new ApiRaid.DiscordMessage(){ + ChannelId = message.DiscordChannelId, + GuildId = message.DiscordGuildId, + MessageId = message.DiscordMessageId, + WebsiteDatabaseId = message.DiscordRaidMessageId + }); + } + return apiMessages; + } + + private ApiUserReminder ConvertUserReminder(RaidReminder reminder, Raid raid) + { + ApiUserReminder apiReminder = new ApiUserReminder() + { + Message = reminder.Message + }; + apiReminder.UserIds = new List(); + foreach(RaidSignUp signUp in raid.SignUps) + { + apiReminder.UserIds.Add(signUp.LiebUserId); + } + return apiReminder; + } + + private ApiChannelReminder ConvertChannelReminder(RaidReminder reminder) + { + return new ApiChannelReminder() + { + DiscordServerId = reminder.DiscordServerId, + DiscordChannelId = reminder.DiscordChannelId, + Message = reminder.Message + }; + } + } +} \ No newline at end of file diff --git a/Lieb/Data/RaidService.cs b/Lieb/Data/RaidService.cs index 2ca9790..0e995d2 100644 --- a/Lieb/Data/RaidService.cs +++ b/Lieb/Data/RaidService.cs @@ -7,10 +7,12 @@ namespace Lieb.Data public class RaidService { private readonly IDbContextFactory _contextFactory; + private readonly DiscordService _discordService; - public RaidService(IDbContextFactory contextFactory) + public RaidService(IDbContextFactory contextFactory, DiscordService discordService) { _contextFactory = contextFactory; + _discordService = discordService; } public List GetRaids() @@ -26,6 +28,7 @@ namespace Lieb.Data .ThenInclude(s => s.GuildWars2Account) .Include(r => r.SignUps) .ThenInclude(s => s.RaidRole) + .Include(r => r.DiscordRaidMessages) .ToList(); } @@ -42,10 +45,11 @@ namespace Lieb.Data .ThenInclude(s => s.GuildWars2Account) .Include(r => r.SignUps) .ThenInclude(s => s.RaidRole) + .Include(r => r.DiscordRaidMessages) .FirstOrDefault(r => r.RaidId == raidId); } - public async Task AddOrEditRaid(Raid raid, List rolesToDelete, List remindersToDelete) + public async Task AddOrEditRaid(Raid raid, List rolesToDelete, List remindersToDelete, List messagesToDelete) { if (raid != null) { @@ -60,6 +64,7 @@ namespace Lieb.Data context.Update(raid); context.RaidRoles.RemoveRange(rolesToDelete); context.RaidReminders.RemoveRange(remindersToDelete); + context.DiscordRaidMessages.RemoveRange(messagesToDelete); //move users back to "Random" role if (raid.RaidType != RaidType.Planned) @@ -74,6 +79,7 @@ namespace Lieb.Data await context.SaveChangesAsync(); } + _discordService.PostRaidMessage(raid.RaidId); } } @@ -88,6 +94,7 @@ namespace Lieb.Data await context.SaveChangesAsync(); context.Raids.Remove(raid); await context.SaveChangesAsync(); + _discordService.DeleteRaidMessages(raid); } public async Task SignUp(int raidId, ulong liebUserId, int guildWars2AccountId, int plannedRoleId, SignUpType signUpType) @@ -115,6 +122,7 @@ namespace Lieb.Data }); await context.SaveChangesAsync(); } + _discordService.PostRaidMessage(raidId); } public async Task SignOff(int raidId, ulong liebUserId) @@ -138,8 +146,8 @@ namespace Lieb.Data signUp.RaidRole = raid.Roles.FirstOrDefault(r => r.IsRandomSignUpRole); } } - await context.SaveChangesAsync(); + _discordService.PostRaidMessage(raidId); } public async Task ChangeAccount(int raidId, ulong liebUserId, int guildWars2AccountId) @@ -151,6 +159,7 @@ namespace Lieb.Data signUp.GuildWars2AccountId = guildWars2AccountId; } await context.SaveChangesAsync(); + _discordService.PostRaidMessage(raidId); } public void ChangeSignUpType(int raidId, ulong liebUserId, int plannedRoleId, SignUpType signUpType) @@ -180,6 +189,7 @@ namespace Lieb.Data signUp.SignUpType = signUpType; } context.SaveChanges(); + _discordService.PostRaidMessage(raidId); } public bool IsRoleSignUpAllowed(ulong liebUserId, int plannedRoleId, SignUpType signUpType) @@ -334,9 +344,8 @@ namespace Lieb.Data using var context = _contextFactory.CreateDbContext(); List raids = context.Raids .Include(r => r.Reminders) - .Where(raid => raid.Reminders.Where(reminder => !reminder.Sent && raid.StartTimeUTC.AddHours(-reminder.HoursBeforeRaid) < DateTime.UtcNow).Any()) .ToList(); - + foreach(Raid raid in raids) { foreach(RaidReminder reminder in raid.Reminders.Where(reminder => !reminder.Sent && raid.StartTimeUTC.AddHours(-reminder.HoursBeforeRaid) < DateTime.UtcNow)) diff --git a/Lieb/Discord/CommandHandler.cs b/Lieb/Discord/CommandHandler.cs new file mode 100644 index 0000000..8f3c2a3 --- /dev/null +++ b/Lieb/Discord/CommandHandler.cs @@ -0,0 +1,62 @@ +using Discord.Commands; +using Discord.WebSocket; +using System.Reflection; + +namespace Lieb.Discord +{ + public class CommandHandler + { + private readonly DiscordSocketClient _client; + private readonly CommandService _commands; + + // Retrieve client and CommandService instance via ctor + public CommandHandler(DiscordSocketClient client, CommandService commands) + { + _commands = commands; + _client = client; + } + + public async Task InstallCommandsAsync() + { + // Hook the MessageReceived event into our command handler + _client.MessageReceived += HandleCommandAsync; + + // Here we discover all of the command modules in the entry + // assembly and load them. Starting from Discord.NET 2.0, a + // service provider is required to be passed into the + // module registration method to inject the + // required dependencies. + // + // If you do not use Dependency Injection, pass null. + // See Dependency Injection guide for more information. + await _commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(), + services: null); + } + + private async Task HandleCommandAsync(SocketMessage messageParam) + { + // Don't process the command if it was a system message + var message = messageParam as SocketUserMessage; + if (message == null) return; + + // Create a number to track where the prefix ends and the command begins + int argPos = 0; + + // Determine if the message is a command based on the prefix and make sure no bots trigger commands + if (!(message.HasCharPrefix('!', ref argPos) || + message.HasMentionPrefix(_client.CurrentUser, ref argPos)) || + message.Author.IsBot) + return; + + // Create a WebSocket-based command context based on the message + var context = new SocketCommandContext(_client, message); + + // Execute the command with the command context we just + // created, along with the service provider for precondition checks. + await _commands.ExecuteAsync( + context: context, + argPos: argPos, + services: null); + } + } +} diff --git a/Lieb/Discord/Messages/RaidMessage.cs b/Lieb/Discord/Messages/RaidMessage.cs new file mode 100644 index 0000000..b209154 --- /dev/null +++ b/Lieb/Discord/Messages/RaidMessage.cs @@ -0,0 +1,25 @@ +using Lieb.Models.GuildWars2.Raid; + +namespace Lieb.Discord.Messages +{ + public class RaidMessage + { + private Raid _raid; + + public RaidMessage(Raid raid) + { + _raid = raid; + } + + public void PostRaidMessage() + { + + } + + public void UpdateRaidMessage() + { + + } + + } +} diff --git a/Lieb/Lieb.csproj b/Lieb/Lieb.csproj index fc40b36..311cbe3 100644 --- a/Lieb/Lieb.csproj +++ b/Lieb/Lieb.csproj @@ -7,7 +7,9 @@ + + all @@ -18,7 +20,7 @@ - + diff --git a/Shared/Models/GuildWars2/Equipped.cs b/Lieb/Models/GuildWars2/Equipped.cs similarity index 100% rename from Shared/Models/GuildWars2/Equipped.cs rename to Lieb/Models/GuildWars2/Equipped.cs diff --git a/Shared/Models/GuildWars2/GuildWars2Account.cs b/Lieb/Models/GuildWars2/GuildWars2Account.cs similarity index 100% rename from Shared/Models/GuildWars2/GuildWars2Account.cs rename to Lieb/Models/GuildWars2/GuildWars2Account.cs diff --git a/Shared/Models/GuildWars2/GuildWars2Build.cs b/Lieb/Models/GuildWars2/GuildWars2Build.cs similarity index 100% rename from Shared/Models/GuildWars2/GuildWars2Build.cs rename to Lieb/Models/GuildWars2/GuildWars2Build.cs diff --git a/Shared/Models/GuildWars2/Raid/DiscordRaidMessage.cs b/Lieb/Models/GuildWars2/Raid/DiscordRaidMessage.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/DiscordRaidMessage.cs rename to Lieb/Models/GuildWars2/Raid/DiscordRaidMessage.cs diff --git a/Shared/Models/GuildWars2/Raid/Raid.cs b/Lieb/Models/GuildWars2/Raid/Raid.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/Raid.cs rename to Lieb/Models/GuildWars2/Raid/Raid.cs diff --git a/Shared/Models/GuildWars2/Raid/RaidBase.cs b/Lieb/Models/GuildWars2/Raid/RaidBase.cs similarity index 96% rename from Shared/Models/GuildWars2/Raid/RaidBase.cs rename to Lieb/Models/GuildWars2/Raid/RaidBase.cs index 05832a3..b48ba66 100644 --- a/Shared/Models/GuildWars2/Raid/RaidBase.cs +++ b/Lieb/Models/GuildWars2/Raid/RaidBase.cs @@ -67,7 +67,8 @@ namespace Lieb.Models.GuildWars2.Raid { this.Reminders.Add(new RaidReminder() { - ChannelId = reminder.ChannelId, + DiscordServerId = reminder.DiscordServerId, + DiscordChannelId = reminder.DiscordChannelId, HoursBeforeRaid = reminder.HoursBeforeRaid, Message = reminder.Message, Sent = reminder.Sent, diff --git a/Shared/Models/GuildWars2/Raid/RaidReminder.cs b/Lieb/Models/GuildWars2/Raid/RaidReminder.cs similarity index 78% rename from Shared/Models/GuildWars2/Raid/RaidReminder.cs rename to Lieb/Models/GuildWars2/Raid/RaidReminder.cs index b52be77..bcd0cc1 100644 --- a/Shared/Models/GuildWars2/Raid/RaidReminder.cs +++ b/Lieb/Models/GuildWars2/Raid/RaidReminder.cs @@ -23,8 +23,14 @@ namespace Lieb.Models.GuildWars2.Raid [Required] public double HoursBeforeRaid { get; set; } - public ulong ChannelId { get; set; } + public ulong DiscordServerId { get; set; } + + public ulong DiscordChannelId { get; set; } public bool Sent { get; set; } = false; + + public int RaidId { get; set; } + + public Raid Raid { get; set; } } } diff --git a/Shared/Models/GuildWars2/Raid/RaidRole.cs b/Lieb/Models/GuildWars2/Raid/RaidRole.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/RaidRole.cs rename to Lieb/Models/GuildWars2/Raid/RaidRole.cs diff --git a/Shared/Models/GuildWars2/Raid/RaidSignUp.cs b/Lieb/Models/GuildWars2/Raid/RaidSignUp.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/RaidSignUp.cs rename to Lieb/Models/GuildWars2/Raid/RaidSignUp.cs diff --git a/Shared/Models/GuildWars2/Raid/RaidSignUpHistory.cs b/Lieb/Models/GuildWars2/Raid/RaidSignUpHistory.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/RaidSignUpHistory.cs rename to Lieb/Models/GuildWars2/Raid/RaidSignUpHistory.cs diff --git a/Shared/Models/GuildWars2/Raid/RaidTemplate.cs b/Lieb/Models/GuildWars2/Raid/RaidTemplate.cs similarity index 100% rename from Shared/Models/GuildWars2/Raid/RaidTemplate.cs rename to Lieb/Models/GuildWars2/Raid/RaidTemplate.cs diff --git a/Shared/Models/LiebRole.cs b/Lieb/Models/LiebRole.cs similarity index 100% rename from Shared/Models/LiebRole.cs rename to Lieb/Models/LiebRole.cs diff --git a/Shared/Models/LiebUser.cs b/Lieb/Models/LiebUser.cs similarity index 100% rename from Shared/Models/LiebUser.cs rename to Lieb/Models/LiebUser.cs diff --git a/Shared/Models/RoleAssignment.cs b/Lieb/Models/RoleAssignment.cs similarity index 100% rename from Shared/Models/RoleAssignment.cs rename to Lieb/Models/RoleAssignment.cs diff --git a/Lieb/Pages/Raids/RaidEdit.razor b/Lieb/Pages/Raids/RaidEdit.razor index 3ec423b..e7fb75d 100644 --- a/Lieb/Pages/Raids/RaidEdit.razor +++ b/Lieb/Pages/Raids/RaidEdit.razor @@ -5,8 +5,10 @@ @using Lieb.Models.GuildWars2.Raid @using System.ComponentModel.DataAnnotations @using System.Security.Claims +@using SharedClasses.SharedModels @inject RaidService RaidService @inject UserService UserService +@inject DiscordService DiscordService @inject TimeZoneService TimeZoneService @inject NavigationManager NavigationManager @inject AuthenticationStateProvider AuthenticationStateProvider @@ -143,6 +145,46 @@

} +

+ + + + + + + + @foreach( DiscordRaidMessage message in _raid.DiscordRaidMessages) + { + bool disableEdit = message.DiscordRaidMessageId != 0; + + + + + + } +
ServerChannel
+ + @foreach(DiscordServer item in _discordServers) + { + + } + + + + @if(message.DiscordGuildId > 0) + { + List channels = _discordServers.Where(s => s.Id == message.DiscordGuildId).FirstOrDefault(new DiscordServer()).Channels; + @foreach(DiscordChannel item in channels) + { + + } + } + +
+

+ @@ -168,6 +210,10 @@ private DateTimeOffset _freeForAllTime; private List _rolesToDelete = new List(); + private List _remindersToDelete = new List(); + private List _messagesToDelete = new List(); + + private List _discordServers = new List(); @@ -206,6 +252,8 @@ { _raid = new Raid(); } + + _discordServers = await DiscordService.GetServers(); } async Task AddRoleClicked() @@ -222,6 +270,20 @@ } _raid.Roles.Remove(role); } + + async Task AddDiscordMessageClicked() + { + _raid.DiscordRaidMessages.Add(new DiscordRaidMessage()); + } + + async Task DeleteMessageClicked(DiscordRaidMessage message) + { + if(message.DiscordRaidMessageId != 0) + { + _messagesToDelete.Add(message); + } + _raid.DiscordRaidMessages.Remove(message); + } async Task DeleteRaidClicked() { @@ -276,7 +338,7 @@ _raid.RaidOwnerId = _user.Id; } - await RaidService.AddOrEditRaid(_raid, _rolesToDelete, new List()); + await RaidService.AddOrEditRaid(_raid, _rolesToDelete, _remindersToDelete, _messagesToDelete); NavigationManager.NavigateTo("raidoverview"); } } \ No newline at end of file diff --git a/Lieb/Pages/User/UserEdit.razor b/Lieb/Pages/User/UserEdit.razor index 178c980..4364038 100644 --- a/Lieb/Pages/User/UserEdit.razor +++ b/Lieb/Pages/User/UserEdit.razor @@ -1,5 +1,5 @@ @page "/useredit" -@page "/useredit/{userId}" +@page "/useredit/{_userId}" @using Lieb.Data @using Lieb.Models @using Lieb.Models.GuildWars2 @@ -59,6 +59,7 @@ } + hi
@code { diff --git a/Lieb/Program.cs b/Lieb/Program.cs index 4453030..88cf0c3 100644 --- a/Lieb/Program.cs +++ b/Lieb/Program.cs @@ -1,7 +1,12 @@ -using Discord.OAuth2; +using Discord; +using Discord.Commands; +using Discord.OAuth2; +using Discord.WebSocket; using Lieb.Data; +using Lieb.Discord; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.EntityFrameworkCore; +using Microsoft.Net.Http.Headers; var builder = WebApplication.CreateBuilder(args); @@ -10,7 +15,8 @@ builder.Services.AddRazorPages(); #if DEBUG builder.Services.AddDbContextFactory(opt => - opt.UseSqlServer(builder.Configuration.GetConnectionString("LiebContext")).EnableSensitiveDataLogging(), ServiceLifetime.Transient); + //opt.UseSqlServer(builder.Configuration.GetConnectionString("LiebContext")).EnableSensitiveDataLogging(), ServiceLifetime.Transient); + opt.UseSqlite(builder.Configuration.GetConnectionString("LiebContext"))); #else builder.Services.AddDbContextFactory(opt => opt.UseMySql(builder.Configuration.GetConnectionString("LiebContext"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("LiebContext"))), ServiceLifetime.Transient); @@ -27,8 +33,21 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddHostedService(); +/* +#region discord +//DiscordSocketClient client = new DiscordSocketClient(); +//client.Log += Log; +var token = File.ReadAllText("token.txt"); + +await client.LoginAsync(TokenType.Bot, token); +await client.StartAsync(); +builder.Services.AddSingleton(client); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +#endregion discord*/ builder.Services.AddAuthentication(opt => { @@ -60,6 +79,24 @@ builder.Services.AddAuthorization(options => Constants.Roles.Admin.Name })); }); +builder.Services.AddHttpClient(Constants.HttpClientName , httpClient => +{ + httpClient.BaseAddress = new Uri("http://localhost:5240/"); + + // using Microsoft.Net.Http.Headers; + // The GitHub API requires two headers. + 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(); diff --git a/Lieb/appsettings.Development.json b/Lieb/appsettings.Development.json index 96313d9..3770fe7 100644 --- a/Lieb/appsettings.Development.json +++ b/Lieb/appsettings.Development.json @@ -8,6 +8,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "LiebContext": "Server=(localdb)\\mssqllocaldb;Database=LiebContext-f9dae0eb-8d5c-495d-ab99-da7951e0638b;Trusted_Connection=True;MultipleActiveResultSets=true" + "LiebContext": "Data Source=./Database/mydb.sqlite;" } } diff --git a/Shared/Class1.cs b/Shared/Class1.cs deleted file mode 100644 index 6cb5fcd..0000000 --- a/Shared/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Shared -{ - public class Class1 - { - - } -} \ No newline at end of file diff --git a/Shared/Shared.csproj b/SharedClasses/SharedClasses.csproj similarity index 65% rename from Shared/Shared.csproj rename to SharedClasses/SharedClasses.csproj index 132c02c..6d8d65f 100644 --- a/Shared/Shared.csproj +++ b/SharedClasses/SharedClasses.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/SharedClasses/SharedModels/ApiRaid.cs b/SharedClasses/SharedModels/ApiRaid.cs new file mode 100644 index 0000000..57102fd --- /dev/null +++ b/SharedClasses/SharedModels/ApiRaid.cs @@ -0,0 +1,58 @@ + +namespace SharedClasses.SharedModels +{ + public class ApiRaid + { + public string Title { get; set; } = String.Empty; + + public string Description { get; set; } = String.Empty; + + public string Organizer { get; set; } = String.Empty; + + public string Guild { get; set; } = String.Empty; + + public string VoiceChat { get; set; } = String.Empty; + + public int RaidId { get; set; } + + public DateTimeOffset StartTimeUTC { get; set; } = DateTime.Now; + + public DateTimeOffset EndTimeUTC { get; set; } + + public List Roles { get; set; } = new List(); + + public List DisocrdMessages { get; set; } = new List(); + + public class DiscordMessage + { + public ulong GuildId { get; set; } = 0; + + public ulong ChannelId { get; set; } = 0; + + public ulong MessageId { get; set; } = 0; + + public int WebsiteDatabaseId {get; set; } = 0; + } + + public class Role + { + public string Name { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + public int Spots { get; set; } = 1; + + public List Users { get; set; } + + + public class User + { + public string UserName { get; set; } = string.Empty; + + public string AccountName { get; set; } = string.Empty; + + public string Status { get; set; } = string.Empty; + } + } + } +} \ No newline at end of file diff --git a/SharedClasses/SharedModels/ApiReminder.cs b/SharedClasses/SharedModels/ApiReminder.cs new file mode 100644 index 0000000..61e6ded --- /dev/null +++ b/SharedClasses/SharedModels/ApiReminder.cs @@ -0,0 +1,19 @@ + +namespace SharedClasses.SharedModels +{ + public class ApiUserReminder + { + public string Message { get; set; } + + public List UserIds {get; set;} = new List(); + } + + public class ApiChannelReminder + { + public string Message { get; set; } + + public ulong DiscordServerId { get; set; } + + public ulong DiscordChannelId { get; set; } + } +} \ No newline at end of file diff --git a/SharedClasses/SharedModels/ApiRole.cs b/SharedClasses/SharedModels/ApiRole.cs new file mode 100644 index 0000000..3db3395 --- /dev/null +++ b/SharedClasses/SharedModels/ApiRole.cs @@ -0,0 +1,12 @@ + +namespace SharedClasses.SharedModels +{ + public class ApiRole + { + public string Name { get; set; } = String.Empty; + + public string Description { get; set; } = String.Empty; + + public bool IsSignUpAllowed{ get; set; } = false; + } +} \ No newline at end of file diff --git a/SharedClasses/SharedModels/ApiSignUp.cs b/SharedClasses/SharedModels/ApiSignUp.cs new file mode 100644 index 0000000..cad4f53 --- /dev/null +++ b/SharedClasses/SharedModels/ApiSignUp.cs @@ -0,0 +1,11 @@ + +namespace SharedClasses.SharedModels +{ + public class ApiSignUp + { + public int raidId {get; set;} + public ulong userId {get; set;} + public int gw2AccountId {get; set;} + public int roleId {get; set;} + } +} \ No newline at end of file diff --git a/SharedClasses/SharedModels/DiscordServer.cs b/SharedClasses/SharedModels/DiscordServer.cs new file mode 100644 index 0000000..088185e --- /dev/null +++ b/SharedClasses/SharedModels/DiscordServer.cs @@ -0,0 +1,16 @@ + +namespace SharedClasses.SharedModels +{ + public class DiscordServer + { + public string Name {get; set;} + public ulong Id {get; set;} + public List Channels {get; set;} = new List(); + } + + public class DiscordChannel + { + public string Name {get; set;} + public ulong Id {get; set;} + } +} \ No newline at end of file