diff --git a/.gitignore b/.gitignore index f81f644..b7ebbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ obj/ /ChaosBot/obj/ .idea/ /ChaosBot/appsettings.json +/ChaosBot/appsettings.*.json /ChaosBot/ChaosBotSQL.db *.sln.DotSettings.user /.vs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f540351 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ChaosBot/wwwroot"] + path = ChaosBot/wwwroot + url = ssh://git@git.chaoticlogic.us:2302/discord-bots/chaosbot-frontend.git diff --git a/ChaosBot/ChaosBot.csproj b/ChaosBot/ChaosBot.csproj index 67c2ad6..c1053c2 100644 --- a/ChaosBot/ChaosBot.csproj +++ b/ChaosBot/ChaosBot.csproj @@ -23,13 +23,22 @@ - - PreserveNewest - + + PreserveNewest + + + + + + + true + PreserveNewest + PreserveNewest + diff --git a/ChaosBot/Discord/Services/CommandHandler.cs b/ChaosBot/Discord/Services/CommandHandler.cs index f2d2290..73841d7 100644 --- a/ChaosBot/Discord/Services/CommandHandler.cs +++ b/ChaosBot/Discord/Services/CommandHandler.cs @@ -76,8 +76,11 @@ namespace ChaosBot.Discord.Services if(Convert.ToBoolean(ConfigurationRepository.GetValue("Experience:Commands", context.Guild.Id, "false"))) ExperienceHandler.AddXp(context); + + bool customCommandExecuted = await CustomCommandHandler.CheckCommand(context, argPos); - await _commands.ExecuteAsync(context, argPos, _services); + if (!customCommandExecuted) + await _commands.ExecuteAsync(context, argPos, _services); } catch (Exception ex) { diff --git a/ChaosBot/Discord/Services/CustomCommandHandler.cs b/ChaosBot/Discord/Services/CustomCommandHandler.cs new file mode 100644 index 0000000..536b8de --- /dev/null +++ b/ChaosBot/Discord/Services/CustomCommandHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using ChaosBot.Models; +using Discord.Commands; +using NLog; + +namespace ChaosBot.Discord.Services +{ + public static class CustomCommandHandler + { + private static readonly ILogger Logger = Program.Logger; + + public static async Task CheckCommand(SocketCommandContext context, int argPos) + { + try + { + await using ChaosbotContext dbContext = new ChaosbotContext(); + string command = context.Message.Content.Substring(argPos); + + IQueryable customCommandQuery = dbContext.CustomCommands; + CustomCommand customCommand = customCommandQuery + .Where(cc => cc.DiscordGuildId == context.Guild.Id) + .FirstOrDefault(cc => command.StartsWith((string) cc.Command)); + + if (customCommand == null) return false; + + if (customCommand.Type == CustomCommandType.Basic) + { + await context.Channel.SendMessageAsync(customCommand.Content); + } + else + { + throw new NotImplementedException($"No support for command type ${customCommand.Type}"); + } + + return true; + } + catch (Exception ex) + { + Logger.Error($"{MethodBase.GetCurrentMethod().ReflectedType.FullName}: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + return false; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Migrations/20200824124830_CustomCommand.Designer.cs b/ChaosBot/Migrations/20200824124830_CustomCommand.Designer.cs new file mode 100644 index 0000000..f256454 --- /dev/null +++ b/ChaosBot/Migrations/20200824124830_CustomCommand.Designer.cs @@ -0,0 +1,182 @@ +// +using System; +using ChaosBot.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ChaosBot.Migrations +{ + [DbContext(typeof(ChaosbotContext))] + [Migration("20200824124830_CustomCommand")] + partial class CustomCommand + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("ChaosBot.Models.CommandPermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint unsigned"); + + b.Property("Command") + .IsRequired() + .HasColumnType("varchar(128) CHARACTER SET utf8mb4") + .HasMaxLength(128); + + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("TargetId") + .HasColumnType("bigint unsigned"); + + b.Property("TargetType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CommandPermissions"); + }); + + modelBuilder.Entity("ChaosBot.Models.Configuration", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("Key") + .HasColumnType("varchar(128) CHARACTER SET utf8mb4") + .HasMaxLength(128); + + b.Property("SerializedValue") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("DiscordGuildId", "Key"); + + b.ToTable("Configuration"); + }); + + modelBuilder.Entity("ChaosBot.Models.Experience", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordUserId") + .HasColumnType("bigint unsigned"); + + b.Property("Amount") + .HasColumnType("bigint unsigned"); + + b.Property("LastUpdated") + .HasColumnType("datetime"); + + b.Property("Level") + .HasColumnType("bigint unsigned"); + + b.HasKey("DiscordGuildId", "DiscordUserId"); + + b.ToTable("ExperiencePoints"); + }); + + modelBuilder.Entity("ChaosBot.Models.LodestoneCharacter", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("LodestoneId") + .HasColumnType("bigint unsigned"); + + b.Property("Avatar") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("DiscordUserId") + .HasColumnType("bigint unsigned"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("DiscordGuildId", "LodestoneId"); + + b.ToTable("LodestoneCharacter"); + }); + + modelBuilder.Entity("ChaosBot.Models.LodestoneFreeCompany", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("LodestoneId") + .HasColumnType("bigint unsigned"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("DiscordGuildId", "LodestoneId"); + + b.ToTable("LodestoneFreeCompany"); + }); + + modelBuilder.Entity("ChaosBot.Models.Point", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordUserId") + .HasColumnType("bigint unsigned"); + + b.Property("Amount") + .HasColumnType("bigint unsigned"); + + b.HasKey("DiscordGuildId", "DiscordUserId"); + + b.ToTable("Points"); + }); + + modelBuilder.Entity("ChaosBot.Models.Raffle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint unsigned"); + + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordUserId") + .HasColumnType("bigint unsigned"); + + b.HasKey("Id"); + + b.ToTable("Raffles"); + }); + + modelBuilder.Entity("ChaosBot.Models.RoleReaction", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordMessageId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordRoleId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordEmoteNameEncoded") + .HasColumnType("varchar(95) CHARACTER SET utf8mb4"); + + b.HasKey("DiscordGuildId", "DiscordMessageId", "DiscordRoleId", "DiscordEmoteNameEncoded"); + + b.ToTable("RoleReactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ChaosBot/Migrations/20200824124830_CustomCommand.cs b/ChaosBot/Migrations/20200824124830_CustomCommand.cs new file mode 100644 index 0000000..ec5acb0 --- /dev/null +++ b/ChaosBot/Migrations/20200824124830_CustomCommand.cs @@ -0,0 +1,32 @@ +using ChaosBot.Models; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ChaosBot.Migrations +{ + public partial class CustomCommand : Migration + { + private readonly string Table = "CustomCommands"; + + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: Table, + columns: table => new + { + DiscordGuildId = table.Column(nullable: false), + Command = table.Column(nullable: false, maxLength: 128), + Type = table.Column(nullable: false), + Content = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomCommands", x => new { x.DiscordGuildId, x.Command }); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable(name: Table); + } + } +} diff --git a/ChaosBot/Migrations/ChaosbotContextModelSnapshot.cs b/ChaosBot/Migrations/ChaosbotContextModelSnapshot.cs index 449c819..9abe71a 100644 --- a/ChaosBot/Migrations/ChaosbotContextModelSnapshot.cs +++ b/ChaosBot/Migrations/ChaosbotContextModelSnapshot.cs @@ -155,6 +155,25 @@ namespace ChaosBot.Migrations b.ToTable("Raffles"); }); + + modelBuilder.Entity("ChaosBot.Models.RoleReaction", b => + { + b.Property("DiscordGuildId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordMessageId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordRoleId") + .HasColumnType("bigint unsigned"); + + b.Property("DiscordEmoteNameEncoded") + .HasColumnType("varchar(95) CHARACTER SET utf8mb4"); + + b.HasKey("DiscordGuildId", "DiscordMessageId", "DiscordRoleId", "DiscordEmoteNameEncoded"); + + b.ToTable("RoleReactions"); + }); #pragma warning restore 612, 618 } } diff --git a/ChaosBot/Models/ChaosbotContext.cs b/ChaosBot/Models/ChaosbotContext.cs index bb6c866..ced2e6f 100644 --- a/ChaosBot/Models/ChaosbotContext.cs +++ b/ChaosBot/Models/ChaosbotContext.cs @@ -13,6 +13,7 @@ namespace ChaosBot.Models public DbSet Configuration { get; set; } public DbSet ExperiencePoints { get; set; } public DbSet RoleReactions { get; set; } + public DbSet CustomCommands { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -49,6 +50,8 @@ namespace ChaosBot.Models .HasKey(x => new {x.DiscordGuildId, x.Key}); modelBuilder.Entity() .HasKey(x => new {x.DiscordGuildId, x.DiscordMessageId, x.DiscordRoleId, x.DiscordEmoteNameEncoded}); + modelBuilder.Entity() + .HasKey(x => new {x.DiscordGuildId, x.Command}); } } } \ No newline at end of file diff --git a/ChaosBot/Models/CustomCommand.cs b/ChaosBot/Models/CustomCommand.cs new file mode 100644 index 0000000..d6fa2bb --- /dev/null +++ b/ChaosBot/Models/CustomCommand.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace ChaosBot.Models +{ + #region Required + public class CustomCommand + { + [Required] + public ulong DiscordGuildId { get; set; } + [Required] + [MaxLength(128)] + public string Command { get; set; } + [Required] + public CustomCommandType Type { get; set; } + [Required] + public string Content { get; set; } + } + #endregion + + public enum CustomCommandType + { + Basic + } +} \ No newline at end of file diff --git a/ChaosBot/WebServer/Startup.cs b/ChaosBot/WebServer/Startup.cs index 626847d..c36d89e 100644 --- a/ChaosBot/WebServer/Startup.cs +++ b/ChaosBot/WebServer/Startup.cs @@ -1,3 +1,4 @@ +using System.IO; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -36,12 +37,20 @@ namespace ChaosBot.WebServer { _logger.Info("Initializing Kestrel Startup and Configuration"); + if (Program.AppSettingsHandler.GetValue("WebServer:Debug", false)) + app.UseDeveloperExceptionPage(); + app.UseForwardedHeaders(); - app.UseDeveloperExceptionPage(); app.UseMiddleware(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + + DefaultFilesOptions options = new DefaultFilesOptions(); + options.DefaultFileNames.Clear(); + options.DefaultFileNames.Add("index.html"); + app.UseDefaultFiles(options); + app.UseStaticFiles(); } } } \ No newline at end of file diff --git a/ChaosBot/WebServer/WebServer.cs b/ChaosBot/WebServer/WebServer.cs index c3a3970..f9c1492 100644 --- a/ChaosBot/WebServer/WebServer.cs +++ b/ChaosBot/WebServer/WebServer.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Net; using NLog.Extensions.Logging; @@ -19,6 +20,11 @@ namespace ChaosBot.WebServer Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { + string contentRoot = Directory.GetCurrentDirectory(); + string webRoot = Path.Combine(contentRoot, "wwwroot/dist"); + + webBuilder.UseContentRoot(contentRoot); + webBuilder.UseWebRoot(webRoot); webBuilder.ConfigureKestrel(serverOptions => { serverOptions.Listen(IPAddress.Any, Program.AppSettingsHandler.GetValue("WebServer:Port"), diff --git a/ChaosBot/wwwroot b/ChaosBot/wwwroot new file mode 160000 index 0000000..ad857be --- /dev/null +++ b/ChaosBot/wwwroot @@ -0,0 +1 @@ +Subproject commit ad857be54bdaf13813a00458c242bcc808db89ec