diff --git a/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.Designer.cs b/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.Designer.cs
new file mode 100644
index 0000000..ddd50ba
--- /dev/null
+++ b/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.Designer.cs
@@ -0,0 +1,689 @@
+//
+using System;
+using Cocotte.Services;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Cocotte.Migrations
+{
+ [DbContext(typeof(CocotteDbContext))]
+ [Migration("20230410102447_AddActivityPlayerHasCompleted")]
+ partial class AddActivityPlayerHasCompleted
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "7.0.4");
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzBlobTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerName")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("BlobData")
+ .HasColumnType("bytea")
+ .HasColumnName("BLOB_DATA");
+
+ b.HasKey("SchedulerName", "TriggerName", "TriggerGroup");
+
+ b.ToTable("QRTZ_BLOB_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCalendar", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("CalendarName")
+ .HasColumnType("text")
+ .HasColumnName("CALENDAR_NAME");
+
+ b.Property("Calendar")
+ .IsRequired()
+ .HasColumnType("bytea")
+ .HasColumnName("CALENDAR");
+
+ b.HasKey("SchedulerName", "CalendarName");
+
+ b.ToTable("QRTZ_CALENDARS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCronTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerName")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("CronExpression")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("CRON_EXPRESSION");
+
+ b.Property("TimeZoneId")
+ .HasColumnType("text")
+ .HasColumnName("TIME_ZONE_ID");
+
+ b.HasKey("SchedulerName", "TriggerName", "TriggerGroup");
+
+ b.ToTable("QRTZ_CRON_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzFiredTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("EntryId")
+ .HasColumnType("text")
+ .HasColumnName("ENTRY_ID");
+
+ b.Property("FiredTime")
+ .HasColumnType("bigint")
+ .HasColumnName("FIRED_TIME");
+
+ b.Property("InstanceName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("INSTANCE_NAME");
+
+ b.Property("IsNonConcurrent")
+ .HasColumnType("bool")
+ .HasColumnName("IS_NONCONCURRENT");
+
+ b.Property("JobGroup")
+ .HasColumnType("text")
+ .HasColumnName("JOB_GROUP");
+
+ b.Property("JobName")
+ .HasColumnType("text")
+ .HasColumnName("JOB_NAME");
+
+ b.Property("Priority")
+ .HasColumnType("integer")
+ .HasColumnName("PRIORITY");
+
+ b.Property("RequestsRecovery")
+ .HasColumnType("bool")
+ .HasColumnName("REQUESTS_RECOVERY");
+
+ b.Property("ScheduledTime")
+ .HasColumnType("bigint")
+ .HasColumnName("SCHED_TIME");
+
+ b.Property("State")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("STATE");
+
+ b.Property("TriggerGroup")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("TriggerName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.HasKey("SchedulerName", "EntryId");
+
+ b.HasIndex("InstanceName")
+ .HasDatabaseName("IDX_QRTZ_FT_TRIG_INST_NAME");
+
+ b.HasIndex("JobGroup")
+ .HasDatabaseName("IDX_QRTZ_FT_JOB_GROUP");
+
+ b.HasIndex("JobName")
+ .HasDatabaseName("IDX_QRTZ_FT_JOB_NAME");
+
+ b.HasIndex("RequestsRecovery")
+ .HasDatabaseName("IDX_QRTZ_FT_JOB_REQ_RECOVERY");
+
+ b.HasIndex("TriggerGroup")
+ .HasDatabaseName("IDX_QRTZ_FT_TRIG_GROUP");
+
+ b.HasIndex("TriggerName")
+ .HasDatabaseName("IDX_QRTZ_FT_TRIG_NAME");
+
+ b.HasIndex("SchedulerName", "TriggerName", "TriggerGroup")
+ .HasDatabaseName("IDX_QRTZ_FT_TRIG_NM_GP");
+
+ b.ToTable("QRTZ_FIRED_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("JobName")
+ .HasColumnType("text")
+ .HasColumnName("JOB_NAME");
+
+ b.Property("JobGroup")
+ .HasColumnType("text")
+ .HasColumnName("JOB_GROUP");
+
+ b.Property("Description")
+ .HasColumnType("text")
+ .HasColumnName("DESCRIPTION");
+
+ b.Property("IsDurable")
+ .HasColumnType("bool")
+ .HasColumnName("IS_DURABLE");
+
+ b.Property("IsNonConcurrent")
+ .HasColumnType("bool")
+ .HasColumnName("IS_NONCONCURRENT");
+
+ b.Property("IsUpdateData")
+ .HasColumnType("bool")
+ .HasColumnName("IS_UPDATE_DATA");
+
+ b.Property("JobClassName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("JOB_CLASS_NAME");
+
+ b.Property("JobData")
+ .HasColumnType("bytea")
+ .HasColumnName("JOB_DATA");
+
+ b.Property("RequestsRecovery")
+ .HasColumnType("bool")
+ .HasColumnName("REQUESTS_RECOVERY");
+
+ b.HasKey("SchedulerName", "JobName", "JobGroup");
+
+ b.HasIndex("RequestsRecovery")
+ .HasDatabaseName("IDX_QRTZ_J_REQ_RECOVERY");
+
+ b.ToTable("QRTZ_JOB_DETAILS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzLock", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("LockName")
+ .HasColumnType("text")
+ .HasColumnName("LOCK_NAME");
+
+ b.HasKey("SchedulerName", "LockName");
+
+ b.ToTable("QRTZ_LOCKS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzPausedTriggerGroup", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.HasKey("SchedulerName", "TriggerGroup");
+
+ b.ToTable("QRTZ_PAUSED_TRIGGER_GRPS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSchedulerState", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("InstanceName")
+ .HasColumnType("text")
+ .HasColumnName("INSTANCE_NAME");
+
+ b.Property("CheckInInterval")
+ .HasColumnType("bigint")
+ .HasColumnName("CHECKIN_INTERVAL");
+
+ b.Property("LastCheckInTime")
+ .HasColumnType("bigint")
+ .HasColumnName("LAST_CHECKIN_TIME");
+
+ b.HasKey("SchedulerName", "InstanceName");
+
+ b.ToTable("QRTZ_SCHEDULER_STATE", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimplePropertyTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerName")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("BooleanProperty1")
+ .HasColumnType("bool")
+ .HasColumnName("BOOL_PROP_1");
+
+ b.Property("BooleanProperty2")
+ .HasColumnType("bool")
+ .HasColumnName("BOOL_PROP_2");
+
+ b.Property("DecimalProperty1")
+ .HasColumnType("numeric")
+ .HasColumnName("DEC_PROP_1");
+
+ b.Property("DecimalProperty2")
+ .HasColumnType("numeric")
+ .HasColumnName("DEC_PROP_2");
+
+ b.Property("IntegerProperty1")
+ .HasColumnType("integer")
+ .HasColumnName("INT_PROP_1");
+
+ b.Property("IntegerProperty2")
+ .HasColumnType("integer")
+ .HasColumnName("INT_PROP_2");
+
+ b.Property("LongProperty1")
+ .HasColumnType("bigint")
+ .HasColumnName("LONG_PROP_1");
+
+ b.Property("LongProperty2")
+ .HasColumnType("bigint")
+ .HasColumnName("LONG_PROP_2");
+
+ b.Property("StringProperty1")
+ .HasColumnType("text")
+ .HasColumnName("STR_PROP_1");
+
+ b.Property("StringProperty2")
+ .HasColumnType("text")
+ .HasColumnName("STR_PROP_2");
+
+ b.Property("StringProperty3")
+ .HasColumnType("text")
+ .HasColumnName("STR_PROP_3");
+
+ b.Property("TimeZoneId")
+ .HasColumnType("text")
+ .HasColumnName("TIME_ZONE_ID");
+
+ b.HasKey("SchedulerName", "TriggerName", "TriggerGroup");
+
+ b.ToTable("QRTZ_SIMPROP_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimpleTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerName")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("RepeatCount")
+ .HasColumnType("bigint")
+ .HasColumnName("REPEAT_COUNT");
+
+ b.Property("RepeatInterval")
+ .HasColumnType("bigint")
+ .HasColumnName("REPEAT_INTERVAL");
+
+ b.Property("TimesTriggered")
+ .HasColumnType("bigint")
+ .HasColumnName("TIMES_TRIGGERED");
+
+ b.HasKey("SchedulerName", "TriggerName", "TriggerGroup");
+
+ b.ToTable("QRTZ_SIMPLE_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b =>
+ {
+ b.Property("SchedulerName")
+ .HasColumnType("text")
+ .HasColumnName("SCHED_NAME");
+
+ b.Property("TriggerName")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_NAME");
+
+ b.Property("TriggerGroup")
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_GROUP");
+
+ b.Property("CalendarName")
+ .HasColumnType("text")
+ .HasColumnName("CALENDAR_NAME");
+
+ b.Property("Description")
+ .HasColumnType("text")
+ .HasColumnName("DESCRIPTION");
+
+ b.Property("EndTime")
+ .HasColumnType("bigint")
+ .HasColumnName("END_TIME");
+
+ b.Property("JobData")
+ .HasColumnType("bytea")
+ .HasColumnName("JOB_DATA");
+
+ b.Property("JobGroup")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("JOB_GROUP");
+
+ b.Property("JobName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("JOB_NAME");
+
+ b.Property("MisfireInstruction")
+ .HasColumnType("smallint")
+ .HasColumnName("MISFIRE_INSTR");
+
+ b.Property("NextFireTime")
+ .HasColumnType("bigint")
+ .HasColumnName("NEXT_FIRE_TIME");
+
+ b.Property("PreviousFireTime")
+ .HasColumnType("bigint")
+ .HasColumnName("PREV_FIRE_TIME");
+
+ b.Property("Priority")
+ .HasColumnType("integer")
+ .HasColumnName("PRIORITY");
+
+ b.Property("StartTime")
+ .HasColumnType("bigint")
+ .HasColumnName("START_TIME");
+
+ b.Property("TriggerState")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_STATE");
+
+ b.Property("TriggerType")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("TRIGGER_TYPE");
+
+ b.HasKey("SchedulerName", "TriggerName", "TriggerGroup");
+
+ b.HasIndex("NextFireTime")
+ .HasDatabaseName("IDX_QRTZ_T_NEXT_FIRE_TIME");
+
+ b.HasIndex("TriggerState")
+ .HasDatabaseName("IDX_QRTZ_T_STATE");
+
+ b.HasIndex("NextFireTime", "TriggerState")
+ .HasDatabaseName("IDX_QRTZ_T_NFT_ST");
+
+ b.HasIndex("SchedulerName", "JobName", "JobGroup");
+
+ b.ToTable("QRTZ_TRIGGERS", (string)null);
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.Activity", b =>
+ {
+ b.Property("GuildId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ChannelId")
+ .HasColumnType("INTEGER");
+
+ b.Property("MessageId")
+ .HasColumnType("INTEGER");
+
+ b.Property("AreRolesEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("CreationDate")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatorDisplayName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatorUserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("DueDateTime")
+ .HasColumnType("TEXT");
+
+ b.Property("IsClosed")
+ .HasColumnType("INTEGER");
+
+ b.Property("MaxPlayers")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .HasColumnType("INTEGER");
+
+ b.Property("ThreadId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("GuildId", "ChannelId", "MessageId");
+
+ b.HasIndex("ThreadId");
+
+ b.ToTable("Activities");
+
+ b.HasDiscriminator("Discriminator").HasValue("Activity");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityPlayer", b =>
+ {
+ b.Property("GuildId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ChannelId")
+ .HasColumnType("INTEGER");
+
+ b.Property("MessageId")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("HasCompleted")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsOrganizer")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("GuildId", "ChannelId", "MessageId", "UserId");
+
+ b.ToTable("ActivityPlayers");
+
+ b.HasDiscriminator("Discriminator").HasValue("ActivityPlayer");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.InterstellarActivity", b =>
+ {
+ b.HasBaseType("Cocotte.Modules.Activities.Models.Activity");
+
+ b.Property("Color")
+ .HasColumnType("INTEGER");
+
+ b.HasDiscriminator().HasValue("InterstellarActivity");
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.OrganizedActivity", b =>
+ {
+ b.HasBaseType("Cocotte.Modules.Activities.Models.Activity");
+
+ b.HasDiscriminator().HasValue("OrganizedActivity");
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.StagedActivity", b =>
+ {
+ b.HasBaseType("Cocotte.Modules.Activities.Models.Activity");
+
+ b.Property("Stage")
+ .HasColumnType("INTEGER");
+
+ b.HasDiscriminator().HasValue("StagedActivity");
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityRolePlayer", b =>
+ {
+ b.HasBaseType("Cocotte.Modules.Activities.Models.ActivityPlayer");
+
+ b.Property("Roles")
+ .HasColumnType("INTEGER");
+
+ b.HasDiscriminator().HasValue("ActivityRolePlayer");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzBlobTrigger", b =>
+ {
+ b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger")
+ .WithMany("BlobTriggers")
+ .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Trigger");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCronTrigger", b =>
+ {
+ b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger")
+ .WithMany("CronTriggers")
+ .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Trigger");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimplePropertyTrigger", b =>
+ {
+ b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger")
+ .WithMany("SimplePropertyTriggers")
+ .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Trigger");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimpleTrigger", b =>
+ {
+ b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger")
+ .WithMany("SimpleTriggers")
+ .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Trigger");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b =>
+ {
+ b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", "JobDetail")
+ .WithMany("Triggers")
+ .HasForeignKey("SchedulerName", "JobName", "JobGroup")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("JobDetail");
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityPlayer", b =>
+ {
+ b.HasOne("Cocotte.Modules.Activities.Models.Activity", "Activity")
+ .WithMany("ActivityPlayers")
+ .HasForeignKey("GuildId", "ChannelId", "MessageId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Activity");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", b =>
+ {
+ b.Navigation("Triggers");
+ });
+
+ modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b =>
+ {
+ b.Navigation("BlobTriggers");
+
+ b.Navigation("CronTriggers");
+
+ b.Navigation("SimplePropertyTriggers");
+
+ b.Navigation("SimpleTriggers");
+ });
+
+ modelBuilder.Entity("Cocotte.Modules.Activities.Models.Activity", b =>
+ {
+ b.Navigation("ActivityPlayers");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.cs b/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.cs
new file mode 100644
index 0000000..b071548
--- /dev/null
+++ b/Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Cocotte.Migrations
+{
+ ///
+ public partial class AddActivityPlayerHasCompleted : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "HasCompleted",
+ table: "ActivityPlayers",
+ type: "INTEGER",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "HasCompleted",
+ table: "ActivityPlayers");
+ }
+ }
+}
diff --git a/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs b/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
index 5c04b66..a6899e3 100644
--- a/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
+++ b/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
@@ -538,6 +538,9 @@ namespace Cocotte.Migrations
.IsRequired()
.HasColumnType("TEXT");
+ b.Property("HasCompleted")
+ .HasColumnType("INTEGER");
+
b.Property("IsOrganizer")
.HasColumnType("INTEGER");
diff --git a/Cocotte/Modules/Activities/ActivityFormatter.cs b/Cocotte/Modules/Activities/ActivityFormatter.cs
index 172427a..802ae48 100644
--- a/Cocotte/Modules/Activities/ActivityFormatter.cs
+++ b/Cocotte/Modules/Activities/ActivityFormatter.cs
@@ -46,7 +46,7 @@ public class ActivityFormatter
public EmbedBuilder ActivityEmbed(Activity activity, IReadOnlyCollection players)
{
// Load activity players and organizers
- var participants = activity.Participants.ToArray();
+ var participants = activity.Participants.OrderBy(p => p.HasCompleted).ToArray();
var organizers = activity.Organizers.ToArray();
// Activity full
@@ -164,7 +164,12 @@ public class ActivityFormatter
_ => "NA"
};
- public string FormatActivityPlayer(ActivityPlayer player, int namePadding, bool isEvent = false) => player switch
+ public string FormatActivityPlayer(ActivityPlayer player, int namePadding, bool isEvent = false) =>
+ player.HasCompleted
+ ? $"~~{FormatActivityPlayerSub(player, namePadding, isEvent)}~~"
+ : FormatActivityPlayerSub(player, namePadding, isEvent);
+
+ private string FormatActivityPlayerSub(ActivityPlayer player, int namePadding, bool isEvent = false) => player switch
{
ActivityRolePlayer rolePlayer => $"` {player.Name.PadRight(namePadding)} ` **|** {RolesToEmotes(rolePlayer.Roles)}",
_ when isEvent && player.IsOrganizer => $"` {player.Name.PadRight(namePadding)} ` **|** {_options.OrganizerEmote} ",
diff --git a/Cocotte/Modules/Activities/ActivityModuleThread.cs b/Cocotte/Modules/Activities/ActivityModuleThread.cs
index 7c2d7ab..e9e42dd 100644
--- a/Cocotte/Modules/Activities/ActivityModuleThread.cs
+++ b/Cocotte/Modules/Activities/ActivityModuleThread.cs
@@ -27,6 +27,7 @@ public partial class ActivityModule
- `/activite description` - **Modifie la description** de l'activité
- `/activite etage` - Pour l'abîme du néant et l'origine de la guerre, **modifie l'étage** de l'activité
+ - `/activite completer` - Marquer un joueur comme ayant complété l'activité, le barrant dans la liste des inscrits
""";
private async Task CreateThread(ActivityName activityName, string creatorName)
@@ -239,6 +240,67 @@ public partial class ActivityModule
);
}
+ [SlashCommand("completer", "Marquer un jour comme ayant complété une activité, le barrant dans la liste des inscrits")]
+ public async Task ThreadPlayerComplete([Summary("joueur", "Le joueur qui a complété l'activité")] IUser user)
+ {
+ // Get activity linked to this thread
+ var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
+
+ if (!await CheckCommandInThread(activity, checkCreator: false) || activity is null)
+ {
+ return;
+ }
+
+ // Check if activity is organized activity
+ if (activity is not OrganizedActivity)
+ {
+ await RespondAsync(
+ ephemeral: true,
+ embed: EmbedUtils.ErrorEmbed("Cette commande n'est pas supporté dans ce type d'activité").Build()
+ );
+
+ return;
+ }
+
+ // Check if player is in activity
+ var players = await _activitiesRepository.LoadActivityPlayers(activity);
+ var player = players.FirstOrDefault(p => p.UserId == user.Id);
+
+ if (player is null)
+ {
+ await RespondAsync(
+ ephemeral: true,
+ embed: EmbedUtils.ErrorEmbed("Ce joueur n'est pas dans cette activité").Build()
+ );
+
+ return;
+ }
+
+
+ // Check if user who used the command is an organizer
+ var organizer = players.FirstOrDefault(p => p.UserId == User.Id);
+
+ if (organizer is not { IsOrganizer: true })
+ {
+ await RespondAsync(
+ ephemeral: true,
+ embed: EmbedUtils.ErrorEmbed("Seul un organisateur de l'activité peut effectuer cette action").Build()
+ );
+
+ return;
+ }
+
+ player.HasCompleted = !player.HasCompleted;
+ await _activitiesRepository.SaveChanges();
+
+ await UpdateActivityEmbed(activity, ActivityUpdateReason.Update);
+
+ await RespondAsync(
+ ephemeral: true,
+ embed: EmbedUtils.InfoEmbed($"L'inscription du joueur {((IGuildUser)user).DisplayName} a bien été mis à jour").Build()
+ );
+ }
+
[ModalInteraction("activity description_modal", ignoreGroupNames: true)]
public async Task ActivityDescriptionSubmit(ActivityDescriptionModal descriptionModal)
{
diff --git a/Cocotte/Modules/Activities/Models/ActivityPlayer.cs b/Cocotte/Modules/Activities/Models/ActivityPlayer.cs
index 2a1baae..b382590 100644
--- a/Cocotte/Modules/Activities/Models/ActivityPlayer.cs
+++ b/Cocotte/Modules/Activities/Models/ActivityPlayer.cs
@@ -11,6 +11,7 @@ public class ActivityPlayer
public required string Name { get; init; }
public bool IsOrganizer { get; init; }
+ public bool HasCompleted { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }