Added RaidTemplates and TimerService to create weekly raids

This commit is contained in:
t.ruspekhofer 2022-03-12 21:16:45 +01:00
parent c215ed058f
commit ab6602710d
13 changed files with 270 additions and 53 deletions

View file

@ -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 },

View file

@ -21,6 +21,7 @@ namespace Lieb.Data
public DbSet<GuildWars2Build> GuildWars2Builds { get; set; }
public DbSet<PlannedRaidRole> PlannedRaidRoles { get; set; }
public DbSet<Raid> Raids { get; set; }
public DbSet<RaidTemplate> RaidTemplates { get; set; }
public DbSet<RaidReminder> RaidReminders { get; set; }
public DbSet<RaidSignUp> RaidSignUps { get; set; }
public DbSet<SignUpHistory> SignUpHistories { get; set; }
@ -36,6 +37,7 @@ namespace Lieb.Data
modelBuilder.Entity<GuildWars2Build>().ToTable("GuildWars2Build");
modelBuilder.Entity<PlannedRaidRole>().ToTable("PlannedRaidRole");
modelBuilder.Entity<Raid>().ToTable("Raid");
modelBuilder.Entity<RaidTemplate>().ToTable("RaidTemplate");
modelBuilder.Entity<RaidReminder>().ToTable("RaidReminder");
modelBuilder.Entity<RaidSignUp>().ToTable("RaidSignUp");
modelBuilder.Entity<SignUpHistory>().ToTable("SignUpHistory");

View file

@ -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<Raid> 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...
}
}
}
}
}

View file

@ -0,0 +1,49 @@
using Lieb.Models.GuildWars2.Raid;
using Microsoft.EntityFrameworkCore;
namespace Lieb.Data
{
public class RaidTemplateService
{
private readonly IDbContextFactory<LiebContext> _contextFactory;
public RaidTemplateService(IDbContextFactory<LiebContext> contextFactory)
{
_contextFactory = contextFactory;
}
public List<RaidTemplate> 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);
}
}
}

60
Lieb/Data/TimerService.cs Normal file
View file

@ -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<RaidTemplateService>();
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>();
raidService.SendReminders();
}
}
public Task StopAsync(CancellationToken stoppingToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
}

View file

@ -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<PlannedRaidRole> Roles { get; set; } = new HashSet<PlannedRaidRole>();
public ICollection<RaidReminder> Reminders { get; set; } = new List<RaidReminder>();
public bool IsRandomized { get; set; } = false;
public ICollection<RaidSignUp> SignUps { get; set; } = new HashSet<RaidSignUp>();
@ -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; }
}
}

View file

@ -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<PlannedRaidRole> Roles { get; set; } = new HashSet<PlannedRaidRole>();
public ICollection<RaidReminder> Reminders { get; set; } = new List<RaidReminder>();
//used to edit the Discord message
public ulong DiscordChannelId { get; set; }
public ulong DiscordGuildId { get; set; }
}
}

View file

@ -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; }

View file

@ -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; }
}
}

View file

@ -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)

View file

@ -1,7 +1,7 @@
@using Lieb.Models.GuildWars2.Raid
<div>
<table class="roleTable">
<table class="table">
<tbody>
@foreach (var role in _raid.Roles)
{

View file

@ -56,7 +56,7 @@
@{string signUpStatus = string.Empty;}
@if (signUp.SignUpType != SignUpType.SignedUp) signUpStatus = $" - {signUp.SignUpType}";
@if (isUser)
@if (isUser && _user.GuildWars2Accounts.Count > 1)
{
<td>@signUp.LiebUser.Name
<select value=@signUp.GuildWars2AccountId @onchange="args => ChangeAccount(_user, args)">

View file

@ -21,11 +21,13 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<RaidService>();
builder.Services.AddScoped<RaidTemplateService>();
builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<GuildWars2AccountService>();
builder.Services.AddScoped<GuildWars2BuildService>();
builder.Services.AddScoped<RaidRandomizerService>();
builder.Services.AddScoped<TimeZoneService>();
builder.Services.AddHostedService<TimerService>();
builder.Services.AddAuthentication(opt =>