diff --git a/Cocotte/Migrations/20230325234255_AddActivityDueTime.Designer.cs b/Cocotte/Migrations/20230325234255_AddActivityDueTime.Designer.cs
new file mode 100644
index 0000000..768a951
--- /dev/null
+++ b/Cocotte/Migrations/20230325234255_AddActivityDueTime.Designer.cs
@@ -0,0 +1,159 @@
+//
+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("20230325234255_AddActivityDueTime")]
+ partial class AddActivityDueTime
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "7.0.4");
+
+ 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("DueTime")
+ .HasColumnType("TEXT");
+
+ 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("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.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("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("Cocotte.Modules.Activities.Models.Activity", b =>
+ {
+ b.Navigation("ActivityPlayers");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Cocotte/Migrations/20230325234255_AddActivityDueTime.cs b/Cocotte/Migrations/20230325234255_AddActivityDueTime.cs
new file mode 100644
index 0000000..55db758
--- /dev/null
+++ b/Cocotte/Migrations/20230325234255_AddActivityDueTime.cs
@@ -0,0 +1,29 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Cocotte.Migrations
+{
+ ///
+ public partial class AddActivityDueTime : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "DueTime",
+ table: "Activities",
+ type: "TEXT",
+ nullable: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "DueTime",
+ table: "Activities");
+ }
+ }
+}
diff --git a/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs b/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
index 185380f..a886ace 100644
--- a/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
+++ b/Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
@@ -48,6 +48,9 @@ namespace Cocotte.Migrations
.IsRequired()
.HasColumnType("TEXT");
+ b.Property("DueTime")
+ .HasColumnType("TEXT");
+
b.Property("MaxPlayers")
.HasColumnType("INTEGER");
diff --git a/Cocotte/Modules/Activities/ActivityFormatter.cs b/Cocotte/Modules/Activities/ActivityFormatter.cs
index f5733b4..d7fbd9d 100644
--- a/Cocotte/Modules/Activities/ActivityFormatter.cs
+++ b/Cocotte/Modules/Activities/ActivityFormatter.cs
@@ -66,13 +66,27 @@ public class ActivityFormatter
$"{FormatActivityName(activity.Name)} ({players.Count}/{activity.MaxPlayers})"
};
+ // Build description
var descriptionBuilder = new StringBuilder();
+
+ // Add time if specified
+ if (activity.DueTime is { } time)
+ {
+ descriptionBuilder.AppendLine($"**:clock2: {TimestampTag.FormatFromDateTime(DateTime.Today.WithTimeOnly(time), TimestampTagStyles.ShortTime)}**");
+ }
+ else
+ {
+ descriptionBuilder.AppendLine($"**:clock2: Maintenant**");
+ }
+
+ // Add generic message or specified activity description
descriptionBuilder.AppendLine(
string.IsNullOrWhiteSpace(activity.Description)
? $"Rejoignez l'activité de {MentionUtils.MentionUser(activity.CreatorUserId)}"
: activity.Description
);
+ // Add thread link
descriptionBuilder.AppendLine();
descriptionBuilder.Append($"**[Fil associé]({ChannelUtils.GetChannelLink(activity.GuildId, activity.ThreadId)})**");
diff --git a/Cocotte/Modules/Activities/ActivityModule.cs b/Cocotte/Modules/Activities/ActivityModule.cs
index 3d95c0d..70abf4d 100644
--- a/Cocotte/Modules/Activities/ActivityModule.cs
+++ b/Cocotte/Modules/Activities/ActivityModule.cs
@@ -53,91 +53,108 @@ public partial class ActivityModule : InteractionModuleBase