Compare commits

...

4 Commits

Author SHA1 Message Date
13d332d306 [Activity] Fix command names 2023-04-13 21:43:31 +02:00
84cac63c9d [Activity] Fix embed field character limit 2023-04-13 21:24:16 +02:00
ad4ee02631 [Activity] Add database backup on migration 2023-04-10 12:38:11 +02:00
4b23dfd8a0 [Activity] Add has completed command 2023-04-10 12:26:17 +02:00
9 changed files with 855 additions and 25 deletions

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,9 @@ namespace Cocotte.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("HasCompleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsOrganizer")
.HasColumnType("INTEGER");

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.Minigame => "Évènement",
ActivityName.Minigame => "Mini-Jeux",
ActivityName.Fishing => "Pêche",
ActivityName.MirroriaRace => "Course Mirroria",
ActivityName.Event => "Évènement",
_ => throw new ArgumentOutOfRangeException(nameof(activityName), activityName, null)
};
}
@@ -45,8 +49,10 @@ public class ActivityFormatter
public EmbedBuilder ActivityEmbed(Activity activity, IReadOnlyCollection<ActivityPlayer> players)
{
int GetNamesPadding(IReadOnlyCollection<ActivityPlayer> activityPlayers) => activityPlayers.Count > 0 ? activityPlayers.Max(p => p.Name.Length) : 0;
// 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
@@ -58,24 +64,50 @@ public class ActivityFormatter
// Add organizers if it's an organized activity
if (activity is OrganizedActivity)
{
var organizersPadding = organizers.Length > 0 ? organizers.Max(p => p.Name.Length) : 0;
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();
var organizersField = new EmbedFieldBuilder()
.WithName("Organisateurs")
.WithValue($"{(!organizers.Any() ? "*Aucun organisateur inscrit*" : string.Join("\n", organizers.Select(p => FormatActivityPlayer(p, organizersPadding, ActivityHelper.IsEventActivity(activity.Type)))))}");
if (organizersFields.Length > 0)
{
organizersFields[0].Name = "Organisateurs";
}
fields.Add(organizersField);
// 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);
}
// Compute padding using player with longest name
var participantsPadding = participants.Length > 0 ? participants.Max(p => p.Name.Length) : 0;
// Players field
var playersField = new EmbedFieldBuilder()
.WithName("Joueurs inscrits")
.WithValue($"{(!participants.Any() ? "*Aucun joueur inscrit*" : string.Join("\n", participants.Select(p => FormatActivityPlayer(p, participantsPadding))))}");
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();
fields.Add(playersField);
// 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
? ""
@@ -129,7 +161,9 @@ public class ActivityFormatter
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)
@@ -159,16 +193,22 @@ public class ActivityFormatter
ActivityName.BreakFromDestiny => "BR",
ActivityName.CriticalAbyss => "CA",
ActivityName.Fishing => "FI",
ActivityName.Minigame => "EV",
ActivityName.Minigame => "MG",
ActivityName.MirroriaRace => "MR",
ActivityName.Event => "EV",
_ => "NA"
};
public string FormatActivityPlayer(ActivityPlayer player, int namePadding, bool isEvent = false) => 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)}",
_ when isEvent && player.IsOrganizer => $"` {player.Name.PadRight(namePadding)} ` **|** {_options.OrganizerEmote} ",
_ => $"` {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)

View File

@@ -56,7 +56,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
#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,8 +140,8 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
await CreateActivity(ActivityName.CriticalAbyss, timeInput, description);
}
[SlashCommand("evenement", "Créer un groupe pour les évènements")]
[Alias("event")]
[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,
@@ -225,7 +225,7 @@ public partial class ActivityModule : InteractionModuleBase<SocketInteractionCon
await CreateActivity(ActivityName.OriginsOfWar, null, description, true, maxPlayers, date: date);
}
[SlashCommand("organiser-evenement", "Organiser un groupe d'aide pour l'abîme du néant")]
[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,

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

View File

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

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,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)
{