From 4c3791c091d09db2958bc932e0f4586355bc46d0 Mon Sep 17 00:00:00 2001 From: Karolis2011 Date: Tue, 18 Jun 2019 23:16:01 +0300 Subject: [PATCH] More DB staff and house keeping and better emoji --- EventBot/EventBot.csproj | 14 +- ...20190618165006_InitialDatabase.Designer.cs | 140 ++++++++++++++++++ .../Sqlite/20190618165006_InitialDatabase.cs | 138 +++++++++++++++++ .../SqliteDatabaseServiceModelSnapshot.cs | 138 +++++++++++++++++ EventBot/Misc/EmoteHelper.cs | 79 ++++++++++ EventBot/Misc/EventTypeReader.cs | 4 +- EventBot/Modules/EventModule.cs | 118 +++++++++++++-- EventBot/Program.cs | 1 - EventBot/Services/DatabaseService.cs | 3 +- EventBot/Services/EmoteService.cs | 46 ------ EventBot/Services/EventManagementService.cs | 4 +- EventBot/Services/MySqlDatabaseService.cs | 2 + EventBot/Services/SqliteDatabaseService.cs | 1 + README | 6 + 14 files changed, 619 insertions(+), 75 deletions(-) create mode 100644 EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.Designer.cs create mode 100644 EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.cs create mode 100644 EventBot/Migrations/Sqlite/SqliteDatabaseServiceModelSnapshot.cs create mode 100644 EventBot/Misc/EmoteHelper.cs delete mode 100644 EventBot/Services/EmoteService.cs create mode 100644 README diff --git a/EventBot/EventBot.csproj b/EventBot/EventBot.csproj index b29189f..25ce4df 100644 --- a/EventBot/EventBot.csproj +++ b/EventBot/EventBot.csproj @@ -6,14 +6,7 @@ - - - - - - - - + @@ -28,7 +21,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -37,8 +29,4 @@ - - - - diff --git a/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.Designer.cs b/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.Designer.cs new file mode 100644 index 0000000..0c1d9bd --- /dev/null +++ b/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.Designer.cs @@ -0,0 +1,140 @@ +// +using System; +using EventBot.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace EventBot.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseService))] + [Migration("20190618165006_InitialDatabase")] + partial class InitialDatabase + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("EventBot.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Description"); + + b.Property("GuildId"); + + b.Property("MessageChannelId"); + + b.Property("MessageId"); + + b.Property("Opened"); + + b.Property("Title"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("EventBot.Entities.EventParticipant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EventId"); + + b.Property("EventRoleId"); + + b.Property("UserData"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.HasIndex("EventRoleId"); + + b.ToTable("EventParticipants"); + }); + + modelBuilder.Entity("EventBot.Entities.EventRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Emote"); + + b.Property("EventId"); + + b.Property("MaxParticipants"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("EventRoles"); + }); + + modelBuilder.Entity("EventBot.Entities.GuildConfig", b => + { + b.Property("GuildId") + .ValueGeneratedOnAdd(); + + b.Property("EventRoleConfirmationChannelId"); + + b.Property("ParticipantRoleId"); + + b.Property("Prefix"); + + b.HasKey("GuildId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("EventBot.Entities.Event", b => + { + b.HasOne("EventBot.Entities.GuildConfig", "Guild") + .WithMany("Events") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("EventBot.Entities.EventParticipant", b => + { + b.HasOne("EventBot.Entities.Event", "Event") + .WithMany("Participants") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("EventBot.Entities.EventRole", "Role") + .WithMany("Participants") + .HasForeignKey("EventRoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("EventBot.Entities.EventRole", b => + { + b.HasOne("EventBot.Entities.Event", "Event") + .WithMany("Roles") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.cs b/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.cs new file mode 100644 index 0000000..fb61cbf --- /dev/null +++ b/EventBot/Migrations/Sqlite/20190618165006_InitialDatabase.cs @@ -0,0 +1,138 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace EventBot.Migrations.Sqlite +{ + public partial class InitialDatabase : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "GuildConfigs", + columns: table => new + { + GuildId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Prefix = table.Column(nullable: true), + EventRoleConfirmationChannelId = table.Column(nullable: false), + ParticipantRoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GuildConfigs", x => x.GuildId); + }); + + migrationBuilder.CreateTable( + name: "Events", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + Description = table.Column(nullable: true), + Active = table.Column(nullable: false), + MessageId = table.Column(nullable: false), + MessageChannelId = table.Column(nullable: false), + Opened = table.Column(nullable: false), + Type = table.Column(nullable: false), + GuildId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Events", x => x.Id); + table.ForeignKey( + name: "FK_Events_GuildConfigs_GuildId", + column: x => x.GuildId, + principalTable: "GuildConfigs", + principalColumn: "GuildId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "EventRoles", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + Description = table.Column(nullable: true), + Emote = table.Column(nullable: true), + MaxParticipants = table.Column(nullable: false), + EventId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EventRoles", x => x.Id); + table.ForeignKey( + name: "FK_EventRoles_Events_EventId", + column: x => x.EventId, + principalTable: "Events", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "EventParticipants", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + EventRoleId = table.Column(nullable: false), + EventId = table.Column(nullable: false), + UserId = table.Column(nullable: false), + UserData = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EventParticipants", x => x.Id); + table.ForeignKey( + name: "FK_EventParticipants_Events_EventId", + column: x => x.EventId, + principalTable: "Events", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EventParticipants_EventRoles_EventRoleId", + column: x => x.EventRoleId, + principalTable: "EventRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_EventParticipants_EventId", + table: "EventParticipants", + column: "EventId"); + + migrationBuilder.CreateIndex( + name: "IX_EventParticipants_EventRoleId", + table: "EventParticipants", + column: "EventRoleId"); + + migrationBuilder.CreateIndex( + name: "IX_EventRoles_EventId", + table: "EventRoles", + column: "EventId"); + + migrationBuilder.CreateIndex( + name: "IX_Events_GuildId", + table: "Events", + column: "GuildId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EventParticipants"); + + migrationBuilder.DropTable( + name: "EventRoles"); + + migrationBuilder.DropTable( + name: "Events"); + + migrationBuilder.DropTable( + name: "GuildConfigs"); + } + } +} diff --git a/EventBot/Migrations/Sqlite/SqliteDatabaseServiceModelSnapshot.cs b/EventBot/Migrations/Sqlite/SqliteDatabaseServiceModelSnapshot.cs new file mode 100644 index 0000000..0ce9158 --- /dev/null +++ b/EventBot/Migrations/Sqlite/SqliteDatabaseServiceModelSnapshot.cs @@ -0,0 +1,138 @@ +// +using System; +using EventBot.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace EventBot.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseService))] + partial class SqliteDatabaseServiceModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + modelBuilder.Entity("EventBot.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Description"); + + b.Property("GuildId"); + + b.Property("MessageChannelId"); + + b.Property("MessageId"); + + b.Property("Opened"); + + b.Property("Title"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("EventBot.Entities.EventParticipant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EventId"); + + b.Property("EventRoleId"); + + b.Property("UserData"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.HasIndex("EventRoleId"); + + b.ToTable("EventParticipants"); + }); + + modelBuilder.Entity("EventBot.Entities.EventRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Emote"); + + b.Property("EventId"); + + b.Property("MaxParticipants"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("EventRoles"); + }); + + modelBuilder.Entity("EventBot.Entities.GuildConfig", b => + { + b.Property("GuildId") + .ValueGeneratedOnAdd(); + + b.Property("EventRoleConfirmationChannelId"); + + b.Property("ParticipantRoleId"); + + b.Property("Prefix"); + + b.HasKey("GuildId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("EventBot.Entities.Event", b => + { + b.HasOne("EventBot.Entities.GuildConfig", "Guild") + .WithMany("Events") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("EventBot.Entities.EventParticipant", b => + { + b.HasOne("EventBot.Entities.Event", "Event") + .WithMany("Participants") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("EventBot.Entities.EventRole", "Role") + .WithMany("Participants") + .HasForeignKey("EventRoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("EventBot.Entities.EventRole", b => + { + b.HasOne("EventBot.Entities.Event", "Event") + .WithMany("Roles") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EventBot/Misc/EmoteHelper.cs b/EventBot/Misc/EmoteHelper.cs new file mode 100644 index 0000000..a1cbeed --- /dev/null +++ b/EventBot/Misc/EmoteHelper.cs @@ -0,0 +1,79 @@ +using Discord; +using System; + +namespace EventBot.Services +{ + + public static class EmoteHelper + { + //private IEnumerable emoji; + + private static readonly int[] EmojiRanges = + { + 0x1F600,0x1F64F, // Emoticons + 0x1F300,0x1F5FF, // Misc Symbols and Pictographs + 0x1F680,0x1F6FF, // Transport and Map + 0x1F1E6,0x1F1FF, // Regional country flags + 0x2600,0x26FF, // Misc symbols + 0x2700,0x27BF, // Dingbats + 0xE0020,0xE007F, // Tags + 0xFE00,0xFE0F, // Variation Selectors + 0x1F900,0x1F9FF, // Supplemental Symbols and Pictographs + 0x1F018,0x1F270, // Various asian characters + 0x238C,0x2454, // Misc items + 0x20D0,0x20FF // Combining Diacritical Marks for Symbols + }; + + public static bool TryParse(string input, out IEmote emote) + { + if(Emote.TryParse(input, out Emote parsedEmote)) + { + emote = parsedEmote; + return true; + } + if(isEmoji(input)) + { + emote = new Emoji(input); + return true; + } + emote = null; + return false; + } + + public static IEmote Parse(string input) + { + if (!TryParse(input, out IEmote parsed)) + throw new ArgumentException("Failed to parse emote."); + return parsed; + } + + public static bool isEmoji(string input) + { + if (input.Length % 2 == 1) { + return false; + } + for (var i = 0; i < input.Length; i += 2) + { + if (!char.IsSurrogatePair(input[i], input[i + 1])) + { + return false; + } + var utf32 = char.ConvertToUtf32(input[i], input[i + 1]); + if (utf32 != 0x200D && !isEmojiChar(utf32)) + { + return false; + } + } + return true; + } + + + public static bool isEmojiChar(int u32) + {; + for (int i = 0; i < EmojiRanges.Length; i+= 2) + if (u32 >= EmojiRanges[i] && u32 <= EmojiRanges[i + 1]) + return true; + return false; + } + } +} \ No newline at end of file diff --git a/EventBot/Misc/EventTypeReader.cs b/EventBot/Misc/EventTypeReader.cs index 9e15100..9f8fae0 100644 --- a/EventBot/Misc/EventTypeReader.cs +++ b/EventBot/Misc/EventTypeReader.cs @@ -18,9 +18,9 @@ namespace EventBot.Misc return Task.FromResult(TypeReaderResult.FromError(CommandError.UnmetPrecondition, "Events are avaivable only inside guild context.")); Event ev; if (input == null) - ev = events.FindEventBy(context.Guild); + ev = events.FindEventBy(context.Guild, true); else if (int.TryParse(input, out int id)) - ev = events.FindEventBy(context.Guild, id); + ev = events.FindEventBy(context.Guild, id, true); else return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Event id is not a number.")); diff --git a/EventBot/Modules/EventModule.cs b/EventBot/Modules/EventModule.cs index 95d38c4..b46e8f6 100644 --- a/EventBot/Modules/EventModule.cs +++ b/EventBot/Modules/EventModule.cs @@ -9,6 +9,7 @@ using System.Linq; using EventBot.Entities; using Discord.WebSocket; using NeoSmart.Unicode; +using Discord.Addons.Interactive; namespace EventBot.Modules { @@ -53,16 +54,14 @@ namespace EventBot.Modules [RequireUserPermission(GuildPermission.Administrator, Group = "Permission")] [RequireOwner(Group = "Permission")] [Group("event")] - public class EventManagementModule : ModuleBase + public class EventManagementModule : InteractiveBase { private readonly EventManagementService _events; private readonly DatabaseService _database; - private readonly EmoteService _emotes; - public EventManagementModule(EventManagementService events, DatabaseService database, EmoteService emotes) + public EventManagementModule(EventManagementService events, DatabaseService database) { _events = events; _database = database; - _emotes = emotes; } [Command("config logchannel")] @@ -128,6 +127,8 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("Unable to locate any events for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); @event.Title = title; await _database.SaveChangesAsync(); await ReplyAsync($"Updated event(`{@event.Id}`) title to `{@event.Title}`"); @@ -143,6 +144,8 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("Unable to locate any events for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); @event.Description = description; await _database.SaveChangesAsync(); await ReplyAsync($"Updated event(`{@event.Id}`) description to `{@event.Description}`"); @@ -161,6 +164,8 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("Unable to locate any events for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); if (@event.MessageId != 0 && @event.Type != type) throw new Exception("Can't change event registration type when it's open for registration. Maube you meant to set type to `Unspecified` (-1)"); if(@event.Type != type) @@ -183,11 +188,13 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("Unable to locate any events for this guild."); - if(@event.Roles != null && @event.Roles.Count >= 20) + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); + if (@event.Roles != null && @event.Roles.Count >= 20) throw new Exception("There are too many roles for this event."); if(@event.MessageId != 0) throw new Exception("Can't add new roles to event with open reigstration."); - if (!_emotes.TryParse(emote, out IEmote parsedEmote)) + if (!EmoteHelper.TryParse(emote, out IEmote parsedEmote)) throw new ArgumentException("Invalid emote provided."); if(@event.Roles != null && @event.Roles.Count(r => r.Emote == parsedEmote.ToString()) > 0) throw new ArgumentException("This emote is already used by other role."); @@ -212,6 +219,8 @@ namespace EventBot.Modules { if(eventRole == null) throw new Exception("Please provide correct role."); + if (!eventRole.Event.Active) + throw new Exception("This event is finalized. Please make a new event."); eventRole.Title = title; var s = _database.SaveChangesAsync(); await ReplyAsync($"Updated event role `{eventRole.Id}` title to `{eventRole.Title}`"); @@ -227,6 +236,8 @@ namespace EventBot.Modules { if (eventRole == null) throw new Exception("Please provide correct role."); + if (!eventRole.Event.Active) + throw new Exception("This event is finalized. Please make a new event."); eventRole.Description = description; var s = _database.SaveChangesAsync(); await ReplyAsync($"Updated event role `{eventRole.Id}` description to `{eventRole.Description}`"); @@ -242,6 +253,8 @@ namespace EventBot.Modules { if (eventRole == null) throw new Exception("Please provide correct role."); + if (!eventRole.Event.Active) + throw new Exception("This event is finalized. Please make a new event."); eventRole.MaxParticipants = maxParticipants; var s = _database.SaveChangesAsync(); await ReplyAsync($"Updated event role `{eventRole.Id}` maximum participant count to `{eventRole.MaxParticipants}`"); @@ -258,7 +271,11 @@ namespace EventBot.Modules { if (eventRole == null) throw new Exception("Please provide correct role."); - if (!_emotes.TryParse(emote, out IEmote parsedEmote)) + if (eventRole.Event.MessageId != 0) + throw new Exception("Role emote can't be edited while event is open for registrasion."); + if (!eventRole.Event.Active) + throw new Exception("This event is finalized. Please make a new event."); + if (!EmoteHelper.TryParse(emote, out IEmote parsedEmote)) throw new ArgumentException("Invalid emote provided."); if (eventRole.Event.Roles.Count(r => r.Emote == parsedEmote.ToString()) > 0) @@ -331,6 +348,8 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("No events were found for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); await Context.Message.DeleteAsync(); var message = await ReplyAsync(embed: _events.GenerateEventEmbed(@event).Build()); @@ -343,7 +362,7 @@ namespace EventBot.Modules case Event.EventParticipactionType.Unspecified: throw new Exception("Event type was unspecified."); case Event.EventParticipactionType.Quick: - await message.AddReactionsAsync(@event.Roles.OrderBy(e => e.SortNumber).Select(r => _emotes.Parse(r.Emote)).ToArray()); + await message.AddReactionsAsync(@event.Roles.OrderBy(e => e.SortNumber).Select(r => EmoteHelper.Parse(r.Emote)).ToArray()); break; case Event.EventParticipactionType.Detailed: break; @@ -351,6 +370,83 @@ namespace EventBot.Modules throw new Exception("Event type in not implemented."); } } + + [Command("close")] + [Summary("Closes event registration.")] + public async Task EventClose( + [Summary("Event to close")] Event @event = null) + { + if (@event == null) + @event = _events.FindEventBy(Context.Guild); + if (@event == null) + throw new Exception("No events were found for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); + if (@event.MessageId == 0) + throw new Exception("This event is already closed for registration."); + + @event.MessageId = 0; + @event.MessageChannelId = 0; + await _database.SaveChangesAsync(); + await ReplyAsync($"Event `{@event.Id}` registration has been closed, it's registration message will now be normal message."); + } + + [Command("finalize")] + [Summary("Archives event and reverts all role additions. This is irreversable.")] + public async Task EventFinilize( + [Summary("Event to finilize")] Event @event = null) + { + if (@event == null) + @event = _events.FindEventBy(Context.Guild); + if (@event == null) + throw new Exception("No events were found for this guild."); + if (!@event.Active) + throw new Exception("This event is already finalized."); + + @event.Active = false; + await _database.SaveChangesAsync(); + + await ReplyAsync($"Event `{@event.Id}` has been finilized. Removing participant roles..."); + if (@event.Guild.ParticipantRoleId != 0) + foreach (var participant in @event.Participants) + { + var user = Context.Guild.GetUser(participant.UserId); + await user.RemoveRoleAsync(Context.Guild.GetRole(@event.Guild.ParticipantRoleId)); + } + await ReplyAsync($"Everyone's roles have been removed. I hope it was fun!"); + } + + [Command("list")] + [Summary("Lists all prevous events that took on this server.")] + public async Task EventArchive() + { + var guildEvents = _database.Events.Where(e => e.GuildId == Context.Guild.Id).OrderBy(e => e.Opened).ToList(); + if (guildEvents.Count() == 0) + throw new Exception("There are no events that roon on this server."); + + var pagedEvents = guildEvents + .Select((e, i) => new { Event = e, Index = i }) + .GroupBy(o => o.Index / 6) + .Select(g => g.Select(o => o.Event)); + var pager = new PaginatedMessage() + { + Title = "List al all prevous events.", + Color = Color.Blue, + Options = new PaginatedAppearanceOptions() + { + Timeout = new TimeSpan(0, 3, 0), + DisplayInformationIcon = false, + JumpDisplayOptions = JumpDisplayOptions.Never + } + }; + pager.Pages = pagedEvents.Select(eg => + string.Join("\r\n", eg.Select(e => + $"`{e.Id}` **{e.Title}** {(e.Active ? "✅" : "❌")}\r\n" + + $"Opened at {e.Opened.ToShortDateString()} {e.Opened.ToShortTimeString()}" + )) + ); + await PagedReplyAsync(pager); + } [Command("participant add")] [Summary("Add user to event role. Acts like join command.")] @@ -367,12 +463,14 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null & !(int.TryParse(emoteOrId, out int roleId))) throw new Exception("Unable to locate any events for this guild."); - else if (@event == null) + else if (@event == null || roleId != 0) er = _database.EventRoles.FirstOrDefault(r => r.Id == roleId); else er = @event.Roles.FirstOrDefault(r => r.Emote == emoteOrId); if (er == null) throw new ArgumentException("Invalid emote or event id specified"); + if (!er.Event.Active) + throw new Exception("This event is finalized. Please make a new event."); await _events.TryJoinEvent(guildUser, er, extraInformation, false); await Context.Message.DeleteAsync(); // Protect somewhat sensitive data. @@ -388,6 +486,8 @@ namespace EventBot.Modules @event = _events.FindEventBy(Context.Guild); if (@event == null) throw new Exception("No events were found for this guild."); + if (!@event.Active) + throw new Exception("This event is finalized. Please make a new event."); if (!(user is IGuildUser guildUser)) throw new Exception("This command must be executed inside guild."); diff --git a/EventBot/Program.cs b/EventBot/Program.cs index 5530885..abae2fe 100644 --- a/EventBot/Program.cs +++ b/EventBot/Program.cs @@ -63,7 +63,6 @@ namespace EventBot return new SqliteDatabaseService(sp); }) .AddSingleton() - .AddSingleton() //.AddSingleton() //.AddSingleton() .BuildServiceProvider(); diff --git a/EventBot/Services/DatabaseService.cs b/EventBot/Services/DatabaseService.cs index 13fd72f..20866b7 100644 --- a/EventBot/Services/DatabaseService.cs +++ b/EventBot/Services/DatabaseService.cs @@ -11,7 +11,7 @@ using System.Linq; namespace EventBot.Services { - public abstract class DatabaseService: DbContext + public abstract class DatabaseService : DbContext { private readonly IServiceProvider _services; private readonly DiscordSocketClient _discord; @@ -36,6 +36,7 @@ namespace EventBot.Services _discord = services.GetRequiredService(); _discord.GuildAvailable += OnGuildAvaivable; } + public DatabaseService(): base() {} protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/EventBot/Services/EmoteService.cs b/EventBot/Services/EmoteService.cs deleted file mode 100644 index fe39c07..0000000 --- a/EventBot/Services/EmoteService.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NeoSmart.Unicode; -using System.Linq; -using Discord; -using DEmoji = Discord.Emoji; -using UEmoji = NeoSmart.Unicode.Emoji; - -namespace EventBot.Services -{ - - public class EmoteService - { - private IEnumerable emoji; - - public EmoteService() - { - emoji = UEmoji.All.Select(e => e.Sequence.AsString); - } - - public bool TryParse(string input, out IEmote emote) - { - if(Emote.TryParse(input, out Emote parsedEmote)) - { - emote = parsedEmote; - return true; - } - if(emoji.Contains(input)) - { - emote = new DEmoji(input); - return true; - } - emote = null; - return false; - } - - public IEmote Parse(string input) - { - if (!TryParse(input, out IEmote parsed)) - throw new ArgumentException("Failed to parse emote."); - return parsed; - } - - } -} \ No newline at end of file diff --git a/EventBot/Services/EventManagementService.cs b/EventBot/Services/EventManagementService.cs index 811f611..923394b 100644 --- a/EventBot/Services/EventManagementService.cs +++ b/EventBot/Services/EventManagementService.cs @@ -15,14 +15,12 @@ namespace EventBot.Services { private readonly DiscordSocketClient _discord; private readonly DatabaseService _database; - private readonly EmoteService _emotes; private readonly IServiceProvider _services; public EventManagementService(IServiceProvider services) { _discord = services.GetRequiredService(); _database = services.GetRequiredService(); - _emotes = services.GetRequiredService(); _services = services; _discord.ReactionAdded += ReactionAddedAsync; @@ -128,7 +126,7 @@ namespace EventBot.Services var @event = _database.Events.FirstOrDefault(e => e.MessageId == message.Id); if (@event != null) { - var role = @event.Roles.FirstOrDefault(r => reaction.Emote.Equals(_emotes.Parse(r.Emote))); + var role = @event.Roles.FirstOrDefault(r => reaction.Emote.Equals(EmoteHelper.Parse(r.Emote))); if(role != null) { var userMessage = await message.GetOrDownloadAsync(); diff --git a/EventBot/Services/MySqlDatabaseService.cs b/EventBot/Services/MySqlDatabaseService.cs index d0a31e9..1f87ace 100644 --- a/EventBot/Services/MySqlDatabaseService.cs +++ b/EventBot/Services/MySqlDatabaseService.cs @@ -9,6 +9,8 @@ namespace EventBot.Services { public MySqlDatabaseService(IServiceProvider services, DbContextOptions options) : base(services, options) { } public MySqlDatabaseService(IServiceProvider services) : base(services) { } + public MySqlDatabaseService() : base() { } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); diff --git a/EventBot/Services/SqliteDatabaseService.cs b/EventBot/Services/SqliteDatabaseService.cs index 021fbd4..f6c6320 100644 --- a/EventBot/Services/SqliteDatabaseService.cs +++ b/EventBot/Services/SqliteDatabaseService.cs @@ -9,6 +9,7 @@ namespace EventBot.Services { public SqliteDatabaseService(IServiceProvider services, DbContextOptions options) : base(services, options) { } public SqliteDatabaseService(IServiceProvider services) : base(services) {} + public SqliteDatabaseService() : base() { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/README b/README new file mode 100644 index 0000000..7e6de9b --- /dev/null +++ b/README @@ -0,0 +1,6 @@ + +`set dbconnection="Server=localhost;Database=eventbot;User=root;Password=password;"` + + +`Add-Migration InitialDatabase -Context MySqlDatabaseService -OutputDir Migrations\MySql` +`Add-Migration InitialDatabase -Context SqliteDatabaseService -OutputDir Migrations\Sqlite` \ No newline at end of file