From ab6602710d289d661fa0044dd4c5f133bf55be73 Mon Sep 17 00:00:00 2001 From: "t.ruspekhofer" Date: Sat, 12 Mar 2022 21:16:45 +0100 Subject: [PATCH] Added RaidTemplates and TimerService to create weekly raids --- Lieb/Data/DbInitializer.cs | 25 +++++++ Lieb/Data/LiebContext.cs | 2 + Lieb/Data/RaidService.cs | 19 ++++- Lieb/Data/RaidTemplateService.cs | 49 +++++++++++++ Lieb/Data/TimerService.cs | 60 ++++++++++++++++ Lieb/Models/GuildWars2/Raid/Raid.cs | 80 +++++++++++---------- Lieb/Models/GuildWars2/Raid/RaidBase.cs | 42 +++++++++++ Lieb/Models/GuildWars2/Raid/RaidReminder.cs | 15 ++-- Lieb/Models/GuildWars2/Raid/RaidTemplate.cs | 23 ++++++ Lieb/Pages/Raids/RaidEdit.razor | 2 +- Lieb/Pages/Raids/RaidRolesNoSignUp.razor | 2 +- Lieb/Pages/Raids/RaidRolesPlanned.razor | 2 +- Lieb/Program.cs | 2 + 13 files changed, 270 insertions(+), 53 deletions(-) create mode 100644 Lieb/Data/RaidTemplateService.cs create mode 100644 Lieb/Data/TimerService.cs create mode 100644 Lieb/Models/GuildWars2/Raid/RaidBase.cs create mode 100644 Lieb/Models/GuildWars2/Raid/RaidTemplate.cs diff --git a/Lieb/Data/DbInitializer.cs b/Lieb/Data/DbInitializer.cs index e0aac31..b484a1a 100644 --- a/Lieb/Data/DbInitializer.cs +++ b/Lieb/Data/DbInitializer.cs @@ -106,6 +106,31 @@ namespace Lieb.Data context.Raids.Add(raid); context.SaveChanges(); + DateTime templateStartTime = DateTime.UtcNow.AddDays(-7).AddMinutes(-117); + RaidTemplate template = new RaidTemplate() + { + Title = "Testraid", + Description = "This is a test raid\nwith multiple lines?", + Guild = "LIEB", + Organizer = "Sarah", + RaidType = RaidType.RandomWithBoons, + StartTime = templateStartTime, + EndTime = templateStartTime.AddHours(2), + FreeForAllTime = templateStartTime.AddHours(-2), + VoiceChat = "ts.lieb.games", + Frequency = 7, + CreateDaysBefore = 7, + TimeZone = "Europe/Vienna", + Roles = new[] { new PlannedRaidRole(){ + Description = "WupWup", + Name = "Ups", + Spots = 10 + } + } + }; + context.RaidTemplates.Add(template); + context.SaveChanges(); + var signUps = new RaidSignUp[] { new RaidSignUp{GuildWars2AccountId = linaith.GuildWars2AccountId, LiebUserId = users[0].LiebUserId, PlannedRaidRoleId = ele.PlannedRaidRoleId, RaidId = raid.RaidId, SignUpType = SignUpType.SignedUp }, diff --git a/Lieb/Data/LiebContext.cs b/Lieb/Data/LiebContext.cs index 9585894..8ed35e9 100644 --- a/Lieb/Data/LiebContext.cs +++ b/Lieb/Data/LiebContext.cs @@ -21,6 +21,7 @@ namespace Lieb.Data public DbSet GuildWars2Builds { get; set; } public DbSet PlannedRaidRoles { get; set; } public DbSet Raids { get; set; } + public DbSet RaidTemplates { get; set; } public DbSet RaidReminders { get; set; } public DbSet RaidSignUps { get; set; } public DbSet SignUpHistories { get; set; } @@ -36,6 +37,7 @@ namespace Lieb.Data modelBuilder.Entity().ToTable("GuildWars2Build"); modelBuilder.Entity().ToTable("PlannedRaidRole"); modelBuilder.Entity().ToTable("Raid"); + modelBuilder.Entity().ToTable("RaidTemplate"); modelBuilder.Entity().ToTable("RaidReminder"); modelBuilder.Entity().ToTable("RaidSignUp"); modelBuilder.Entity().ToTable("SignUpHistory"); diff --git a/Lieb/Data/RaidService.cs b/Lieb/Data/RaidService.cs index a16378d..3010715 100644 --- a/Lieb/Data/RaidService.cs +++ b/Lieb/Data/RaidService.cs @@ -67,12 +67,10 @@ namespace Lieb.Data raidToChange.Description = raid.Description; raidToChange.StartTimeUTC = raid.StartTimeUTC; raidToChange.EndTimeUTC = raid.EndTimeUTC; - raidToChange.TimeZone = raid.TimeZone; raidToChange.Organizer = raid.Organizer; raidToChange.Guild = raid.Guild; raidToChange.VoiceChat = raid.VoiceChat; raidToChange.RaidType = raid.RaidType; - raidToChange.Frequency = raid.Frequency; raidToChange.RequiredRole = raid.RequiredRole; raidToChange.FreeForAllTimeUTC = raid.FreeForAllTimeUTC; raidToChange.DiscordMessageId = raid.DiscordMessageId; @@ -337,5 +335,22 @@ namespace Lieb.Data return true; } + + public void SendReminders() + { + 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)) + { + //TODO send reminders -> this is a Discord Problem... + } + } + } } } diff --git a/Lieb/Data/RaidTemplateService.cs b/Lieb/Data/RaidTemplateService.cs new file mode 100644 index 0000000..b140c93 --- /dev/null +++ b/Lieb/Data/RaidTemplateService.cs @@ -0,0 +1,49 @@ +using Lieb.Models.GuildWars2.Raid; +using Microsoft.EntityFrameworkCore; + +namespace Lieb.Data +{ + public class RaidTemplateService + { + + private readonly IDbContextFactory _contextFactory; + + public RaidTemplateService(IDbContextFactory contextFactory) + { + _contextFactory = contextFactory; + } + + public List GetTemplates() + { + using var context = _contextFactory.CreateDbContext(); + return context.RaidTemplates + .Include(r => r.Roles) + .Include(r => r.Reminders) + .ToList(); + } + + public async Task CreateNewRaid(int raidTempalteId) + { + using var context = _contextFactory.CreateDbContext(); + RaidTemplate? template = await context.RaidTemplates + .Include(r => r.Roles) + .Include(r => r.Reminders) + .FirstOrDefaultAsync(t => t.RaidTemplateId == raidTempalteId); + if(template == null) + { + return; + } + Raid raid = new Raid(template); + context.Raids.Add(raid); + MoveTemplate(template); + await context.SaveChangesAsync(); + } + + private void MoveTemplate(RaidTemplate template) + { + template.StartTime = template.StartTime.AddDays(template.Frequency); + template.EndTime = template.EndTime.AddDays(template.Frequency); + template.FreeForAllTime = template.FreeForAllTime.AddDays(template.Frequency); + } + } +} diff --git a/Lieb/Data/TimerService.cs b/Lieb/Data/TimerService.cs new file mode 100644 index 0000000..77d8eba --- /dev/null +++ b/Lieb/Data/TimerService.cs @@ -0,0 +1,60 @@ +using Lieb.Models.GuildWars2.Raid; + +namespace Lieb.Data +{ + public class TimerService : IHostedService, IDisposable + { + private Timer _timer = null!; + private IServiceProvider _services; + + public TimerService(IServiceProvider services) + { + _services = services; + } + + public Task StartAsync(CancellationToken stoppingToken) + { + _timer = new Timer(CheckRaids, null, TimeSpan.Zero, + TimeSpan.FromMinutes(1)); + + return Task.CompletedTask; + } + + private void CheckRaids(object? state) + { + using (var scope = _services.CreateScope()) + { + var raidTemplateService = + scope.ServiceProvider + .GetRequiredService(); + + foreach(RaidTemplate template in raidTemplateService.GetTemplates()) + { + TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(template.TimeZone); + DateTime UTCStartTime = TimeZoneInfo.ConvertTimeToUtc(template.StartTime, timeZone); + if(UTCStartTime.AddDays(-template.CreateDaysBefore).AddHours(1) < DateTime.UtcNow) + { + raidTemplateService.CreateNewRaid(template.RaidTemplateId).Wait(); + } + } + + var raidService = + scope.ServiceProvider + .GetRequiredService(); + raidService.SendReminders(); + } + } + + public Task StopAsync(CancellationToken stoppingToken) + { + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } + } +} diff --git a/Lieb/Models/GuildWars2/Raid/Raid.cs b/Lieb/Models/GuildWars2/Raid/Raid.cs index 243eb96..36046bb 100644 --- a/Lieb/Models/GuildWars2/Raid/Raid.cs +++ b/Lieb/Models/GuildWars2/Raid/Raid.cs @@ -10,53 +10,19 @@ namespace Lieb.Models.GuildWars2.Raid RandomEliteSpecialization = 3, } - public class Raid + public class Raid : RaidBase { public int RaidId { get; private set; } - [Required] - [StringLength(100, ErrorMessage = "Title too long (100 character limit).")] - public string Title { get; set; } = String.Empty; - - [Required] - [StringLength(1000, ErrorMessage = "Description too long (1000 character limit).")] - public string Description { get; set; } = String.Empty; - [Required] public DateTimeOffset StartTimeUTC { get; set; } = DateTime.Now; [Required] public DateTimeOffset EndTimeUTC { get; set; } - public string TimeZone { get; set; } = String.Empty; - - [Required] - [StringLength(50, ErrorMessage = "Organizer too long (50 character limit).")] - public string Organizer { get; set; } = String.Empty; - - [Required] - [StringLength(50, ErrorMessage = "Guild too long (50 character limit).")] - public string Guild { get; set; } = String.Empty; - - [Required] - [StringLength(50, ErrorMessage = "VoiceChat too long (50 character limit).")] - public string VoiceChat { get; set; } = String.Empty; - - [Required] - public RaidType RaidType { get; set; } - - public bool IsRandomized { get; set; } = false; - - public int Frequency { get; set; } - - public string RequiredRole { get; set; } = String.Empty; - public DateTimeOffset FreeForAllTimeUTC { get; set; } - //role name, number of spots - public ICollection Roles { get; set; } = new HashSet(); - - public ICollection Reminders { get; set; } = new List(); + public bool IsRandomized { get; set; } = false; public ICollection SignUps { get; set; } = new HashSet(); @@ -65,8 +31,46 @@ namespace Lieb.Models.GuildWars2.Raid //used to edit the Discord message public ulong DiscordMessageId { get; set; } - public ulong DiscordChannelId { get; set; } + public Raid() { } + + public Raid(RaidTemplate template) + { + this.Title = template.Title; + this.Description = template.Description; + this.Organizer = template.Organizer; + this.Guild = template.Guild; + this.VoiceChat = template.VoiceChat; + this.RaidType = template.RaidType; + this.RequiredRole = template.RequiredRole; + this.DiscordChannelId = template.DiscordChannelId; + this.DiscordGuildId = template.DiscordGuildId; + + foreach(PlannedRaidRole role in template.Roles) + { + this.Roles.Add(new PlannedRaidRole() + { + Description = role.Description, + Name = role.Name, + Spots = role.Spots + }); + } + foreach(RaidReminder reminder in template.Reminders) + { + this.Reminders.Add(new RaidReminder() + { + ChannelId = reminder.ChannelId, + HoursBeforeRaid = reminder.HoursBeforeRaid, + Message = reminder.Message, + Sent = reminder.Sent, + Type = reminder.Type + }); + } + + TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(template.TimeZone); + StartTimeUTC = TimeZoneInfo.ConvertTimeToUtc(template.StartTime, timeZone); + EndTimeUTC = TimeZoneInfo.ConvertTimeToUtc(template.EndTime, timeZone); + FreeForAllTimeUTC = TimeZoneInfo.ConvertTimeToUtc(template.FreeForAllTime, timeZone); + } - public ulong DiscordGuildId { get; set; } } } diff --git a/Lieb/Models/GuildWars2/Raid/RaidBase.cs b/Lieb/Models/GuildWars2/Raid/RaidBase.cs new file mode 100644 index 0000000..b4f447c --- /dev/null +++ b/Lieb/Models/GuildWars2/Raid/RaidBase.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lieb.Models.GuildWars2.Raid +{ + public class RaidBase + { + [Required] + [StringLength(100, ErrorMessage = "Title too long (100 character limit).")] + public string Title { get; set; } = String.Empty; + + [Required] + [StringLength(1000, ErrorMessage = "Description too long (1000 character limit).")] + public string Description { get; set; } = String.Empty; + + [Required] + [StringLength(50, ErrorMessage = "Organizer too long (50 character limit).")] + public string Organizer { get; set; } = String.Empty; + + [Required] + [StringLength(50, ErrorMessage = "Guild too long (50 character limit).")] + public string Guild { get; set; } = String.Empty; + + [Required] + [StringLength(50, ErrorMessage = "VoiceChat too long (50 character limit).")] + public string VoiceChat { get; set; } = String.Empty; + + [Required] + public RaidType RaidType { get; set; } + + public string RequiredRole { get; set; } = String.Empty; + + //role name, number of spots + public ICollection Roles { get; set; } = new HashSet(); + + public ICollection Reminders { get; set; } = new List(); + + //used to edit the Discord message + public ulong DiscordChannelId { get; set; } + + public ulong DiscordGuildId { get; set; } + } +} diff --git a/Lieb/Models/GuildWars2/Raid/RaidReminder.cs b/Lieb/Models/GuildWars2/Raid/RaidReminder.cs index f3fd2ff..b52be77 100644 --- a/Lieb/Models/GuildWars2/Raid/RaidReminder.cs +++ b/Lieb/Models/GuildWars2/Raid/RaidReminder.cs @@ -6,26 +6,21 @@ namespace Lieb.Models.GuildWars2.Raid { public enum ReminderType { - User = 0, - Channel = 1 - } - - public RaidReminder(ReminderType type, string message, double hoursBeforeRaid, ulong channelId = 0) - { - Type = type; - Message = message; - HoursBeforeRaid = hoursBeforeRaid; - ChannelId = channelId; + User = 1, + Channel = 2 } public int RaidReminderId { get; set; } + [Required] + [Range(1, 2, ErrorMessage = "Please select a reminder type")] public ReminderType Type { get; set; } [Required] [StringLength(1000, ErrorMessage = "Message too long (1000 character limit).")] public string Message { get; set; } + [Required] public double HoursBeforeRaid { get; set; } public ulong ChannelId { get; set; } diff --git a/Lieb/Models/GuildWars2/Raid/RaidTemplate.cs b/Lieb/Models/GuildWars2/Raid/RaidTemplate.cs new file mode 100644 index 0000000..9e8cff6 --- /dev/null +++ b/Lieb/Models/GuildWars2/Raid/RaidTemplate.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lieb.Models.GuildWars2.Raid +{ + public class RaidTemplate : RaidBase + { + public int RaidTemplateId { get; private set; } + + [Required] + public DateTime StartTime { get; set; } = DateTime.Now; + + [Required] + public DateTime EndTime { get; set; } + + public DateTime FreeForAllTime { get; set; } + + public string TimeZone { get; set; } = String.Empty; + + public int Frequency { get; set; } + + public int CreateDaysBefore { get; set; } + } +} diff --git a/Lieb/Pages/Raids/RaidEdit.razor b/Lieb/Pages/Raids/RaidEdit.razor index db9657b..ed4de2a 100644 --- a/Lieb/Pages/Raids/RaidEdit.razor +++ b/Lieb/Pages/Raids/RaidEdit.razor @@ -193,7 +193,7 @@ return; } - _raid.TimeZone = await TimeZoneService.GetUserTimeZone(); + //_raid.TimeZone = await TimeZoneService.GetUserTimeZone(); _raid.StartTimeUTC = await TimeZoneService.GetUTCDateTime(_raidDate.Date + _startTime.TimeOfDay); if(_startTime.TimeOfDay > _endTime.TimeOfDay) diff --git a/Lieb/Pages/Raids/RaidRolesNoSignUp.razor b/Lieb/Pages/Raids/RaidRolesNoSignUp.razor index e705299..4cf697e 100644 --- a/Lieb/Pages/Raids/RaidRolesNoSignUp.razor +++ b/Lieb/Pages/Raids/RaidRolesNoSignUp.razor @@ -1,7 +1,7 @@ @using Lieb.Models.GuildWars2.Raid
- +
@foreach (var role in _raid.Roles) { diff --git a/Lieb/Pages/Raids/RaidRolesPlanned.razor b/Lieb/Pages/Raids/RaidRolesPlanned.razor index 067a90b..f5ecf29 100644 --- a/Lieb/Pages/Raids/RaidRolesPlanned.razor +++ b/Lieb/Pages/Raids/RaidRolesPlanned.razor @@ -56,7 +56,7 @@ @{string signUpStatus = string.Empty;} @if (signUp.SignUpType != SignUpType.SignedUp) signUpStatus = $" - {signUp.SignUpType}"; - @if (isUser) + @if (isUser && _user.GuildWars2Accounts.Count > 1) {
@signUp.LiebUser.Name