Compare commits

...

13 Commits

24 changed files with 1959 additions and 92 deletions

View File

@@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite" Version="0.4.0" />
<PackageReference Include="Discord.Net" Version="3.9.0" />
<PackageReference Include="Discord.Net" Version="3.10.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -0,0 +1,686 @@
// <auto-generated />
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("20230326160040_AddOrganizedActivities")]
partial class AddOrganizedActivities
{
/// <inheritdoc />
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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<byte[]>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("CalendarName")
.HasColumnType("text")
.HasColumnName("CALENDAR_NAME");
b.Property<byte[]>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("CronExpression")
.IsRequired()
.HasColumnType("text")
.HasColumnName("CRON_EXPRESSION");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("EntryId")
.HasColumnType("text")
.HasColumnName("ENTRY_ID");
b.Property<long>("FiredTime")
.HasColumnType("bigint")
.HasColumnName("FIRED_TIME");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("INSTANCE_NAME");
b.Property<bool>("IsNonConcurrent")
.HasColumnType("bool")
.HasColumnName("IS_NONCONCURRENT");
b.Property<string>("JobGroup")
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("JobName")
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<int>("Priority")
.HasColumnType("integer")
.HasColumnName("PRIORITY");
b.Property<bool?>("RequestsRecovery")
.HasColumnType("bool")
.HasColumnName("REQUESTS_RECOVERY");
b.Property<long>("ScheduledTime")
.HasColumnType("bigint")
.HasColumnName("SCHED_TIME");
b.Property<string>("State")
.IsRequired()
.HasColumnType("text")
.HasColumnName("STATE");
b.Property<string>("TriggerGroup")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("JobName")
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<string>("JobGroup")
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("DESCRIPTION");
b.Property<bool>("IsDurable")
.HasColumnType("bool")
.HasColumnName("IS_DURABLE");
b.Property<bool>("IsNonConcurrent")
.HasColumnType("bool")
.HasColumnName("IS_NONCONCURRENT");
b.Property<bool>("IsUpdateData")
.HasColumnType("bool")
.HasColumnName("IS_UPDATE_DATA");
b.Property<string>("JobClassName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_CLASS_NAME");
b.Property<byte[]>("JobData")
.HasColumnType("bytea")
.HasColumnName("JOB_DATA");
b.Property<bool>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("InstanceName")
.HasColumnType("text")
.HasColumnName("INSTANCE_NAME");
b.Property<long>("CheckInInterval")
.HasColumnType("bigint")
.HasColumnName("CHECKIN_INTERVAL");
b.Property<long>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<bool?>("BooleanProperty1")
.HasColumnType("bool")
.HasColumnName("BOOL_PROP_1");
b.Property<bool?>("BooleanProperty2")
.HasColumnType("bool")
.HasColumnName("BOOL_PROP_2");
b.Property<decimal?>("DecimalProperty1")
.HasColumnType("numeric")
.HasColumnName("DEC_PROP_1");
b.Property<decimal?>("DecimalProperty2")
.HasColumnType("numeric")
.HasColumnName("DEC_PROP_2");
b.Property<int?>("IntegerProperty1")
.HasColumnType("integer")
.HasColumnName("INT_PROP_1");
b.Property<int?>("IntegerProperty2")
.HasColumnType("integer")
.HasColumnName("INT_PROP_2");
b.Property<long?>("LongProperty1")
.HasColumnType("bigint")
.HasColumnName("LONG_PROP_1");
b.Property<long?>("LongProperty2")
.HasColumnType("bigint")
.HasColumnName("LONG_PROP_2");
b.Property<string>("StringProperty1")
.HasColumnType("text")
.HasColumnName("STR_PROP_1");
b.Property<string>("StringProperty2")
.HasColumnType("text")
.HasColumnName("STR_PROP_2");
b.Property<string>("StringProperty3")
.HasColumnType("text")
.HasColumnName("STR_PROP_3");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<long>("RepeatCount")
.HasColumnType("bigint")
.HasColumnName("REPEAT_COUNT");
b.Property<long>("RepeatInterval")
.HasColumnType("bigint")
.HasColumnName("REPEAT_INTERVAL");
b.Property<long>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("CalendarName")
.HasColumnType("text")
.HasColumnName("CALENDAR_NAME");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("DESCRIPTION");
b.Property<long?>("EndTime")
.HasColumnType("bigint")
.HasColumnName("END_TIME");
b.Property<byte[]>("JobData")
.HasColumnType("bytea")
.HasColumnName("JOB_DATA");
b.Property<string>("JobGroup")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<short?>("MisfireInstruction")
.HasColumnType("smallint")
.HasColumnName("MISFIRE_INSTR");
b.Property<long?>("NextFireTime")
.HasColumnType("bigint")
.HasColumnName("NEXT_FIRE_TIME");
b.Property<long?>("PreviousFireTime")
.HasColumnType("bigint")
.HasColumnName("PREV_FIRE_TIME");
b.Property<int?>("Priority")
.HasColumnType("integer")
.HasColumnName("PRIORITY");
b.Property<long>("StartTime")
.HasColumnType("bigint")
.HasColumnName("START_TIME");
b.Property<string>("TriggerState")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TRIGGER_STATE");
b.Property<string>("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<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<ulong>("MessageId")
.HasColumnType("INTEGER");
b.Property<bool>("AreRolesEnabled")
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationDate")
.HasColumnType("TEXT");
b.Property<string>("CreatorDisplayName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<ulong>("CreatorUserId")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime?>("DueDateTime")
.HasColumnType("TEXT");
b.Property<bool>("IsClosed")
.HasColumnType("INTEGER");
b.Property<uint>("MaxPlayers")
.HasColumnType("INTEGER");
b.Property<int>("Name")
.HasColumnType("INTEGER");
b.Property<ulong>("ThreadId")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("GuildId", "ChannelId", "MessageId");
b.HasIndex("ThreadId");
b.ToTable("Activities");
b.HasDiscriminator<string>("Discriminator").HasValue("Activity");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityPlayer", b =>
{
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<ulong>("MessageId")
.HasColumnType("INTEGER");
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsOrganizer")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("GuildId", "ChannelId", "MessageId", "UserId");
b.ToTable("ActivityPlayers");
b.HasDiscriminator<string>("Discriminator").HasValue("ActivityPlayer");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.InterstellarActivity", b =>
{
b.HasBaseType("Cocotte.Modules.Activities.Models.Activity");
b.Property<int>("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<uint>("Stage")
.HasColumnType("INTEGER");
b.HasDiscriminator().HasValue("StagedActivity");
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityRolePlayer", b =>
{
b.HasBaseType("Cocotte.Modules.Activities.Models.ActivityPlayer");
b.Property<byte>("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
}
}
}

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Cocotte.Migrations
{
/// <inheritdoc />
public partial class AddOrganizedActivities : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsOrganizer",
table: "ActivityPlayers",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsOrganizer",
table: "ActivityPlayers");
}
}
}

View File

@@ -0,0 +1,689 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<byte[]>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("CalendarName")
.HasColumnType("text")
.HasColumnName("CALENDAR_NAME");
b.Property<byte[]>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("CronExpression")
.IsRequired()
.HasColumnType("text")
.HasColumnName("CRON_EXPRESSION");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("EntryId")
.HasColumnType("text")
.HasColumnName("ENTRY_ID");
b.Property<long>("FiredTime")
.HasColumnType("bigint")
.HasColumnName("FIRED_TIME");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("INSTANCE_NAME");
b.Property<bool>("IsNonConcurrent")
.HasColumnType("bool")
.HasColumnName("IS_NONCONCURRENT");
b.Property<string>("JobGroup")
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("JobName")
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<int>("Priority")
.HasColumnType("integer")
.HasColumnName("PRIORITY");
b.Property<bool?>("RequestsRecovery")
.HasColumnType("bool")
.HasColumnName("REQUESTS_RECOVERY");
b.Property<long>("ScheduledTime")
.HasColumnType("bigint")
.HasColumnName("SCHED_TIME");
b.Property<string>("State")
.IsRequired()
.HasColumnType("text")
.HasColumnName("STATE");
b.Property<string>("TriggerGroup")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("JobName")
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<string>("JobGroup")
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("DESCRIPTION");
b.Property<bool>("IsDurable")
.HasColumnType("bool")
.HasColumnName("IS_DURABLE");
b.Property<bool>("IsNonConcurrent")
.HasColumnType("bool")
.HasColumnName("IS_NONCONCURRENT");
b.Property<bool>("IsUpdateData")
.HasColumnType("bool")
.HasColumnName("IS_UPDATE_DATA");
b.Property<string>("JobClassName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_CLASS_NAME");
b.Property<byte[]>("JobData")
.HasColumnType("bytea")
.HasColumnName("JOB_DATA");
b.Property<bool>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("InstanceName")
.HasColumnType("text")
.HasColumnName("INSTANCE_NAME");
b.Property<long>("CheckInInterval")
.HasColumnType("bigint")
.HasColumnName("CHECKIN_INTERVAL");
b.Property<long>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<bool?>("BooleanProperty1")
.HasColumnType("bool")
.HasColumnName("BOOL_PROP_1");
b.Property<bool?>("BooleanProperty2")
.HasColumnType("bool")
.HasColumnName("BOOL_PROP_2");
b.Property<decimal?>("DecimalProperty1")
.HasColumnType("numeric")
.HasColumnName("DEC_PROP_1");
b.Property<decimal?>("DecimalProperty2")
.HasColumnType("numeric")
.HasColumnName("DEC_PROP_2");
b.Property<int?>("IntegerProperty1")
.HasColumnType("integer")
.HasColumnName("INT_PROP_1");
b.Property<int?>("IntegerProperty2")
.HasColumnType("integer")
.HasColumnName("INT_PROP_2");
b.Property<long?>("LongProperty1")
.HasColumnType("bigint")
.HasColumnName("LONG_PROP_1");
b.Property<long?>("LongProperty2")
.HasColumnType("bigint")
.HasColumnName("LONG_PROP_2");
b.Property<string>("StringProperty1")
.HasColumnType("text")
.HasColumnName("STR_PROP_1");
b.Property<string>("StringProperty2")
.HasColumnType("text")
.HasColumnName("STR_PROP_2");
b.Property<string>("StringProperty3")
.HasColumnType("text")
.HasColumnName("STR_PROP_3");
b.Property<string>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<long>("RepeatCount")
.HasColumnType("bigint")
.HasColumnName("REPEAT_COUNT");
b.Property<long>("RepeatInterval")
.HasColumnType("bigint")
.HasColumnName("REPEAT_INTERVAL");
b.Property<long>("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<string>("SchedulerName")
.HasColumnType("text")
.HasColumnName("SCHED_NAME");
b.Property<string>("TriggerName")
.HasColumnType("text")
.HasColumnName("TRIGGER_NAME");
b.Property<string>("TriggerGroup")
.HasColumnType("text")
.HasColumnName("TRIGGER_GROUP");
b.Property<string>("CalendarName")
.HasColumnType("text")
.HasColumnName("CALENDAR_NAME");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("DESCRIPTION");
b.Property<long?>("EndTime")
.HasColumnType("bigint")
.HasColumnName("END_TIME");
b.Property<byte[]>("JobData")
.HasColumnType("bytea")
.HasColumnName("JOB_DATA");
b.Property<string>("JobGroup")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_GROUP");
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("JOB_NAME");
b.Property<short?>("MisfireInstruction")
.HasColumnType("smallint")
.HasColumnName("MISFIRE_INSTR");
b.Property<long?>("NextFireTime")
.HasColumnType("bigint")
.HasColumnName("NEXT_FIRE_TIME");
b.Property<long?>("PreviousFireTime")
.HasColumnType("bigint")
.HasColumnName("PREV_FIRE_TIME");
b.Property<int?>("Priority")
.HasColumnType("integer")
.HasColumnName("PRIORITY");
b.Property<long>("StartTime")
.HasColumnType("bigint")
.HasColumnName("START_TIME");
b.Property<string>("TriggerState")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TRIGGER_STATE");
b.Property<string>("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<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<ulong>("MessageId")
.HasColumnType("INTEGER");
b.Property<bool>("AreRolesEnabled")
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationDate")
.HasColumnType("TEXT");
b.Property<string>("CreatorDisplayName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<ulong>("CreatorUserId")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime?>("DueDateTime")
.HasColumnType("TEXT");
b.Property<bool>("IsClosed")
.HasColumnType("INTEGER");
b.Property<uint>("MaxPlayers")
.HasColumnType("INTEGER");
b.Property<int>("Name")
.HasColumnType("INTEGER");
b.Property<ulong>("ThreadId")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("GuildId", "ChannelId", "MessageId");
b.HasIndex("ThreadId");
b.ToTable("Activities");
b.HasDiscriminator<string>("Discriminator").HasValue("Activity");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityPlayer", b =>
{
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<ulong>("MessageId")
.HasColumnType("INTEGER");
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("HasCompleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsOrganizer")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("GuildId", "ChannelId", "MessageId", "UserId");
b.ToTable("ActivityPlayers");
b.HasDiscriminator<string>("Discriminator").HasValue("ActivityPlayer");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.InterstellarActivity", b =>
{
b.HasBaseType("Cocotte.Modules.Activities.Models.Activity");
b.Property<int>("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<uint>("Stage")
.HasColumnType("INTEGER");
b.HasDiscriminator().HasValue("StagedActivity");
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.ActivityRolePlayer", b =>
{
b.HasBaseType("Cocotte.Modules.Activities.Models.ActivityPlayer");
b.Property<byte>("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
}
}
}

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Cocotte.Migrations
{
/// <inheritdoc />
public partial class AddActivityPlayerHasCompleted : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "HasCompleted",
table: "ActivityPlayers",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "HasCompleted",
table: "ActivityPlayers");
}
}
}

View File

@@ -538,6 +538,12 @@ namespace Cocotte.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("HasCompleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsOrganizer")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
@@ -561,6 +567,13 @@ namespace Cocotte.Migrations
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");

View File

@@ -9,6 +9,9 @@ namespace Cocotte.Modules.Activities;
public class ActivityFormatter
{
private const int FieldsChunkSize = 20;
private const string EmptyField = "\u200B";
private readonly ActivityOptions _options;
private readonly InterstellarFormatter _interstellarFormatter;
@@ -31,9 +34,10 @@ public class ActivityFormatter
ActivityName.InterstellarExploration => "Porte interstellaire",
ActivityName.BreakFromDestiny => "Échapper au destin (3v3)",
ActivityName.CriticalAbyss => "Abîme critique (8v8)",
ActivityName.Event => "Event",
ActivityName.Minigame => "Mini-Jeux",
ActivityName.Fishing => "Pêche",
ActivityName.MirroriaRace => "Course Mirroria",
ActivityName.Event => "Évènement",
_ => throw new ArgumentOutOfRangeException(nameof(activityName), activityName, null)
};
}
@@ -45,25 +49,79 @@ public class ActivityFormatter
public EmbedBuilder ActivityEmbed(Activity activity, IReadOnlyCollection<ActivityPlayer> players)
{
// Activity full
bool activityFull = players.Count >= activity.MaxPlayers;
int GetNamesPadding(IReadOnlyCollection<ActivityPlayer> activityPlayers) => activityPlayers.Count > 0 ? activityPlayers.Max(p => p.Name.Length) : 0;
// Compute padding using player with longest name
var namePadding = players.Count > 0 ? players.Max(p => p.Name.Length) : 0;
// Load activity players and organizers
var participants = activity.Participants.OrderBy(p => p.HasCompleted).ToArray();
var organizers = activity.Organizers.ToArray();
// Activity full
bool activityFull = participants.Length >= activity.MaxPlayers;
// Players and organizers fields
var fields = new List<EmbedFieldBuilder>();
// Add organizers if it's an organized activity
if (activity is OrganizedActivity)
{
var organizersFields = organizers.Chunk(FieldsChunkSize).Select(organizersChunk =>
new EmbedFieldBuilder()
.WithName(EmptyField)
.WithIsInline(true)
.WithValue($"{(!organizersChunk.Any() ? "*Aucun organisateur inscrit*" : string.Join("\n", organizersChunk.Select(p => FormatActivityPlayer(p, GetNamesPadding(organizersChunk), showOrganizerRole: ActivityHelper.IsEventActivity(activity.Type)))))}")
).ToArray();
if (organizersFields.Length > 0)
{
organizersFields[0].Name = "Organisateurs";
}
// Complete with empty fields to go to next line
var emptyFields = Enumerable.Repeat(0, (3 - organizersFields.Length) % 3).Select(_ =>
new EmbedFieldBuilder()
.WithName(EmptyField)
.WithValue(EmptyField)
.WithIsInline(true)
);
fields.AddRange(organizersFields);
fields.AddRange(emptyFields);
}
// Players field
var playersField = new EmbedFieldBuilder()
.WithName("Joueurs inscrits")
.WithValue($"{(!players.Any() ? "*Aucun joueur inscrit*" : string.Join("\n", players.Select(p => FormatActivityPlayer(p, namePadding))))}");
var playersFields = participants.Chunk(FieldsChunkSize).Select(participantsChunk =>
new EmbedFieldBuilder()
.WithName(EmptyField)
.WithIsInline(true)
.WithValue($"{(!participantsChunk.Any() ? "*Aucun joueur inscrit*" : string.Join("\n", participantsChunk.Select(p => FormatActivityPlayer(p, GetNamesPadding(participantsChunk), hideRoles: activity is OrganizedActivity))))}")
).ToList();
// Insert empty fields in third column
for (int i = 2; i <= playersFields.Count; i += 3)
{
playersFields.Insert(i, new EmbedFieldBuilder().WithName(EmptyField).WithValue(EmptyField).WithIsInline(true));
}
if (playersFields.Count > 0)
{
playersFields[0].Name = "Joueurs inscrits";
}
fields.AddRange(playersFields);
string countTitlePart = activity.MaxPlayers == ActivityHelper.UnlimitedPlayers
? ""
: $"({participants.Length}/{activity.MaxPlayers})";
var title = activity switch
{
StagedActivity stagedActivity =>
$"{FormatActivityName(activity.Name)} ({players.Count}/{activity.MaxPlayers}) - Étage {stagedActivity.Stage}",
$"{FormatActivityName(activity.Name)} {countTitlePart} - Étage {stagedActivity.Stage}",
InterstellarActivity interstellar =>
$"{FormatActivityName(activity.Name)} {_interstellarFormatter.FormatInterstellarColor(interstellar.Color)} ({players.Count}/{activity.MaxPlayers})",
$"{FormatActivityName(activity.Name)} {_interstellarFormatter.FormatInterstellarColor(interstellar.Color)} {countTitlePart}",
OrganizedActivity =>
$"{(ActivityHelper.IsEventActivity(activity.Type) ? "Organisation d'évènement" : $"Proposition d'aide - {FormatActivityName(activity.Name)}")} {countTitlePart}",
_ =>
$"{FormatActivityName(activity.Name)} ({players.Count}/{activity.MaxPlayers})"
$"{FormatActivityName(activity.Name)} {countTitlePart}"
};
// Build description
@@ -72,7 +130,13 @@ public class ActivityFormatter
// Add time if specified
if (activity.DueDateTime is { } dueDateTime)
{
descriptionBuilder.AppendLine($"**:clock2: {TimestampTag.FormatFromDateTime(dueDateTime, TimestampTagStyles.ShortTime)} ― {(activity.IsClosed ? "Fermée" : TimestampTag.FormatFromDateTime(dueDateTime, TimestampTagStyles.Relative))}**");
// Also Add date of organized
if (activity is OrganizedActivity)
{
descriptionBuilder.AppendLine($"**:date: {TimestampTag.FormatFromDateTime(dueDateTime, TimestampTagStyles.LongDate)}**");
}
descriptionBuilder.AppendLine($"**:clock2: {TimestampTag.FormatFromDateTime(dueDateTime, TimestampTagStyles.ShortTime)} | {(activity.IsClosed ? "Fermée" : TimestampTag.FormatFromDateTime(dueDateTime, TimestampTagStyles.Relative))}**");
descriptionBuilder.AppendLine();
}
else
{
@@ -88,18 +152,25 @@ public class ActivityFormatter
// Add thread link
descriptionBuilder.AppendLine();
descriptionBuilder.Append($"**[Fil associé]({ChannelUtils.GetChannelLink(activity.GuildId, activity.ThreadId)})**");
descriptionBuilder.Append(
$"""
**⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯**
**[Fil associé]({ChannelUtils.GetChannelLink(activity.GuildId, activity.ThreadId)})**
**⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯**
""");
string bannerUrl = GetActivityBanner(activity.Name);
var color = activity.IsClosed ? Colors.CocotteRed : (activityFull ? Colors.CocotteOrange : Colors.CocotteBlue);
var color = activity.IsClosed ?
Colors.CocotteRed : activityFull ?
Colors.CocotteOrange : Colors.CocotteBlue;
var builder = new EmbedBuilder()
.WithColor(color)
.WithTitle(title)
.WithDescription(descriptionBuilder.ToString())
.WithImageUrl(bannerUrl)
.WithFields(playersField);
.WithFields(fields);
// Add material for interstellar exploration
if (activity is InterstellarActivity interstellarActivity)
@@ -122,15 +193,22 @@ public class ActivityFormatter
ActivityName.BreakFromDestiny => "BR",
ActivityName.CriticalAbyss => "CA",
ActivityName.Fishing => "FI",
ActivityName.Event => "EV",
ActivityName.Minigame => "MG",
ActivityName.MirroriaRace => "MR",
ActivityName.Event => "EV",
_ => "NA"
};
public string FormatActivityPlayer(ActivityPlayer player, int namePadding) => player switch
public string FormatActivityPlayer(ActivityPlayer player, int namePadding, bool showOrganizerRole = false, bool hideRoles = false) =>
player.HasCompleted
? $"~~{FormatActivityPlayerSub(player, namePadding, showOrganizerRole, hideRoles)}~~"
: FormatActivityPlayerSub(player, namePadding, showOrganizerRole, hideRoles);
private string FormatActivityPlayerSub(ActivityPlayer player, int namePadding, bool showOrganizerRole = false, bool hideRoles = false) => player switch
{
ActivityRolePlayer rolePlayer => $"` {player.Name.PadRight(namePadding)} ` **** {RolesToEmotes(rolePlayer.Roles)}",
_ => $"` {player.Name} `"
ActivityRolePlayer rolePlayer when !hideRoles => $"` {player.Name.PadRight(namePadding)} ` **|** {RolesToEmotes(rolePlayer.Roles)}",
_ when showOrganizerRole && player.IsOrganizer => $"` {player.Name.PadRight(namePadding)} ` **|** {_options.OrganizerEmote} ",
_ => $"` {player.Name.PadRight(namePadding)} `"
};
private string RolesToEmotes(PlayerRoles rolePlayerRoles)
@@ -139,17 +217,7 @@ public class ActivityFormatter
if (rolePlayerRoles.HasFlag(PlayerRoles.Helper))
{
emotesBuilder.Append($" {_options.HelperEmote} ");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Dps))
{
emotesBuilder.Append($" {_options.DpsEmote} ");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Tank))
{
emotesBuilder.Append($" {_options.TankEmote} ");
emotesBuilder.Append($" {_options.HelperEmote} **|**");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Support))
@@ -157,6 +225,16 @@ public class ActivityFormatter
emotesBuilder.Append($" {_options.SupportEmote} ");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Tank))
{
emotesBuilder.Append($" {_options.TankEmote} ");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Dps))
{
emotesBuilder.Append($" {_options.DpsEmote} ");
}
return emotesBuilder.ToString();
}
}

View File

@@ -9,7 +9,7 @@ namespace Cocotte.Modules.Activities;
public class ActivityHelper
{
private const uint UnlimitedPlayers = uint.MaxValue;
public const uint UnlimitedPlayers = uint.MaxValue;
private readonly ActivityOptions _options;
private readonly ActivitiesRepository _activitiesRepository;
@@ -34,6 +34,7 @@ public class ActivityHelper
var role when role == _options.DpsRoleId => PlayerRoles.Dps,
var role when role == _options.TankRoleId => PlayerRoles.Tank,
var role when role == _options.SupportRoleId => PlayerRoles.Support,
var role when role == _options.OrganizerRoleId => PlayerRoles.Organizer,
_ => PlayerRoles.None
};
}
@@ -56,30 +57,43 @@ public class ActivityHelper
ActivityName.BreakFromDestiny => ActivityType.Pvp3Players,
ActivityName.Event or
ActivityName.Fishing => ActivityType.Event8Players,
ActivityName.Minigame => ActivityType.Other8Players,
ActivityName.MirroriaRace => ActivityType.Event4Players,
ActivityName.MirroriaRace => ActivityType.Other4Players,
_ => ActivityType.Other
_ => ActivityType.OtherUnlimitedPlayers
};
public static uint ActivityTypeToMaxPlayers(ActivityType activityType) => activityType switch
{
ActivityType.Pve4Players or
ActivityType.Event4Players => 4,
ActivityType.Other4Players => 4,
ActivityType.Pve8Players or
ActivityType.Pvp8Players or
ActivityType.Event8Players => 8,
ActivityType.Other8Players => 8,
ActivityType.Pvp3Players => 3,
ActivityType.Other => UnlimitedPlayers,
ActivityType.OtherUnlimitedPlayers => UnlimitedPlayers,
_ => 0
};
public static bool IsEventActivity(ActivityType activityType) =>
activityType is ActivityType.Other8Players or ActivityType.Other4Players or ActivityType.OtherUnlimitedPlayers;
public bool IsOrganizer(OrganizedActivity organizedActivity, SocketGuildUser user)
{
// If it is an event, check if the user has organizer role, otherwise helper
if (IsEventActivity(organizedActivity.Type))
{
return user.Roles.Any(r => r.Id == _options.OrganizerRoleId);
}
return user.Roles.Any(r => r.Id == _options.HelperRoleId);
}
public async Task UpdateActivityEmbed(IMessageChannel channel, Activity activity, ActivityUpdateReason updateReason)
{
// Fetch players
@@ -93,7 +107,7 @@ public class ActivityHelper
var isActivityFull = players.Count >= activity.MaxPlayers;
properties.Components = updateReason switch
{
ActivityUpdateReason.PlayerJoin when isActivityFull => ActivityComponents(activity.MessageId, disabled: true).Build(),
ActivityUpdateReason.PlayerJoin when isActivityFull && activity is not OrganizedActivity => ActivityComponents(activity.MessageId, disabled: true).Build(),
ActivityUpdateReason.PlayerLeave when !isActivityFull => ActivityComponents(activity.MessageId, disabled: false).Build(),
_ => Optional<MessageComponent>.Unspecified
};

View File

@@ -54,9 +54,9 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
}
#endif
#region activities
#region Activities
[SlashCommand("abime-néant", "Créer un groupe pour l'Abîme du Néant")]
[SlashCommand("abime-neant", "Créer un groupe pour l'Abîme du Néant")]
[Alias("abime", "abyss")]
public async Task ActivityVoidAbyss(
[Summary("étage", "A quel étage vous êtes")] [MinValue(1), MaxValue(6)] uint stage,
@@ -140,14 +140,14 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
await CreateActivity(ActivityName.CriticalAbyss, timeInput, description);
}
[SlashCommand("evenement", "Créer un groupe pour les évènements")]
[Alias("event")]
public async Task ActivityEvent(
[SlashCommand("mini-jeux", "Créer un groupe pour les mini-jeux")]
[Alias("jeux")]
public async Task ActivityMinigame(
[Summary("joueurs", "Nombre de joueurs maximum pour cette activité")] [MinValue(2), MaxValue(16)]
uint maxPlayers = 8, [Summary("heure", "Heure à laquelle l'activité est prévue")] string? timeInput = null,
[Summary("description", "Message accompagnant la demande de groupe")] string description = "")
{
await CreateActivity(ActivityName.Event, timeInput, description, areRolesEnabled: false,
await CreateActivity(ActivityName.Minigame, timeInput, description, areRolesEnabled: false,
maxPlayers: maxPlayers);
}
@@ -171,12 +171,94 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
#endregion
#region Organization
[SlashCommand("organiser-abime", "Organiser un groupe d'aide pour l'abîme du néant")]
public async Task OrganizeVoidAbyss(
[Summary("jour", "Jour auquel l'activité est prévu")] CocotteDayOfWeek day,
[Summary("heure", "Heure à laquelle l'activité est prévue")] string timeInput,
[Summary("joueurs", "Nombre de joueurs maximum qui peuvent s'inscrire")] uint maxPlayers,
string description = "")
{
if (!TimeOnly.TryParse(timeInput, out var time))
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(
"**Heure invalide**, essayez avec le **format** `heure:minutes`\nPar exemple: `15:30`")
.Build()
);
return;
}
// Calculate date
var date = DateTimeUtils.NextDateWithDayAndTime((DayOfWeek) day, time);
await CreateActivity(ActivityName.Abyss, null, description, true, maxPlayers, date: date);
}
[SlashCommand("organiser-origine", "Organiser un groupe d'aide pour l'origine de la guerre")]
public async Task OrganizeOrigins(
[Summary("jour", "Jour auquel l'activité est prévu")] CocotteDayOfWeek day,
[Summary("heure", "Heure à laquelle l'activité est prévue")] string timeInput,
[Summary("joueurs", "Nombre de joueurs maximum qui peuvent s'inscrire")] uint maxPlayers,
string description = "")
{
if (!TimeOnly.TryParse(timeInput, out var time))
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(
"**Heure invalide**, essayez avec le **format** `heure:minutes`\nPar exemple: `15:30`")
.Build()
);
return;
}
// Calculate date
var date = DateTimeUtils.NextDateWithDayAndTime((DayOfWeek) day, time);
await CreateActivity(ActivityName.OriginsOfWar, null, description, true, maxPlayers, date: date);
}
[SlashCommand("organiser-evenement", "Organiser un événement")]
public async Task OrganizeEvent(
[Summary("jour", "Jour auquel l'activité est prévu")] CocotteDayOfWeek day,
[Summary("heure", "Heure à laquelle l'activité est prévue")] string timeInput,
[Summary("joueurs", "Nombre de joueurs maximum qui peuvent s'inscrire")] uint maxPlayers,
string description = "")
{
if (!TimeOnly.TryParse(timeInput, out var time))
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(
"**Heure invalide**, essayez avec le **format** `heure:minutes`\nPar exemple: `15:30`")
.Build()
);
return;
}
// Calculate date
var date = DateTimeUtils.NextDateWithDayAndTime((DayOfWeek) day, time);
await CreateActivity(ActivityName.Event, null, description, false, maxPlayers, date: date);
}
#endregion
private async Task CreateActivity(ActivityName activityName, string? timeInput, string description,
bool areRolesEnabled = true, uint? maxPlayers = null, uint? stage = null,
InterstellarColor? interstellarColor = null)
InterstellarColor? interstellarColor = null, DateTime? date = null)
{
// Check time if it's specified
DateTime? dueDate = null;
var dueDate = date;
if (timeInput is not null)
{
if (!TimeOnly.TryParse(timeInput, out var parsedTime))
@@ -209,7 +291,26 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
maxPlayers ??= ActivityHelper.ActivityTypeToMaxPlayers(activityType);
Activity activity;
if (stage is not null)
// Create organized activity if date is not null
if (date is not null)
{
activity = new OrganizedActivity
{
MessageId = response.Id,
ChannelId = Context.Channel.Id,
GuildId = Context.Guild.Id,
ThreadId = threadId,
CreatorUserId = Context.User.Id,
CreatorDisplayName = ((SocketGuildUser) Context.User).DisplayName,
Description = description,
DueDateTime = dueDate,
Type = activityType,
Name = activityName,
AreRolesEnabled = areRolesEnabled,
MaxPlayers = (uint) maxPlayers
};
}
else if (stage is not null)
{
activity = new StagedActivity
{
@@ -269,10 +370,10 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
// Add activity to db
await _activitiesRepository.AddActivity(activity);
// Add creator to activity
var rolePlayer = CreateActivityPlayer(activity, User, areRolesEnabled);
// Add creator to activity, make it an helper
var activityPlayer = CreateActivityPlayer(activity, User, areRolesEnabled, activity is OrganizedActivity);
activity.ActivityPlayers.Add(rolePlayer);
activity.ActivityPlayers.Add(activityPlayer);
await _activitiesRepository.SaveChanges();
@@ -283,7 +384,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
{
m.Content = "";
m.Components = components.Build();
m.Embed = _activityFormatter.ActivityEmbed(activity, Enumerable.Repeat(rolePlayer, 1).ToImmutableList())
m.Embed = _activityFormatter.ActivityEmbed(activity, Enumerable.Repeat(activityPlayer, 1).ToImmutableList())
.Build();
});
@@ -310,7 +411,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
}
}
private ActivityPlayer CreateActivityPlayer(Activity activity, SocketGuildUser user, bool areRolesEnabled)
private ActivityPlayer CreateActivityPlayer(Activity activity, SocketGuildUser user, bool areRolesEnabled, bool isOrganizer = false)
{
return areRolesEnabled
? new ActivityRolePlayer
@@ -318,13 +419,15 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
Activity = activity,
UserId = user.Id,
Name = user.DisplayName,
Roles = _activityHelper.GetPlayerRoles(user.Roles)
Roles = _activityHelper.GetPlayerRoles(user.Roles),
IsOrganizer = isOrganizer
}
: new ActivityPlayer
{
Activity = activity,
UserId = user.Id,
Name = user.DisplayName
Name = user.DisplayName,
IsOrganizer = isOrganizer
};
}
@@ -396,7 +499,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
}
// Check if user has permission to delete this activity
if (User.Id != activity.CreatorUserId && !User.GetPermissions((IGuildChannel) Context.Channel).ManageMessages)
if (User.Id != activity.CreatorUserId && Context.User.Id != (await Context.Client.GetApplicationInfoAsync()).Owner.Id)
{
await RespondAsync(
ephemeral: true,
@@ -429,37 +532,43 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
embed: EmbedUtils
.ErrorEmbed(self
? "Vous êtes **déjà inscrit** à cette activité"
: $"{user.DisplayName} est **déjà inscrit** à cette activité").Build()
: $"{MentionUtils.MentionUser(user.Id)} est **déjà inscrit** à cette activité").Build()
);
return false;
}
// Check if activity is closed
if (activity.IsClosed)
// If activity is an organized activity and user is an organizer, bypass full and closed checks
var isOrganizer = activity is OrganizedActivity organizedActivity &&
_activityHelper.IsOrganizer(organizedActivity, user);
if (!isOrganizer)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité est fermée").Build()
);
// Check if activity is closed
if (activity.IsClosed)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité est fermée").Build()
);
return false;
}
return false;
}
// Check if activity is full
if (await _activitiesRepository.ActivityPlayerCount(activity) >= activity.MaxPlayers)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("L'activité est **complète**").Build()
);
// Check if activity is full
if (await _activitiesRepository.ActivityPlayerCount(activity) >= activity.MaxPlayers)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("L'activité est **complète**").Build()
);
return false;
return false;
}
}
_logger.LogTrace("Player {Player} joined activity {Id}", user.DisplayName, activity.MessageId);
var activityPlayer = CreateActivityPlayer(activity, user, activity.AreRolesEnabled);
var activityPlayer = CreateActivityPlayer(activity, user, activity.AreRolesEnabled, isOrganizer: isOrganizer);
// Add player to activity
activity.ActivityPlayers.Add(activityPlayer);
@@ -473,7 +582,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
await thread.AddUserAsync(user);
string embedContent = $"**{user.DisplayName}** a été **ajouté** à l'activité";
string embedContent = $"{MentionUtils.MentionUser(user.Id)} a été **ajouté** à l'activité";
if (self)
{
await thread.SendMessageAsync(
@@ -501,7 +610,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
embed: EmbedUtils
.ErrorEmbed(self
? "Vous n'êtes **pas inscrit** à cette activité"
: $"{user.DisplayName} n'est **pas inscrit** à cette activité").Build()
: $"{MentionUtils.MentionUser(user.Id)} n'est **pas inscrit** à cette activité").Build()
);
return false;
@@ -520,7 +629,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
await thread.RemoveUserAsync(user);
string embedContent = $"{user.DisplayName} a été **enlevé** de l'activité";
string embedContent = $"{MentionUtils.MentionUser(user.Id)} a été **enlevé** de l'activité";
if (self)
{
await thread.SendMessageAsync(

View File

@@ -21,8 +21,8 @@ public partial class ActivityModule
Activity = activity,
Name = $"Player{Random.Shared.Next(1, 100)}",
UserId = (ulong) Random.Shared.NextInt64(),
Roles = (PlayerRoles) Random.Shared.Next((int) (PlayerRoles.Dps | PlayerRoles.Helper |
PlayerRoles.Support | PlayerRoles.Tank) + 1)
Roles = (PlayerRoles) Random.Shared.Next((int)PlayerRoles.Helper, (int) (PlayerRoles.Dps | PlayerRoles.Helper |
PlayerRoles.Support | PlayerRoles.Tank) + 1 - (int)PlayerRoles.Helper)
};
// Add the player to the activity

View File

@@ -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<ulong> CreateThread(ActivityName activityName, string creatorName)
@@ -174,6 +175,155 @@ public partial class ActivityModule
await RespondAsync(pingMessageBuilder.ToString());
}
[SlashCommand("description", "Changer la description de l'activité")]
public async Task ThreadChangeDescription()
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
// Open modal
await RespondWithModalAsync<ActivityDescriptionModal>("activity description_modal");
}
[SlashCommand("etage", "Changer l'étage de l'activité pour l'abîme du néant et l'origine de la guerre")]
public async Task ThreadChangeStage([Summary("étage", "Nouvel étage, de 1 à 6 en abîme du néant, et de 1 à 25 en origine de la guerre")] [MinValue(1), MaxValue(25)] uint stage)
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
if (activity is not StagedActivity stagedActivity)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette commande n'est utilisable que pour l'abîme du néant et l'origine de la guerre").Build()
);
return;
}
// Check if stage is valid
var stageValid = (activity.Name, stage) switch
{
(ActivityName.Abyss, >= 1 and <= 6) => true,
(ActivityName.OriginsOfWar, >= 1 and <= 25) => true,
_ => false
};
if (!stageValid)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Etage invalide").Build()
);
return;
}
stagedActivity.Stage = stage;
await _activitiesRepository.SaveChanges();
await UpdateActivityEmbed(activity, ActivityUpdateReason.Update);
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed($"L'activité est maintenant bien définie à **l'étage {stage}**").Build()
);
}
[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)
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
// Update description
activity.Description = descriptionModal.Description;
await _activitiesRepository.SaveChanges();
await UpdateActivityEmbed(activity, ActivityUpdateReason.Update);
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed("**La description** a bien été changée").Build()
);
}
private async Task<bool> CheckCommandInThread(Activity? activity, bool checkCreator = false)
{
// Check if activity is not null (means we are in a valid thread)
@@ -200,3 +350,12 @@ public partial class ActivityModule
return true;
}
}
public class ActivityDescriptionModal : IModal
{
public string Title => "Nouvelle description";
[InputLabel("Description")]
[ModalTextInput("activity_description", TextInputStyle.Paragraph)]
public required string Description { get; set; }
}

View File

@@ -12,6 +12,7 @@ public enum ActivityName
BreakFromDestiny,
CriticalAbyss,
Fishing,
Event,
MirroriaRace
Minigame,
MirroriaRace,
Event
}

View File

@@ -6,7 +6,7 @@ public enum ActivityType
Pve8Players,
Pvp8Players,
Pvp3Players,
Event8Players,
Event4Players,
Other
Other8Players,
Other4Players,
OtherUnlimitedPlayers
}

View File

@@ -29,6 +29,12 @@ public class Activity
public List<ActivityPlayer> ActivityPlayers { get; init; } = new();
[NotMapped]
public IEnumerable<ActivityPlayer> Participants => ActivityPlayers.Where(p => !p.IsOrganizer);
[NotMapped]
public IEnumerable<ActivityPlayer> Organizers => ActivityPlayers.Where(p => p.IsOrganizer);
[NotMapped]
public string JobKey => $"{GuildId}/{ChannelId}/{MessageId}";
public override string ToString()

View File

@@ -10,6 +10,8 @@ public class ActivityPlayer
public required ulong UserId { get; init; }
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; }

View File

@@ -0,0 +1,8 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Cocotte.Modules.Activities.Models;
public class OrganizedActivity : Activity
{
}

View File

@@ -3,9 +3,10 @@
[Flags]
public enum PlayerRoles : byte
{
None = 0b0000,
Helper = 0b0001,
Dps = 0b0010,
Tank = 0b0100,
Support = 0b1000
None = 0b0000_0000,
Helper = 0b0000_0001,
Dps = 0b0000_0010,
Tank = 0b0000_0100,
Support = 0b0000_1000,
Organizer = 0b0001_0000
}

View File

@@ -4,6 +4,9 @@ public class ActivityOptions
{
public const string SectionName = "ActivityOptions";
public ulong OrganizerRoleId { get; init; }
public required string OrganizerEmote { get; init; }
public ulong HelperRoleId { get; init; }
public required string HelperEmote { get; init; }

View File

@@ -93,6 +93,12 @@ await using(var scope = host.Services.CreateAsyncScope())
}
else
{
// Backup database before migrations
if (File.Exists("cocotte.db"))
{
File.Copy("cocotte.db", $"cocotte.{DateTime.UtcNow:yy-MM-dd hh-mm-ss}.db");
}
// Apply migrations
await dbContext.Database.MigrateAsync();
}

View File

@@ -10,6 +10,7 @@ public class CocotteDbContext : DbContext
public DbSet<Activity> Activities => Set<Activity>();
public DbSet<StagedActivity> StagedActivities => Set<StagedActivity>();
public DbSet<InterstellarActivity> InterstellarActivities => Set<InterstellarActivity>();
public DbSet<OrganizedActivity> OrganizedActivities => Set<OrganizedActivity>();
public DbSet<ActivityPlayer> ActivityPlayers => Set<ActivityPlayer>();
public DbSet<ActivityRolePlayer> ActivityRolePlayers => Set<ActivityRolePlayer>();

View File

@@ -10,7 +10,7 @@ public class CdnUtils
/// <summary>
/// Needs to be updated each time a media is updated on the CDN
/// </summary>
private const string RandomSuffix = "assets1";
private const string RandomSuffix = "assets2";
public static string GetAsset(string assetName)
{

View File

@@ -0,0 +1,21 @@
using Discord.Interactions;
namespace Cocotte.Utils;
public enum CocotteDayOfWeek
{
[ChoiceDisplay("Lundi")]
Monday = DayOfWeek.Monday,
[ChoiceDisplay("Mardi")]
Tuesday = DayOfWeek.Tuesday,
[ChoiceDisplay("Mercredi")]
Wednesday = DayOfWeek.Wednesday,
[ChoiceDisplay("Jeudi")]
Thursday = DayOfWeek.Thursday,
[ChoiceDisplay("Vendredi")]
Friday = DayOfWeek.Friday,
[ChoiceDisplay("Samedi")]
Saturday = DayOfWeek.Saturday,
[ChoiceDisplay("Dimanche")]
Sunday = DayOfWeek.Sunday
}

View File

@@ -14,6 +14,15 @@ public static class DateTimeUtils
return offset;
}
public static DateTime NextDateWithDayAndTime(DayOfWeek desired, TimeOnly time)
{
var date = DateTime.Today;
date = date.AddDays(CalculateDayOfWeekOffset(date.DayOfWeek, desired)).WithTimeOnly(time);
// If it's previous than current date, add 7 days
return date < DateTime.Now ? date.AddDays(7) : date;
}
public static DateTime WithTimeOnly(this DateTime dateTime, TimeOnly timeOnly)
{
return new DateTime(

View File

@@ -1,5 +1,8 @@
{
"ActivityOptions": {
"OrganizerRoleId": 0,
"OrganizerEmote": "",
"HelperRoleId": 0,
"HelperEmote": "",