Compare commits

...

34 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
224fe4cb25 [Activity] Change event to minigame and add event 2023-04-10 11:56:49 +02:00
ca3e951d9a [Activity] Add stage modification 2023-04-10 11:55:52 +02:00
37a4da26d7 [Activity] Change permissions for activity delete 2023-03-30 17:48:55 +02:00
d000e6a299 [Activity] Fix embed symbols 2023-03-27 21:51:00 +02:00
08af0afbfb [Actitivy] Fix embed on mobile 2023-03-27 21:46:31 +02:00
b4e7e52827 [Activity] Add date for organized activities 2023-03-27 21:31:14 +02:00
7a76ca2147 [Activity] Update some messages 2023-03-27 01:34:57 +02:00
54b4e6fb3f [Activity] Add description change command in thread 2023-03-27 00:42:46 +02:00
78fe2d5913 [Activity] Add organized activities 2023-03-27 00:22:43 +02:00
a6e8ad0401 [CompositeRoles] Add fix command 2023-03-26 17:57:46 +02:00
a79ed18e26 [Activity] Add activity close and open commands 2023-03-26 15:34:53 +02:00
1083f604f7 [Activity] Add activity auto close 2023-03-26 14:56:54 +02:00
63e464e602 [Activity] Cleanup schema and DueDateTime 2023-03-26 10:33:03 +02:00
cd6a9e87ad [Activity] Add due time 2023-03-26 00:43:15 +01:00
6001a3dc24 [Database] Add migrations for production 2023-03-25 21:41:46 +01:00
cb0b993ceb [CompositeRoles] Directly deserialiaze to ulong 2023-03-25 10:57:57 +01:00
14f90a2d18 [CompositeRoles] Add listener and options 2023-03-25 09:25:20 +01:00
ced9d15083 [Activity] Add thread ping and refactor activity join/leave 2023-03-24 23:39:35 +01:00
a3051ed060 [Activity] Add thread add and remove player commands 2023-03-24 00:01:04 +01:00
bd52e37454 [Activity] Add thread creation and start message 2023-03-23 22:49:49 +01:00
79d9f16a8b [Activity] Add CDN refresh 2023-03-23 11:04:34 +01:00
b6897f7327 [Activity] Add interstellar exploration formatter 2023-03-23 10:16:25 +01:00
8db87b9098 [Activity] Add all activities 2023-03-22 22:59:37 +01:00
ef948dba27 [Activity] Update component style
- Add icons
- Disable button when activity is full
2023-03-22 12:26:21 +01:00
8dcefeeb39 [Activity] Cleanup embed and code 2023-03-22 11:33:34 +01:00
3f2dc16fad [Activit] Add banner and stage to embed 2023-03-22 10:12:04 +01:00
21996a2e03 [Activity] Add full check 2023-03-21 22:52:47 +01:00
bffa7d06bb [Activity] Add activity join/leave 2023-03-21 22:20:00 +01:00
094f07ea3c [Activity] Add persistence 2023-03-20 16:56:53 +01:00
d47442b984 [Activity] Add activity creation 2023-03-20 14:34:50 +01:00
56 changed files with 6289 additions and 200 deletions

View File

@@ -8,7 +8,24 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
<PackageReference Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite" Version="0.4.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>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.6.2" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.6.2" />
<PackageReference Include="Quartz.Serialization.Json" Version="3.6.2" />
</ItemGroup>
<ItemGroup>
<None Remove="cocotte.db" />
<Content Include="cocotte.db">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,159 @@
// <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("20230326083141_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.4");
modelBuilder.Entity("Cocotte.Modules.Activities.Models.Activity", b =>
{
b.Property<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<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<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.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("Cocotte.Modules.Activities.Models.ActivityPlayer", b =>
{
b.HasOne("Cocotte.Modules.Activities.Models.Activity", "Activity")
.WithMany("ActivityPlayers")
.HasForeignKey("GuildId", "ChannelId", "MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Activity");
});
modelBuilder.Entity("Cocotte.Modules.Activities.Models.Activity", b =>
{
b.Navigation("ActivityPlayers");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Cocotte.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Activities",
columns: table => new
{
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
MessageId = table.Column<ulong>(type: "INTEGER", nullable: false),
ThreadId = table.Column<ulong>(type: "INTEGER", nullable: false),
CreatorUserId = table.Column<ulong>(type: "INTEGER", nullable: false),
CreatorDisplayName = table.Column<string>(type: "TEXT", nullable: false),
DueDateTime = table.Column<DateTime>(type: "TEXT", nullable: true),
Description = table.Column<string>(type: "TEXT", nullable: true),
Type = table.Column<int>(type: "INTEGER", nullable: false),
Name = table.Column<int>(type: "INTEGER", nullable: false),
AreRolesEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
MaxPlayers = table.Column<uint>(type: "INTEGER", nullable: false),
CreationDate = table.Column<DateTime>(type: "TEXT", nullable: false),
Discriminator = table.Column<string>(type: "TEXT", nullable: false),
Color = table.Column<int>(type: "INTEGER", nullable: true),
Stage = table.Column<uint>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Activities", x => new { x.GuildId, x.ChannelId, x.MessageId });
});
migrationBuilder.CreateTable(
name: "ActivityPlayers",
columns: table => new
{
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
MessageId = table.Column<ulong>(type: "INTEGER", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Discriminator = table.Column<string>(type: "TEXT", nullable: false),
Roles = table.Column<byte>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ActivityPlayers", x => new { x.GuildId, x.ChannelId, x.MessageId, x.UserId });
table.ForeignKey(
name: "FK_ActivityPlayers_Activities_GuildId_ChannelId_MessageId",
columns: x => new { x.GuildId, x.ChannelId, x.MessageId },
principalTable: "Activities",
principalColumns: new[] { "GuildId", "ChannelId", "MessageId" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Activities_ThreadId",
table: "Activities",
column: "ThreadId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ActivityPlayers");
migrationBuilder.DropTable(
name: "Activities");
}
}
}

View File

@@ -0,0 +1,673 @@
// <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("20230326095220_AddQuartz")]
partial class AddQuartz
{
/// <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<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<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.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,331 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Cocotte.Migrations
{
/// <inheritdoc />
public partial class AddQuartz : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "QRTZ_CALENDARS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
CALENDAR_NAME = table.Column<string>(type: "text", nullable: false),
CALENDAR = table.Column<byte[]>(type: "bytea", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_CALENDARS", x => new { x.SCHED_NAME, x.CALENDAR_NAME });
});
migrationBuilder.CreateTable(
name: "QRTZ_FIRED_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
ENTRY_ID = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
INSTANCE_NAME = table.Column<string>(type: "text", nullable: false),
FIRED_TIME = table.Column<long>(type: "bigint", nullable: false),
SCHED_TIME = table.Column<long>(type: "bigint", nullable: false),
PRIORITY = table.Column<int>(type: "integer", nullable: false),
STATE = table.Column<string>(type: "text", nullable: false),
JOB_NAME = table.Column<string>(type: "text", nullable: true),
JOB_GROUP = table.Column<string>(type: "text", nullable: true),
IS_NONCONCURRENT = table.Column<bool>(type: "bool", nullable: false),
REQUESTS_RECOVERY = table.Column<bool>(type: "bool", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_FIRED_TRIGGERS", x => new { x.SCHED_NAME, x.ENTRY_ID });
});
migrationBuilder.CreateTable(
name: "QRTZ_JOB_DETAILS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
JOB_NAME = table.Column<string>(type: "text", nullable: false),
JOB_GROUP = table.Column<string>(type: "text", nullable: false),
DESCRIPTION = table.Column<string>(type: "text", nullable: true),
JOB_CLASS_NAME = table.Column<string>(type: "text", nullable: false),
IS_DURABLE = table.Column<bool>(type: "bool", nullable: false),
IS_NONCONCURRENT = table.Column<bool>(type: "bool", nullable: false),
IS_UPDATE_DATA = table.Column<bool>(type: "bool", nullable: false),
REQUESTS_RECOVERY = table.Column<bool>(type: "bool", nullable: false),
JOB_DATA = table.Column<byte[]>(type: "bytea", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_JOB_DETAILS", x => new { x.SCHED_NAME, x.JOB_NAME, x.JOB_GROUP });
});
migrationBuilder.CreateTable(
name: "QRTZ_LOCKS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
LOCK_NAME = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_LOCKS", x => new { x.SCHED_NAME, x.LOCK_NAME });
});
migrationBuilder.CreateTable(
name: "QRTZ_PAUSED_TRIGGER_GRPS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_PAUSED_TRIGGER_GRPS", x => new { x.SCHED_NAME, x.TRIGGER_GROUP });
});
migrationBuilder.CreateTable(
name: "QRTZ_SCHEDULER_STATE",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
INSTANCE_NAME = table.Column<string>(type: "text", nullable: false),
LAST_CHECKIN_TIME = table.Column<long>(type: "bigint", nullable: false),
CHECKIN_INTERVAL = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_SCHEDULER_STATE", x => new { x.SCHED_NAME, x.INSTANCE_NAME });
});
migrationBuilder.CreateTable(
name: "QRTZ_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
JOB_NAME = table.Column<string>(type: "text", nullable: false),
JOB_GROUP = table.Column<string>(type: "text", nullable: false),
DESCRIPTION = table.Column<string>(type: "text", nullable: true),
NEXT_FIRE_TIME = table.Column<long>(type: "bigint", nullable: true),
PREV_FIRE_TIME = table.Column<long>(type: "bigint", nullable: true),
PRIORITY = table.Column<int>(type: "integer", nullable: true),
TRIGGER_STATE = table.Column<string>(type: "text", nullable: false),
TRIGGER_TYPE = table.Column<string>(type: "text", nullable: false),
START_TIME = table.Column<long>(type: "bigint", nullable: false),
END_TIME = table.Column<long>(type: "bigint", nullable: true),
CALENDAR_NAME = table.Column<string>(type: "text", nullable: true),
MISFIRE_INSTR = table.Column<short>(type: "smallint", nullable: true),
JOB_DATA = table.Column<byte[]>(type: "bytea", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_TRIGGERS", x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP });
table.ForeignKey(
name: "FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS_SCHED_NAME_JOB_NAME_JOB_GROUP",
columns: x => new { x.SCHED_NAME, x.JOB_NAME, x.JOB_GROUP },
principalTable: "QRTZ_JOB_DETAILS",
principalColumns: new[] { "SCHED_NAME", "JOB_NAME", "JOB_GROUP" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "QRTZ_BLOB_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
BLOB_DATA = table.Column<byte[]>(type: "bytea", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_BLOB_TRIGGERS", x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP });
table.ForeignKey(
name: "FK_QRTZ_BLOB_TRIGGERS_QRTZ_TRIGGERS_SCHED_NAME_TRIGGER_NAME_TRIGGER_GROUP",
columns: x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP },
principalTable: "QRTZ_TRIGGERS",
principalColumns: new[] { "SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "QRTZ_CRON_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
CRON_EXPRESSION = table.Column<string>(type: "text", nullable: false),
TIME_ZONE_ID = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_CRON_TRIGGERS", x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP });
table.ForeignKey(
name: "FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS_SCHED_NAME_TRIGGER_NAME_TRIGGER_GROUP",
columns: x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP },
principalTable: "QRTZ_TRIGGERS",
principalColumns: new[] { "SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "QRTZ_SIMPLE_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
REPEAT_COUNT = table.Column<long>(type: "bigint", nullable: false),
REPEAT_INTERVAL = table.Column<long>(type: "bigint", nullable: false),
TIMES_TRIGGERED = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_SIMPLE_TRIGGERS", x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP });
table.ForeignKey(
name: "FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS_SCHED_NAME_TRIGGER_NAME_TRIGGER_GROUP",
columns: x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP },
principalTable: "QRTZ_TRIGGERS",
principalColumns: new[] { "SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "QRTZ_SIMPROP_TRIGGERS",
columns: table => new
{
SCHED_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_NAME = table.Column<string>(type: "text", nullable: false),
TRIGGER_GROUP = table.Column<string>(type: "text", nullable: false),
STR_PROP_1 = table.Column<string>(type: "text", nullable: true),
STR_PROP_2 = table.Column<string>(type: "text", nullable: true),
STR_PROP_3 = table.Column<string>(type: "text", nullable: true),
INT_PROP_1 = table.Column<int>(type: "integer", nullable: true),
INT_PROP_2 = table.Column<int>(type: "integer", nullable: true),
LONG_PROP_1 = table.Column<long>(type: "bigint", nullable: true),
LONG_PROP_2 = table.Column<long>(type: "bigint", nullable: true),
DEC_PROP_1 = table.Column<decimal>(type: "numeric", nullable: true),
DEC_PROP_2 = table.Column<decimal>(type: "numeric", nullable: true),
BOOL_PROP_1 = table.Column<bool>(type: "bool", nullable: true),
BOOL_PROP_2 = table.Column<bool>(type: "bool", nullable: true),
TIME_ZONE_ID = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_QRTZ_SIMPROP_TRIGGERS", x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP });
table.ForeignKey(
name: "FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS_SCHED_NAME_TRIGGER_NAME_TRIGGER_GROUP",
columns: x => new { x.SCHED_NAME, x.TRIGGER_NAME, x.TRIGGER_GROUP },
principalTable: "QRTZ_TRIGGERS",
principalColumns: new[] { "SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_JOB_GROUP",
table: "QRTZ_FIRED_TRIGGERS",
column: "JOB_GROUP");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_JOB_NAME",
table: "QRTZ_FIRED_TRIGGERS",
column: "JOB_NAME");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_JOB_REQ_RECOVERY",
table: "QRTZ_FIRED_TRIGGERS",
column: "REQUESTS_RECOVERY");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_TRIG_GROUP",
table: "QRTZ_FIRED_TRIGGERS",
column: "TRIGGER_GROUP");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_TRIG_INST_NAME",
table: "QRTZ_FIRED_TRIGGERS",
column: "INSTANCE_NAME");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_TRIG_NAME",
table: "QRTZ_FIRED_TRIGGERS",
column: "TRIGGER_NAME");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_FT_TRIG_NM_GP",
table: "QRTZ_FIRED_TRIGGERS",
columns: new[] { "SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP" });
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_J_REQ_RECOVERY",
table: "QRTZ_JOB_DETAILS",
column: "REQUESTS_RECOVERY");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_T_NEXT_FIRE_TIME",
table: "QRTZ_TRIGGERS",
column: "NEXT_FIRE_TIME");
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_T_NFT_ST",
table: "QRTZ_TRIGGERS",
columns: new[] { "NEXT_FIRE_TIME", "TRIGGER_STATE" });
migrationBuilder.CreateIndex(
name: "IDX_QRTZ_T_STATE",
table: "QRTZ_TRIGGERS",
column: "TRIGGER_STATE");
migrationBuilder.CreateIndex(
name: "IX_QRTZ_TRIGGERS_SCHED_NAME_JOB_NAME_JOB_GROUP",
table: "QRTZ_TRIGGERS",
columns: new[] { "SCHED_NAME", "JOB_NAME", "JOB_GROUP" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "QRTZ_BLOB_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_CALENDARS");
migrationBuilder.DropTable(
name: "QRTZ_CRON_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_FIRED_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_LOCKS");
migrationBuilder.DropTable(
name: "QRTZ_PAUSED_TRIGGER_GRPS");
migrationBuilder.DropTable(
name: "QRTZ_SCHEDULER_STATE");
migrationBuilder.DropTable(
name: "QRTZ_SIMPLE_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_SIMPROP_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_TRIGGERS");
migrationBuilder.DropTable(
name: "QRTZ_JOB_DETAILS");
}
}
}

View File

@@ -0,0 +1,676 @@
// <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("20230326133102_AddActivityIsClosed")]
partial class AddActivityIsClosed
{
/// <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<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.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 AddActivityIsClosed : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsClosed",
table: "Activities",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsClosed",
table: "Activities");
}
}
}

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

@@ -0,0 +1,686 @@
// <auto-generated />
using System;
using Cocotte.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Cocotte.Migrations
{
[DbContext(typeof(CocotteDbContext))]
partial class CocotteDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(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,51 @@
using Cocotte.Modules.Activities.Models;
using Discord.WebSocket;
using Quartz;
namespace Cocotte.Modules.Activities;
public class ActivityCloseJob : IJob
{
public long GuildId { private get; set; }
public long ChannelId { private get; set; }
public long MessageId { private get; set; }
private readonly ILogger<ActivityCloseJob> _logger;
private readonly ActivitiesRepository _activitiesRepository;
private readonly ActivityHelper _activityHelper;
private readonly DiscordSocketClient _discordClient;
public ActivityCloseJob(ILogger<ActivityCloseJob> logger, ActivitiesRepository activitiesRepository, ActivityHelper activityHelper, DiscordSocketClient discordClient)
{
_logger = logger;
_activitiesRepository = activitiesRepository;
_activityHelper = activityHelper;
_discordClient = discordClient;
}
public async Task Execute(IJobExecutionContext context)
{
// Get associated activity
if (await _activitiesRepository.FindActivity((ulong)GuildId, (ulong)ChannelId, (ulong)MessageId) is not { } activity)
{
_logger.LogTrace("Activity {MessageId} does not exist anymore", MessageId);
return;
}
// Close activity
activity.IsClosed = true;
await _activitiesRepository.SaveChanges();
_logger.LogInformation("Closed activity {Activity}", activity);
// Get channel
if (_discordClient.GetChannel(activity.ChannelId) is not SocketTextChannel channel)
{
return;
}
// Update embed
await _activityHelper.UpdateActivityEmbed(channel, activity, ActivityUpdateReason.Update);
}
}

View File

@@ -0,0 +1,240 @@
using System.Text;
using Cocotte.Modules.Activities.Models;
using Cocotte.Options;
using Cocotte.Utils;
using Discord;
using Microsoft.Extensions.Options;
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;
public ActivityFormatter(IOptions<ActivityOptions> options, InterstellarFormatter interstellarFormatter)
{
_interstellarFormatter = interstellarFormatter;
_options = options.Value;
}
public string FormatActivityName(ActivityName activityName)
{
return activityName switch
{
ActivityName.Abyss => "Abîme du Néant",
ActivityName.Raids => "Raids",
ActivityName.FrontierClash => "Conflit frontalier",
ActivityName.VoidRift => "Failles du néant",
ActivityName.OriginsOfWar => "Origine de la guerre",
ActivityName.JointOperation => "Opération conjointe",
ActivityName.InterstellarExploration => "Porte interstellaire",
ActivityName.BreakFromDestiny => "Échapper au destin (3v3)",
ActivityName.CriticalAbyss => "Abîme critique (8v8)",
ActivityName.Minigame => "Mini-Jeux",
ActivityName.Fishing => "Pêche",
ActivityName.MirroriaRace => "Course Mirroria",
ActivityName.Event => "Évènement",
_ => throw new ArgumentOutOfRangeException(nameof(activityName), activityName, null)
};
}
public string GetActivityBanner(ActivityName activityName)
{
return CdnUtils.GetAsset($"banner/{GetActivityCode(activityName)}.webp");
}
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.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 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)} {countTitlePart} - Étage {stagedActivity.Stage}",
InterstellarActivity interstellar =>
$"{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)} {countTitlePart}"
};
// Build description
var descriptionBuilder = new StringBuilder();
// Add time if specified
if (activity.DueDateTime is { } dueDateTime)
{
// 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
{
descriptionBuilder.AppendLine("**:clock2: Maintenant**");
}
// Add generic message or specified activity description
descriptionBuilder.AppendLine(
string.IsNullOrWhiteSpace(activity.Description)
? $"Rejoignez l'activité de {MentionUtils.MentionUser(activity.CreatorUserId)}"
: activity.Description
);
// Add thread link
descriptionBuilder.AppendLine();
descriptionBuilder.Append(
$"""
**⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯**
**[Fil associé]({ChannelUtils.GetChannelLink(activity.GuildId, activity.ThreadId)})**
**⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯**
""");
string bannerUrl = GetActivityBanner(activity.Name);
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(fields);
// Add material for interstellar exploration
if (activity is InterstellarActivity interstellarActivity)
{
builder.WithThumbnailUrl(_interstellarFormatter.GetColorIcon(interstellarActivity.Color));
}
return builder;
}
private string GetActivityCode(ActivityName activityName) => activityName switch
{
ActivityName.Abyss => "VA",
ActivityName.OriginsOfWar => "OOW",
ActivityName.Raids => "RD",
ActivityName.FrontierClash => "FCH",
ActivityName.VoidRift => "VR",
ActivityName.JointOperation => "JO",
ActivityName.InterstellarExploration => "IE",
ActivityName.BreakFromDestiny => "BR",
ActivityName.CriticalAbyss => "CA",
ActivityName.Fishing => "FI",
ActivityName.Minigame => "MG",
ActivityName.MirroriaRace => "MR",
ActivityName.Event => "EV",
_ => "NA"
};
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 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)
{
var emotesBuilder = new StringBuilder();
if (rolePlayerRoles.HasFlag(PlayerRoles.Helper))
{
emotesBuilder.Append($" {_options.HelperEmote} **|**");
}
if (rolePlayerRoles.HasFlag(PlayerRoles.Support))
{
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

@@ -0,0 +1,142 @@
using Cocotte.Modules.Activities.Models;
using Cocotte.Options;
using Cocotte.Utils;
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Options;
namespace Cocotte.Modules.Activities;
public class ActivityHelper
{
public const uint UnlimitedPlayers = uint.MaxValue;
private readonly ActivityOptions _options;
private readonly ActivitiesRepository _activitiesRepository;
private readonly ActivityFormatter _activityFormatter;
public ActivityHelper(IOptions<ActivityOptions> options, ActivitiesRepository activitiesRepository, ActivityFormatter activityFormatter)
{
_activitiesRepository = activitiesRepository;
_activityFormatter = activityFormatter;
_options = options.Value;
}
public PlayerRoles GetPlayerRoles(IEnumerable<SocketRole> userRoles)
{
var roles = PlayerRoles.None;
foreach (var socketRole in userRoles)
{
roles |= socketRole.Id switch
{
var role when role == _options.HelperRoleId => PlayerRoles.Helper,
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
};
}
return roles;
}
public static ActivityType ActivityNameToType(ActivityName activityName) => activityName switch
{
ActivityName.Abyss or
ActivityName.FrontierClash or
ActivityName.InterstellarExploration or
ActivityName.JointOperation or
ActivityName.VoidRift or
ActivityName.OriginsOfWar => ActivityType.Pve4Players,
ActivityName.Raids => ActivityType.Pve8Players,
ActivityName.CriticalAbyss => ActivityType.Pvp8Players,
ActivityName.BreakFromDestiny => ActivityType.Pvp3Players,
ActivityName.Minigame => ActivityType.Other8Players,
ActivityName.MirroriaRace => ActivityType.Other4Players,
_ => ActivityType.OtherUnlimitedPlayers
};
public static uint ActivityTypeToMaxPlayers(ActivityType activityType) => activityType switch
{
ActivityType.Pve4Players or
ActivityType.Other4Players => 4,
ActivityType.Pve8Players or
ActivityType.Pvp8Players or
ActivityType.Other8Players => 8,
ActivityType.Pvp3Players => 3,
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
var players = await _activitiesRepository.LoadActivityPlayers(activity);
await channel.ModifyMessageAsync(activity.MessageId, properties =>
{
properties.Embed = _activityFormatter.ActivityEmbed(activity, players).Build();
// Disable join button if the activity is full on join, enable it on leave if activity is not full anymore
var isActivityFull = players.Count >= activity.MaxPlayers;
properties.Components = updateReason switch
{
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
};
});
}
public ComponentBuilder ActivityComponents(ulong activityId, bool disabled = false)
{
return new ComponentBuilder()
.AddRow(new ActionRowBuilder()
.WithButton(new ButtonBuilder()
.WithLabel("Rejoindre")
.WithCustomId($"activity join:{activityId}")
.WithEmote(":white_check_mark:".ToEmote())
.WithStyle(ButtonStyle.Primary)
.WithDisabled(disabled)
)
.WithButton(new ButtonBuilder()
.WithLabel("Se désinscrire")
.WithCustomId($"activity leave:{activityId}")
.WithEmote(":x:".ToEmote())
.WithStyle(ButtonStyle.Secondary)
)
.WithButton(new ButtonBuilder()
.WithLabel("Supprimer")
.WithCustomId($"activity delete:{activityId}")
.WithEmote(":wastebasket:".ToEmote())
.WithStyle(ButtonStyle.Danger)
)
);
}
}

View File

@@ -0,0 +1,659 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Cocotte.Modules.Activities.Models;
using Cocotte.Options;
using Cocotte.Utils;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using Microsoft.Extensions.Options;
using Quartz;
using Alias = Discord.Commands.AliasAttribute;
namespace Cocotte.Modules.Activities;
/// <summary>
/// Module to ask and propose groups for different activities: Abyss, OOW, FC, ...
/// </summary>
[Group("activite", "Organise des activités")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
public partial class ActivityModule : InteractionModuleBase<SocketInteractionContext>
{
private readonly ILogger<ActivityModule> _logger;
private readonly ActivityOptions _options;
private readonly ActivityHelper _activityHelper;
private readonly ActivitiesRepository _activitiesRepository;
private readonly ActivityFormatter _activityFormatter;
private readonly ISchedulerFactory _schedulerFactory;
private SocketGuildUser User => (SocketGuildUser) Context.User;
public ActivityModule(ILogger<ActivityModule> logger, IOptions<ActivityOptions> options,
ActivityHelper activityHelper, ActivitiesRepository activitiesRepository,
ActivityFormatter activityFormatter, ISchedulerFactory schedulerFactory)
{
_logger = logger;
_activityHelper = activityHelper;
_activitiesRepository = activitiesRepository;
_activityFormatter = activityFormatter;
_schedulerFactory = schedulerFactory;
_options = options.Value;
}
#if DEBUG
[RequireOwner]
[SlashCommand("setup-info", "Display activity setup info")]
public async Task SetupInfo()
{
await RespondAsync($"""
- Helper: {MentionUtils.MentionRole(_options.HelperRoleId)} {_options.HelperEmote.ToEmote()}
- Dps: {MentionUtils.MentionRole(_options.DpsRoleId)} {_options.DpsEmote.ToEmote()}
- Tank: {MentionUtils.MentionRole(_options.TankRoleId)} {_options.TankEmote.ToEmote()}
- Healer: {MentionUtils.MentionRole(_options.SupportRoleId)} {_options.SupportEmote.ToEmote()}
""");
}
#endif
#region Activities
[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,
[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.Abyss, timeInput, description, stage: stage);
}
[SlashCommand("origine-guerre", "Créer un groupe pour l'Origine de la guerre")]
[Alias("origine", "OOW")]
public async Task ActivityOrigins(
[Summary("étage", "A quel étage vous êtes")] [MinValue(1), MaxValue(25)] uint stage,
[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.OriginsOfWar, timeInput, description, stage: stage);
}
[SlashCommand("raids", "Créer un groupe pour les Raids")]
public async Task ActivityRaids(
[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.Raids, timeInput, description);
}
[SlashCommand("conflit-frontalier", "Créer un groupe pour Conflit frontalier")]
[Alias("conflit", "FC")]
public async Task ActivityFrontierClash(
[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.FrontierClash, timeInput, description);
}
[SlashCommand("failles-neant", "Créer un groupe pour les Failles du néant")]
[Alias("failles", "rift")]
public async Task ActivityVoidRift(
[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.VoidRift, timeInput, description);
}
[SlashCommand("operations-conjointes", "Créer un groupe pour les Opérations conjointes")]
[Alias("operations", "JO")]
public async Task ActivityJointOperation(
[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.JointOperation, timeInput, description);
}
[SlashCommand("portes-interstellaires", "Créer un groupe pour les Portes interstellaires")]
[Alias("portes")]
public async Task ActivityInterstellarExploration(
[Summary("couleur", "De quel couleur de matériaux s'agît-il")] InterstellarColor color,
[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.InterstellarExploration, timeInput, description, areRolesEnabled: false,
interstellarColor: color);
}
[SlashCommand("3v3", "Créer un groupe pour le 3v3 (Échapper au destin)")]
[Alias("BR")]
public async Task ActivityBreakFromDestiny(
[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.BreakFromDestiny, timeInput, description, areRolesEnabled: false);
}
[SlashCommand("8v8", "Créer un groupe pour le 8v8 (Abîme critique)")]
[Alias("critical")]
public async Task ActivityCriticalAbyss(
[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.CriticalAbyss, timeInput, description);
}
[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.Minigame, timeInput, description, areRolesEnabled: false,
maxPlayers: maxPlayers);
}
[SlashCommand("peche", "Créer un groupe pour de la pêche")]
[Alias("fishing")]
public async Task ActivityFishing(
[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.Fishing, timeInput, description, areRolesEnabled: false);
}
[SlashCommand("course", "Créer un groupe pour les courses de Mirroria")]
[Alias("BR")]
public async Task ActivityMirroriaRace(
[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.MirroriaRace, timeInput, description, areRolesEnabled: false);
}
#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, DateTime? date = null)
{
// Check time if it's specified
var dueDate = date;
if (timeInput is not null)
{
if (!TimeOnly.TryParse(timeInput, out var parsedTime))
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(
"**Heure invalide**, essayez avec le **format** `heure:minutes`\nPar exemple: `15:30`")
.Build()
);
return;
}
dueDate = DateTimeUtils.NextDateWithTimeOfDay(parsedTime);
}
_logger.LogTrace("{User} is creating activity {Activity}", User.DisplayName, activityName);
// Activities are identified using their original message id
await RespondAsync("> ***Création de l'activité en cours...***");
var response = await GetOriginalResponseAsync();
// Create associated thread
var threadId = await CreateThread(activityName, User.DisplayName);
var activityType = ActivityHelper.ActivityNameToType(activityName);
maxPlayers ??= ActivityHelper.ActivityTypeToMaxPlayers(activityType);
Activity activity;
// 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
{
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,
Stage = (uint) stage
};
}
else if (interstellarColor is not null)
{
activity = new InterstellarActivity
{
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 = false,
MaxPlayers = (uint) maxPlayers,
Color = (InterstellarColor) interstellarColor
};
}
else
{
activity = new Activity
{
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
};
}
// Add activity to db
await _activitiesRepository.AddActivity(activity);
// Add creator to activity, make it an helper
var activityPlayer = CreateActivityPlayer(activity, User, areRolesEnabled, activity is OrganizedActivity);
activity.ActivityPlayers.Add(activityPlayer);
await _activitiesRepository.SaveChanges();
// Add components
var components = _activityHelper.ActivityComponents(activity.MessageId);
await ModifyOriginalResponseAsync(m =>
{
m.Content = "";
m.Components = components.Build();
m.Embed = _activityFormatter.ActivityEmbed(activity, Enumerable.Repeat(activityPlayer, 1).ToImmutableList())
.Build();
});
// Add job to close this activity in scheduler if due date is specified
if (dueDate is { } dueDateTime)
{
var scheduler = await _schedulerFactory.GetScheduler();
var job = JobBuilder.Create<ActivityCloseJob>()
.WithIdentity(activity.JobKey, "activity-close")
.WithDescription("Automatically close an activity after due date has passed")
.UsingJobData(nameof(ActivityCloseJob.GuildId), (long) activity.GuildId)
.UsingJobData(nameof(ActivityCloseJob.ChannelId), (long) activity.ChannelId)
.UsingJobData(nameof(ActivityCloseJob.MessageId), (long) activity.MessageId)
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity($"{activity.JobKey}-trigger", "activity-close")
.StartAt(dueDateTime)
.ForJob(job)
.Build();
await scheduler.ScheduleJob(job, trigger);
}
}
private ActivityPlayer CreateActivityPlayer(Activity activity, SocketGuildUser user, bool areRolesEnabled, bool isOrganizer = false)
{
return areRolesEnabled
? new ActivityRolePlayer
{
Activity = activity,
UserId = user.Id,
Name = user.DisplayName,
Roles = _activityHelper.GetPlayerRoles(user.Roles),
IsOrganizer = isOrganizer
}
: new ActivityPlayer
{
Activity = activity,
UserId = user.Id,
Name = user.DisplayName,
IsOrganizer = isOrganizer
};
}
[ComponentInteraction("activity join:*", ignoreGroupNames: true)]
public async Task JoinActivity(ulong messageId)
{
// Check if activity exists
if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, messageId) is not
{ } activity)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité **n'existe plus**").Build()
);
return;
}
if (!await AddUserToActivity(activity, User, self: true))
{
return;
}
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.SuccessEmbed("Vous avez bien été **inscrit** pour cette activité").Build()
);
}
[ComponentInteraction("activity leave:*", ignoreGroupNames: true)]
public async Task LeaveActivity(ulong messageId)
{
// Check if activity exists
if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, messageId) is not
{ } activity)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité **n'existe plus**").Build()
);
return;
}
if (!await RemovePlayerFromActivity(activity, User, self: true))
{
return;
}
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.SuccessEmbed("Vous avez bien été **désinscrit** pour cette activité").Build()
);
}
[ComponentInteraction("activity delete:*", ignoreGroupNames: true)]
public async Task DeleteActivity(ulong messageId)
{
// Check if activity exists
if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, messageId) is not
{ } activity)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité **n'existe plus**").Build()
);
return;
}
// Check if user has permission to delete this activity
if (User.Id != activity.CreatorUserId && Context.User.Id != (await Context.Client.GetApplicationInfoAsync()).Owner.Id)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Vous n'avez **pas la permission** d'exécuter cette action").Build()
);
return;
}
// Remove from database
_activitiesRepository.DeleteActivity(activity);
await _activitiesRepository.SaveChanges();
// Remove thread
await Context.Guild.GetThreadChannel(activity.ThreadId).DeleteAsync();
// Delete response
await Context.Channel.DeleteMessageAsync(messageId);
}
private async Task<bool> AddUserToActivity(Activity activity, SocketGuildUser user, bool self)
{
// If player is already registered
if (await _activitiesRepository.FindActivityPlayer(activity.GuildId, activity.ChannelId, activity.MessageId,
user.Id) is not
null)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(self
? "Vous êtes **déjà inscrit** à cette activité"
: $"{MentionUtils.MentionUser(user.Id)} est **déjà inscrit** à cette activité").Build()
);
return false;
}
// 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)
{
// Check if activity is closed
if (activity.IsClosed)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Cette activité est fermée").Build()
);
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()
);
return false;
}
}
_logger.LogTrace("Player {Player} joined activity {Id}", user.DisplayName, activity.MessageId);
var activityPlayer = CreateActivityPlayer(activity, user, activity.AreRolesEnabled, isOrganizer: isOrganizer);
// Add player to activity
activity.ActivityPlayers.Add(activityPlayer);
await _activitiesRepository.SaveChanges();
// Update activity embed
await UpdateActivityEmbed(activity, ActivityUpdateReason.PlayerJoin);
// Send join message to thread and add user to thread
var thread = Context.Guild.GetThreadChannel(activity.ThreadId);
await thread.AddUserAsync(user);
string embedContent = $"{MentionUtils.MentionUser(user.Id)} a été **ajouté** à l'activité";
if (self)
{
await thread.SendMessageAsync(
embed: EmbedUtils.InfoEmbed(embedContent).Build()
);
}
else
{
await RespondAsync(
embed: EmbedUtils.InfoEmbed(embedContent).Build()
);
}
return true;
}
private async Task<bool> RemovePlayerFromActivity(Activity activity, SocketGuildUser user, bool self)
{
// Check if player is in activity
if (await _activitiesRepository.FindActivityPlayer(activity.GuildId, activity.ChannelId, activity.MessageId,
user.Id) is not { } activityPlayer)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils
.ErrorEmbed(self
? "Vous n'êtes **pas inscrit** à cette activité"
: $"{MentionUtils.MentionUser(user.Id)} n'est **pas inscrit** à cette activité").Build()
);
return false;
}
_logger.LogTrace("Player {Player} left activity {Id}", user.DisplayName, activity.MessageId);
activity.ActivityPlayers.Remove(activityPlayer);
await _activitiesRepository.SaveChanges();
// Update activity embed
await UpdateActivityEmbed(activity, ActivityUpdateReason.PlayerLeave);
// Send leave message to thread and remove user from thread
var thread = Context.Guild.GetThreadChannel(activity.ThreadId);
await thread.RemoveUserAsync(user);
string embedContent = $"{MentionUtils.MentionUser(user.Id)} a été **enlevé** de l'activité";
if (self)
{
await thread.SendMessageAsync(
embed: EmbedUtils.InfoEmbed(embedContent).Build()
);
}
else
{
await RespondAsync(
embed: EmbedUtils.InfoEmbed(embedContent).Build()
);
}
return true;
}
private async Task UpdateActivityEmbed(Activity activity, ActivityUpdateReason updateReason)
{
// Get channel
if (Context.Guild.GetChannel(activity.ChannelId) is not SocketTextChannel channel)
{
return;
}
await _activityHelper.UpdateActivityEmbed(channel, activity, updateReason);
}
}

View File

@@ -0,0 +1,42 @@
using Cocotte.Modules.Activities.Models;
using Cocotte.Utils;
using Discord;
using Discord.Interactions;
namespace Cocotte.Modules.Activities;
#if DEBUG
public partial class ActivityModule
{
[MessageCommand("Add role player")]
public async Task AddRolePlayer(IMessage message)
{
if (message is IUserMessage userMessage && userMessage.Author.IsBot)
{
if (await _activitiesRepository.FindActivity(Context.Guild.Id, Context.Channel.Id, message.Id) is { } activity)
{
// Generate random player
var player = new ActivityRolePlayer
{
Activity = activity,
Name = $"Player{Random.Shared.Next(1, 100)}",
UserId = (ulong) Random.Shared.NextInt64(),
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
activity.ActivityPlayers.Add(player);
await _activitiesRepository.SaveChanges();
await UpdateActivityEmbed(activity, ActivityUpdateReason.PlayerJoin);
}
}
await RespondAsync(
embed: EmbedUtils.SuccessEmbed($"Successfully added a player").Build(),
ephemeral: true
);
}
}
#endif

View File

@@ -0,0 +1,361 @@
using System.Text;
using Cocotte.Modules.Activities.Models;
using Cocotte.Utils;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
namespace Cocotte.Modules.Activities;
public partial class ActivityModule
{
private string GetStartMessage(ActivityName activityName, string creatorName) =>
$"""
**― Bienvenue ―**
Bienvenue sur le thread lié à l'activité **{_activityFormatter.FormatActivityName(activityName)}** de **{creatorName}**
Ici, vous pouvez **discuter** de l'activité, mais aussi **gérer** cette activité à l'aide de diverses **commandes**.
**― Commandes ―**
- `/activite ajouter <joueur>` - **Ajoute un joueur** à cette activité
- `/activite enlever <joueur>` - **Enlève un joueur** de cette activité
- `/activite fermer` - **Ferme l'activité**, désactive les inscriptions
- `/activite ouvrir` - **Ouvre l'activité**, réactive les inscriptions après une fermeture
- `/activite ping` - **Ping les joueurs** inscrits à cette activité
- `/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)
{
var channel = (SocketTextChannel) Context.Channel;
var message = await GetOriginalResponseAsync();
// Create thread
var thread = await channel.CreateThreadAsync(
$"{_activityFormatter.FormatActivityName(activityName)} - {creatorName}", ThreadType.PublicThread,
ThreadArchiveDuration.OneHour,
message, true
);
// Send management message
await thread.SendMessageAsync(GetStartMessage(activityName, creatorName));
// Add activity creator
await thread.AddUserAsync((IGuildUser) Context.User);
return thread.Id;
}
[SlashCommand("ajouter", "Ajouter un joueur à cette activité")]
public async Task ThreadAddPlayer(IUser user)
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity) || activity is null)
{
return;
}
await AddUserToActivity(activity, (SocketGuildUser) user, self: false);
}
[SlashCommand("enlever", "Enlever un joueur de cette activité")]
public async Task ThreadRemovePlayer(IUser user)
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
await RemovePlayerFromActivity(activity, (SocketGuildUser) user, self: false);
}
[SlashCommand("fermer", "Fermer l'activité, désactivant les inscriptions")]
public async Task ThreadCloseActivity()
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
// Do nothing if already closed
if (activity.IsClosed)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed("Cette activité est **déjà fermée**").Build()
);
return;
}
activity.IsClosed = true;
await _activitiesRepository.SaveChanges();
// Get activity channel to update
if (Context.Guild.GetChannel(activity.ChannelId) is ITextChannel channel)
{
await _activityHelper.UpdateActivityEmbed(channel, activity, ActivityUpdateReason.Update);
}
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed("L'activité a bien été **fermée**").Build()
);
}
[SlashCommand("ouvrir", "Ouvrir l'activité, réactivant les inscriptions")]
public async Task ThreadOpenActivity()
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity, checkCreator: true) || activity is null)
{
return;
}
// Do nothing if already opened
if (!activity.IsClosed)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed("Cette activité est **déjà ouverte**").Build()
);
return;
}
activity.IsClosed = false;
await _activitiesRepository.SaveChanges();
// Get activity channel to update
if (Context.Guild.GetChannel(activity.ChannelId) is ITextChannel channel)
{
await _activityHelper.UpdateActivityEmbed(channel, activity, ActivityUpdateReason.Update);
}
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.InfoEmbed("L'activité a bien été **ouverte**").Build()
);
}
[SlashCommand("ping", "Ping les joueurs inscrits à cette activité")]
public async Task ThreadPingPlayers(string message = "**Appel de groupe**")
{
// Get activity linked to this thread
var activity = _activitiesRepository.FindActivityByThreadId(Context.Channel.Id);
if (!await CheckCommandInThread(activity) || activity is null)
{
return;
}
// Get user ids
var userIds = await _activitiesRepository.GetActivityPlayerIds(activity);
// Generate message
var pingMessageBuilder = new StringBuilder(message);
pingMessageBuilder.AppendLine("\n");
pingMessageBuilder.Append(string.Join(", ", userIds.Select(id => MentionUtils.MentionUser(id))));
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)
if (activity is null)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Vous devez être dans un **thread lié à une activité** pour utiliser cette commande").Build()
);
return false;
}
if (checkCreator && User.Id != activity.CreatorUserId)
{
await RespondAsync(
ephemeral: true,
embed: EmbedUtils.ErrorEmbed("Seul le **créateur de l'activité** a le droit dexécuter cette action").Build()
);
return false;
}
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

@@ -0,0 +1,18 @@
namespace Cocotte.Modules.Activities;
public enum ActivityName
{
Abyss,
OriginsOfWar,
Raids,
FrontierClash,
VoidRift,
JointOperation,
InterstellarExploration,
BreakFromDestiny,
CriticalAbyss,
Fishing,
Minigame,
MirroriaRace,
Event
}

View File

@@ -0,0 +1,12 @@
namespace Cocotte.Modules.Activities;
public enum ActivityType
{
Pve4Players,
Pve8Players,
Pvp8Players,
Pvp3Players,
Other8Players,
Other4Players,
OtherUnlimitedPlayers
}

View File

@@ -0,0 +1,8 @@
namespace Cocotte.Modules.Activities;
public enum ActivityUpdateReason
{
PlayerJoin,
PlayerLeave,
Update
}

View File

@@ -0,0 +1,30 @@
using Cocotte.Modules.Activities.Models;
using Cocotte.Utils;
namespace Cocotte.Modules.Activities;
public class InterstellarFormatter
{
public string FormatInterstellarColor(InterstellarColor color) => color switch
{
InterstellarColor.Black => "noire",
InterstellarColor.Blue => "bleue",
InterstellarColor.Green => "verte",
InterstellarColor.Red => "rouge",
_ => "N/A"
};
public string InterstellarColorEmote(InterstellarColor color) => color switch
{
InterstellarColor.Black => "<:gate_black:1088385899022258248>",
InterstellarColor.Blue => "<:gate_blue:1088385901324935200>",
InterstellarColor.Green => "<:gate_green:1088385902792945664>",
InterstellarColor.Red => "<:gate_red:1088385905405984859>",
_ => ""
};
public string GetColorIcon(InterstellarColor color)
{
return CdnUtils.GetAsset($"icons/tof/gate/gate_{color.ToString().ToLowerInvariant()}.webp");
}
}

View File

@@ -0,0 +1,73 @@
using Cocotte.Services;
using Microsoft.EntityFrameworkCore;
namespace Cocotte.Modules.Activities.Models;
public class ActivitiesRepository
{
private readonly CocotteDbContext _cocotteDbContext;
public ActivitiesRepository(CocotteDbContext cocotteDbContext)
{
_cocotteDbContext = cocotteDbContext;
}
public async Task<Activity?> FindActivity(ulong guildId, ulong channelId, ulong messageId)
{
return await _cocotteDbContext.Activities.FindAsync(guildId, channelId, messageId);
}
public async Task<ActivityPlayer?> FindActivityPlayer(ulong guildId, ulong channelId, ulong messageId, ulong userId)
{
return await _cocotteDbContext.ActivityPlayers.FindAsync(guildId, channelId, messageId, userId);
}
public async Task<int> ActivityPlayerCount(Activity activity) => await _cocotteDbContext.Entry(activity).Collection(a => a.ActivityPlayers).Query().CountAsync();
public async Task<IReadOnlyCollection<ActivityPlayer>> LoadActivityPlayers(Activity activity)
{
await _cocotteDbContext
.Entry(activity)
.Collection(a => a.ActivityPlayers)
.LoadAsync();
return activity.ActivityPlayers;
}
public async Task AddActivity(Activity activity)
{
await _cocotteDbContext.AddAsync(activity);
}
public async Task SaveChanges()
{
await _cocotteDbContext.SaveChangesAsync();
}
public void DeleteActivity(Activity activity)
{
_cocotteDbContext.Activities.Remove(activity);
}
public Activity? FindActivityByThreadId(ulong threadId)
{
return _cocotteDbContext.Activities.FirstOrDefault(activity => activity.ThreadId == threadId);
}
public async Task<bool> ActivityContainsUser(Activity activity, ulong userId)
{
return await _cocotteDbContext.Entry(activity)
.Collection(a => a.ActivityPlayers)
.Query()
.AnyAsync(p => p.UserId == userId);
}
public async Task<IList<ulong>> GetActivityPlayerIds(Activity activity)
{
return await _cocotteDbContext.Entry(activity)
.Collection(a => a.ActivityPlayers)
.Query()
.Select(p => p.UserId)
.ToListAsync();
}
}

View File

@@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Cocotte.Modules.Activities.Models;
[PrimaryKey(nameof(GuildId), nameof(ChannelId), nameof(MessageId))]
[Index(nameof(ThreadId))]
public class Activity
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public required ulong GuildId { get; init; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public required ulong ChannelId { get; init; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public required ulong MessageId { get; init; }
public required ulong ThreadId { get; init; }
public required ulong CreatorUserId { get; init; }
public required string CreatorDisplayName { get; init; }
public DateTime? DueDateTime { get; init; }
public string? Description { get; set; }
public required ActivityType Type { get; init; }
public required ActivityName Name { get; init; }
public required bool AreRolesEnabled { get; init; }
public required uint MaxPlayers { get; set; }
public DateTime CreationDate { get; init; } = DateTime.Now;
public bool IsClosed { get; set; }
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()
{
return $"{nameof(MessageId)}: {MessageId}, {nameof(CreatorUserId)}: {CreatorUserId}, {nameof(Description)}: {Description}, {nameof(Type)}: {Type}, {nameof(Name)}: {Name}, {nameof(MaxPlayers)}: {MaxPlayers}";
}
}

View File

@@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Cocotte.Modules.Activities.Models;
[PrimaryKey(nameof(GuildId), nameof(ChannelId), nameof(MessageId), nameof(UserId))]
public class ActivityPlayer
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
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; }
public ulong MessageId { get; init; }
public required Activity Activity { get; init; }
public override string ToString()
{
return $"{nameof(UserId)}: {UserId}, {nameof(Name)}: {Name}";
}
}

View File

@@ -0,0 +1,6 @@
namespace Cocotte.Modules.Activities.Models;
public class ActivityRolePlayer : ActivityPlayer
{
public required PlayerRoles Roles { get; init; }
}

View File

@@ -0,0 +1,11 @@
namespace Cocotte.Modules.Activities.Models;
public class InterstellarActivity : Activity
{
public required InterstellarColor Color { get; init; }
public override string ToString()
{
return $"{base.ToString()}, {nameof(InterstellarColor)}: {Color}";
}
}

View File

@@ -0,0 +1,15 @@
using Discord.Interactions;
namespace Cocotte.Modules.Activities.Models;
public enum InterstellarColor
{
[ChoiceDisplay("Verte")]
Green,
[ChoiceDisplay("Rouge")]
Red,
[ChoiceDisplay("Bleue")]
Blue,
[ChoiceDisplay("Noire")]
Black
}

View File

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

View File

@@ -0,0 +1,11 @@
namespace Cocotte.Modules.Activities.Models;
public class StagedActivity : Activity
{
public uint Stage { get; set; }
public override string ToString()
{
return $"{base.ToString()}, {nameof(Stage)}: {Stage}";
}
}

View File

@@ -0,0 +1,12 @@
namespace Cocotte.Modules.Activities;
[Flags]
public enum PlayerRoles : byte
{
None = 0b0000_0000,
Helper = 0b0000_0001,
Dps = 0b0000_0010,
Tank = 0b0000_0100,
Support = 0b0000_1000,
Organizer = 0b0001_0000
}

View File

@@ -0,0 +1,59 @@
using Cocotte.Options;
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Options;
namespace Cocotte.Modules.CompositeRoles;
public class CompositeRolesListener
{
private readonly ILogger<CompositeRolesListener> _logger;
private readonly IReadOnlyDictionary<ulong, GuildCompositeRoles[]> _compositeRoles;
public CompositeRolesListener(ILogger<CompositeRolesListener> logger,
IOptions<CompositeRolesOptions> compositeRolesOptions)
{
_logger = logger;
_compositeRoles = compositeRolesOptions.Value.CompositeRoles;
}
public async Task UserUpdated(Cacheable<SocketGuildUser, ulong> cacheable, SocketGuildUser guildUser)
{
// Fetch composite roles for this guild
if (!_compositeRoles.TryGetValue(guildUser.Guild.Id, out var guildCompositeRoles))
{
return;
}
_logger.LogTrace("Guild {Guild} has at least one composite role, checking for user {User}", guildUser.Guild.Name, guildUser.DisplayName);
// Check roles for each composite roles
var roles = guildUser.Roles;
foreach (var compositeRole in guildCompositeRoles)
{
// If the user has the target role, check if we need to remove it
if (roles.FirstOrDefault(r => r.Id == compositeRole.TargetRoleId) is { } presentTargetRole)
{
// Check that the user no associated role
if (!roles.Any(r => compositeRole.CompositeRolesIds.Contains(r.Id)))
{
await guildUser.RemoveRoleAsync(presentTargetRole);
_logger.LogInformation("CompositeRoles removed role {Role} from {User}", presentTargetRole.Name, guildUser.DisplayName);
}
}
// It the user doesn't have the target role, check if we need to add it
else
{
// Check that the user has at least one of the desired roles
if (roles.Any(r => compositeRole.CompositeRolesIds.Contains(r.Id)))
{
var missingTargetRole = guildUser.Guild.GetRole(compositeRole.TargetRoleId);
await guildUser.AddRoleAsync(missingTargetRole);
_logger.LogInformation("CompositeRoles added role {Role} from {User}", missingTargetRole.Name, guildUser.DisplayName);
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
using Cocotte.Options;
using Cocotte.Utils;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using Microsoft.Extensions.Options;
namespace Cocotte.Modules.CompositeRoles;
[Group("autoroles", "Commandes liées aux rôles composés")]
public class CompositeRolesModule : InteractionModuleBase<SocketInteractionContext>
{
private readonly ILogger<CompositeRolesModule> _logger;
private readonly CompositeRolesOptions _compositeRolesOptions;
public CompositeRolesModule(ILogger<CompositeRolesModule> logger, IOptions<CompositeRolesOptions> options)
{
_logger = logger;
_compositeRolesOptions = options.Value;
}
[RequireOwner]
[SlashCommand("fix", "Réattribuer les roles composés")]
public async Task FixRoles()
{
var count = 0;
await RespondAsync("***`Checking autoroles for guild users...`***", ephemeral: true);
// Check if there's composite roles for this guild
if (!_compositeRolesOptions.CompositeRoles.TryGetValue(Context.Guild.Id, out var compositeRoles))
{
await ModifyOriginalResponseAsync(properties =>
{
properties.Content = "";
properties.Embed = EmbedUtils.InfoEmbed("Il n'y a pas de rôle composé pour ce serveur").Build();
});
return;
}
// Check for all guild members
await Context.Guild.DownloadUsersAsync();
foreach (var guildUser in Context.Guild.Users)
{
var roles = ((SocketGuildUser)guildUser).Roles;
// Check for each target role if they have at least one of the composite roles
foreach (var compositeRole in compositeRoles)
{
// If the user has the target role, check if we need to remove it
if (roles.FirstOrDefault(r => r.Id == compositeRole.TargetRoleId) is { } presentTargetRole)
{
// Check that the user no associated role
if (!roles.Any(r => compositeRole.CompositeRolesIds.Contains(r.Id)))
{
await guildUser.RemoveRoleAsync(presentTargetRole);
_logger.LogInformation("CompositeRoles removed role {Role} from {User}", presentTargetRole.Name, guildUser.DisplayName);
count++;
}
}
// It the user doesn't have the target role, check if we need to add it
else
{
// Check that the user has at least one of the desired roles
if (roles.Any(r => compositeRole.CompositeRolesIds.Contains(r.Id)))
{
var missingTargetRole = guildUser.Guild.GetRole(compositeRole.TargetRoleId);
await guildUser.AddRoleAsync(missingTargetRole);
_logger.LogInformation("CompositeRoles added role {Role} from {User}", missingTargetRole.Name, guildUser.DisplayName);
count++;
}
}
}
}
await ModifyOriginalResponseAsync(properties =>
{
properties.Content = "";
properties.Embed = EmbedUtils.InfoEmbed($"Les rôles de **{count}** utilisateurs ont été mis à jour").Build();
});
}
}

View File

@@ -1,42 +0,0 @@
using Cocotte.Options;
using Cocotte.Utils;
using Discord;
using Discord.Interactions;
using Microsoft.Extensions.Options;
namespace Cocotte.Modules.Groups;
/// <summary>
/// Module to ask and propose groups for different activities: Abyss, OOW, FC, ...
/// </summary>
[Group("group", "Group related commands")]
public class GroupModule : InteractionModuleBase<SocketInteractionContext>
{
private readonly ILogger<GroupModule> _logger;
private readonly GroupsOptions _options;
public GroupModule(ILogger<GroupModule> logger, IOptions<GroupsOptions> options)
{
_logger = logger;
_options = options.Value;
}
[RequireOwner]
[SlashCommand("test", "Test group module")]
public async Task Test()
{
await RespondAsync("Module is active!!");
}
[RequireOwner]
[SlashCommand("setup-info", "Display group setup info")]
public async Task SetupInfo()
{
await RespondAsync($"""
- Helper: {MentionUtils.MentionRole(_options.HelperRoleId)} {_options.HelperEmote.ToEmote()}
- Dps: {MentionUtils.MentionRole(_options.DpsRoleId)} {_options.DpsEmote.ToEmote()}
- Tank: {MentionUtils.MentionRole(_options.TankRoleId)} {_options.TankEmote.ToEmote()}
- Healer: {MentionUtils.MentionRole(_options.HealerRoleId)} {_options.HealerEmote.ToEmote()}
""");
}
}

View File

@@ -19,15 +19,11 @@ namespace Cocotte.Modules.Ping;
public class PingModule : InteractionModuleBase<SocketInteractionContext>
{
private readonly ILogger<PingModule> _logger;
private readonly SharedCounter _sharedCounter;
private readonly TransientCounter _transientCounter;
private static readonly SemaphoreSlim CounterWait = new(0);
public PingModule(ILogger<PingModule> logger, SharedCounter sharedCounter, TransientCounter transientCounter)
public PingModule(ILogger<PingModule> logger)
{
_logger = logger;
_sharedCounter = sharedCounter;
_transientCounter = transientCounter;
}
[SlashCommand("runtime-info", "Get runtime info")]
@@ -167,94 +163,6 @@ public class PingModule : InteractionModuleBase<SocketInteractionContext>
await RespondAsync($"{userName} clicked on button: {buttonId}");
}
[SlashCommand("counter-shared", "Spawn a shared counter")]
public async Task CounterShared()
{
var component = new ComponentBuilder()
.WithButton("Increment", "increment_shared");
await RespondAsync($"Counter: {_sharedCounter.Count}", components: component.Build());
}
[ComponentInteraction("increment_shared")]
public async Task SharedCounterIncrement()
{
_logger.LogTrace("Received increment on shared counter");
_sharedCounter.Count++;
try
{
await (Context.Interaction as IComponentInteraction)!.UpdateAsync(msg =>
{
msg.Content = $"Counter: {_sharedCounter.Count}";
});
}
catch (Exception e)
{
_logger.LogError(e, "An error occured while updating original message:");
}
await RespondAsync();
}
[SlashCommand("counter-transient", "Spawn a transient counter")]
public async Task CounterTransient()
{
var component = new ComponentBuilder()
.WithButton("Increment", "increment_transient");
await RespondAsync($"Counter: {_transientCounter.Count}", components: component.Build());
}
[ComponentInteraction("increment_transient")]
public async Task TransientCounterIncrement()
{
_logger.LogTrace("Received increment on transient counter");
_transientCounter.Count++;
try
{
await (Context.Interaction as IComponentInteraction)!.UpdateAsync(msg =>
{
msg.Content = $"Counter: {_transientCounter.Count}";
});
}
catch (Exception e)
{
_logger.LogError(e, "An error occured while updating original message:");
}
await RespondAsync();
}
[SlashCommand("counter-transient-wait", "Spawn a transient counter using wait")]
public async Task CounterTransientWait()
{
var component = new ComponentBuilder()
.WithButton("Increment", "increment_transient_wait");
await RespondAsync($"Counter: {_transientCounter.Count}", components: component.Build());
var response = await GetOriginalResponseAsync();
while (true)
{
// Wait for the button to be clicked
_logger.LogTrace("Waiting for semaphore release");
await CounterWait.WaitAsync();
_logger.LogTrace("Received increment on transient wait counter");
_transientCounter.Count++;
await ModifyOriginalResponseAsync(m => m.Content = $"Counter: {_transientCounter.Count}");
}
}
[ComponentInteraction("increment_transient_wait")]
public async Task WaitCounterIncrement()
{
CounterWait.Release();
await RespondAsync();
}
[SlashCommand("select-test", "Test menu select")]
public async Task SelectTest()
{
@@ -324,7 +232,7 @@ public class FoodModal : IModal
[RequiredInput(false)]
[InputLabel("Why??")]
[ModalTextInput("food_reason", TextInputStyle.Paragraph, "Kuz it's tasty", maxLength: 500)]
public string Reason { get; set; }
public required string Reason { get; set; }
}

View File

@@ -0,0 +1,21 @@
namespace Cocotte.Options;
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; }
public ulong DpsRoleId { get; init; }
public required string DpsEmote { get; init; }
public ulong TankRoleId { get; init; }
public required string TankEmote { get; init; }
public ulong SupportRoleId { get; init; }
public required string SupportEmote { get; init; }
}

View File

@@ -0,0 +1,14 @@
namespace Cocotte.Options;
public class CompositeRolesOptions
{
public const string SectionName = "CompositeRolesOptions";
public required IReadOnlyDictionary<ulong, GuildCompositeRoles[]> CompositeRoles { get; init; }
}
public class GuildCompositeRoles
{
public required ulong TargetRoleId { get; init; }
public required ulong[] CompositeRolesIds { get; init; }
}

View File

@@ -1,18 +0,0 @@
namespace Cocotte.Options;
public class GroupsOptions
{
public const string SectionName = "GroupsOptions";
public ulong HelperRoleId { get; init; }
public string HelperEmote { get; init; }
public ulong DpsRoleId { get; init; }
public string DpsEmote { get; init; }
public ulong TankRoleId { get; init; }
public string TankEmote { get; init; }
public ulong HealerRoleId { get; init; }
public string HealerEmote { get; init; }
}

View File

@@ -1,3 +1,6 @@
using Cocotte.Modules.Activities;
using Cocotte.Modules.Activities.Models;
using Cocotte.Modules.CompositeRoles;
using Cocotte.Modules.Raids;
using Cocotte.Options;
using Cocotte.Services;
@@ -5,25 +8,46 @@ using Discord;
using Discord.Commands;
using Discord.Interactions;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using Quartz;
DiscordSocketConfig discordSocketConfig = new()
{
LogLevel = LogSeverity.Debug,
MessageCacheSize = 200,
GatewayIntents = GatewayIntents.None
GatewayIntents = GatewayIntents.GuildMembers | GatewayIntents.Guilds
};
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((_, configuration) =>
{
configuration.AddJsonFile("discord.json", false, false);
configuration.AddJsonFile("groups.json", false, false);
configuration.AddJsonFile("activity.json", false, false);
configuration.AddJsonFile("compositeRoles.json", false, false);
})
.ConfigureServices((context, services) =>
{
// Quartz service
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.UsePersistentStore(options =>
{
options.UseJsonSerializer();
options.UseMicrosoftSQLite("Data Source=cocotte.db");
});
});
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
// Options
services.Configure<DiscordOptions>(context.Configuration.GetSection(DiscordOptions.SectionName));
services.Configure<GroupsOptions>(context.Configuration.GetSection(GroupsOptions.SectionName));
services.Configure<ActivityOptions>(context.Configuration.GetSection(ActivityOptions.SectionName));
services.Configure<CompositeRolesOptions>(context.Configuration.GetSection(CompositeRolesOptions.SectionName));
// Database
services.AddDbContext<CocotteDbContext>(options =>
options.UseSqlite(context.Configuration.GetConnectionString("CocotteContext")));
services.AddScoped<ActivitiesRepository>();
// Discord.Net
services.AddHostedService<DiscordLoggingService>();
@@ -40,17 +64,44 @@ IHost host = Host.CreateDefaultBuilder(args)
services.AddSingleton<IPlayerInfosRepository, MemoryPlayerInfosRepository>();
services.AddSingleton<RolesOptions>();
// Groups
// Activities
services.AddTransient<ActivityFormatter>();
services.AddTransient<InterstellarFormatter>();
services.AddScoped<ActivityHelper>();
services.AddScoped<ActivityCloseJob>();
// Composite roles
services.AddSingleton<CompositeRolesListener>();
// Raids
services.AddTransient<RaidFormatter>();
services.AddSingleton<RaidRegisterManager>();
services.AddTransient<RosterAssigner>();
// Custom
services.AddSingleton<SharedCounter>();
services.AddTransient<TransientCounter>();
// services.AddTransient<RaidFormatter>();
// services.AddSingleton<RaidRegisterManager>();
// services.AddTransient<RosterAssigner>();
})
.Build();
// Recreate database if in development environment
await using(var scope = host.Services.CreateAsyncScope())
{
var hostEnvironment = scope.ServiceProvider.GetRequiredService<IHostEnvironment>();
var dbContext = scope.ServiceProvider.GetRequiredService<CocotteDbContext>();
if (hostEnvironment.IsDevelopment())
{
// await dbContext.Database.EnsureDeletedAsync();
await dbContext.Database.EnsureCreatedAsync();
}
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();
}
}
await host.RunAsync();

View File

@@ -7,6 +7,14 @@
"DOTNET_ENVIRONMENT": "Development"
},
"workingDirectory": "bin/Debug/net7.0"
},
"Cocotte-Prod": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Production"
},
"workingDirectory": "bin/Debug/net7.0"
}
}
}

View File

@@ -0,0 +1,30 @@
using AppAny.Quartz.EntityFrameworkCore.Migrations;
using AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite;
using Cocotte.Modules.Activities.Models;
using Microsoft.EntityFrameworkCore;
namespace Cocotte.Services;
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>();
public CocotteDbContext(DbContextOptions<CocotteDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Adds Quartz.NET SQLite schema to EntityFrameworkCore
modelBuilder.AddQuartz(builder => builder.UseSqlite());
}
}

View File

@@ -1,4 +1,6 @@
using System.Reflection;
using Cocotte.Modules.Activities;
using Cocotte.Modules.CompositeRoles;
using Cocotte.Options;
using Discord;
using Discord.Interactions;
@@ -15,20 +17,20 @@ public class CocotteService : BackgroundService
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly DiscordSocketClient _client;
private readonly DiscordOptions _options;
private readonly GroupsOptions _groupOptions;
private readonly ActivityOptions _activityOptions;
private readonly InteractionService _interactionService;
public CocotteService(ILogger<CocotteService> logger, IServiceProvider serviceProvider,
IHostEnvironment hostEnvironment,
IHostApplicationLifetime hostApplicationLifetime, DiscordSocketClient client,
IOptions<DiscordOptions> options, IOptions<GroupsOptions> groupOptions, InteractionService interactionService)
IOptions<DiscordOptions> options, IOptions<ActivityOptions> groupOptions, InteractionService interactionService)
{
_logger = logger;
_serviceProvider = serviceProvider;
_hostApplicationLifetime = hostApplicationLifetime;
_client = client;
_options = options.Value;
_groupOptions = groupOptions.Value;
_activityOptions = groupOptions.Value;
_interactionService = interactionService;
_hostEnvironment = hostEnvironment;
}
@@ -51,7 +53,14 @@ public class CocotteService : BackgroundService
}
// Initialize modules and commands
await _interactionService.AddModulesAsync(Assembly.GetEntryAssembly(), _serviceProvider);
using var scope = _serviceProvider.CreateScope();
#if DEBUG
await _interactionService.AddModuleAsync(typeof(Modules.Ping.PingModule), scope.ServiceProvider);
#endif
await _interactionService.AddModuleAsync(typeof(ActivityModule), scope.ServiceProvider);
await _interactionService.AddModuleAsync(typeof(CompositeRolesModule), scope.ServiceProvider);
_client.Ready += ClientOnReady;
_client.InteractionCreated += HandleInteraction;
@@ -60,16 +69,26 @@ public class CocotteService : BackgroundService
await _client.LoginAsync(TokenType.Bot, _options.Token);
await _client.StartAsync();
// Register events
RegisterEvents();
await Task.Delay(Timeout.Infinite, stoppingToken);
}
private void RegisterEvents()
{
var composteRolesListener = _serviceProvider.GetRequiredService<CompositeRolesListener>();
_client.GuildMemberUpdated += composteRolesListener.UserUpdated;
}
private bool ValidateOptions()
{
// Validate group options
if ((_groupOptions.HelperRoleId
| _groupOptions.DpsRoleId
| _groupOptions.TankRoleId
| _groupOptions.HealerRoleId) == 0)
if ((_activityOptions.HelperRoleId
| _activityOptions.DpsRoleId
| _activityOptions.TankRoleId
| _activityOptions.SupportRoleId) == 0)
{
_logger.LogError("One of the group options id is invalid, it cannot be 0");

View File

@@ -1,6 +0,0 @@
namespace Cocotte.Services;
public class SharedCounter
{
public int Count { get; set; } = 0;
}

View File

@@ -1,6 +0,0 @@
namespace Cocotte.Services;
public class TransientCounter
{
public int Count { get; set; } = 0;
}

19
Cocotte/Utils/CdnUtils.cs Normal file
View File

@@ -0,0 +1,19 @@
namespace Cocotte.Utils;
public class CdnUtils
{
/// <summary>
/// Use this to force discord media cache to fetch new content from CDN
/// </summary>
private const string QuerySuffix = $"?q={RandomSuffix}";
/// <summary>
/// Needs to be updated each time a media is updated on the CDN
/// </summary>
private const string RandomSuffix = "assets2";
public static string GetAsset(string assetName)
{
return $"https://sage.cdn.ilysix.fr/assets/Cocotte/{assetName}{QuerySuffix}";
}
}

View File

@@ -0,0 +1,16 @@
using Discord;
namespace Cocotte.Utils;
public class ChannelUtils
{
public static string GetChannelLink(IGuildChannel guildChannel)
{
return GetChannelLink(guildChannel.GuildId, guildChannel.Id);
}
public static string GetChannelLink(ulong guildId, ulong channelId)
{
return $"https://discord.com/channels/{guildId}/{channelId}";
}
}

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

@@ -6,6 +6,8 @@ public static class Colors
{
// Main Cocotte colors
public static Color CocotteBlue => new(0x3196c8);
public static Color CocotteRed => new(0xe40808);
public static Color CocotteOrange => new(0xff6d01);
// Colors used in embeds
public static Color ErrorColor => new(0xFB6060);

View File

@@ -13,4 +13,23 @@ public static class DateTimeUtils
int offset = (7 - c + d) % 7;
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(
dateTime.Year, dateTime.Month, dateTime.Day, timeOnly.Hour, timeOnly.Minute, timeOnly.Second
);
}
public static DateTime NextDateWithTimeOfDay(TimeOnly timeOfDay) =>
DateTime.Now.TimeOfDay.Ticks > timeOfDay.Ticks ? DateTime.Now.AddDays(1).WithTimeOnly(timeOfDay) : DateTime.Now.WithTimeOnly(timeOfDay);
}

View File

@@ -4,13 +4,13 @@ namespace Cocotte.Utils;
public static class EmbedUtils
{
public static EmbedBuilder ErrorEmbed(string message, string title = "Error")
public static EmbedBuilder ErrorEmbed(string message, string title = "Erreur")
{
return new EmbedBuilder()
.WithColor(Colors.ErrorColor)
.WithAuthor(a => a
.WithName(title)
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/error.webp")
.WithIconUrl(CdnUtils.GetAsset("icons/error.webp"))
)
.WithDescription(message);
}
@@ -21,29 +21,29 @@ public static class EmbedUtils
.WithColor(Colors.InfoColor)
.WithAuthor(a => a
.WithName(title)
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/info.webp")
.WithIconUrl(CdnUtils.GetAsset("icons/info.webp"))
)
.WithDescription(message);
}
public static EmbedBuilder SuccessEmbed(string message, string title = "Success")
public static EmbedBuilder SuccessEmbed(string message, string title = "Succès")
{
return new EmbedBuilder()
.WithColor(Colors.SuccessColor)
.WithAuthor(a => a
.WithName(title)
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/success.webp")
.WithIconUrl(CdnUtils.GetAsset("icons/success.webp"))
)
.WithDescription(message);
}
public static EmbedBuilder WarningEmbed(string message, string title = "Warning")
public static EmbedBuilder WarningEmbed(string message, string title = "Attention")
{
return new EmbedBuilder()
.WithColor(Colors.WarningColor)
.WithAuthor(a => a
.WithName(title)
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/warning.webp")
.WithIconUrl(CdnUtils.GetAsset("icons/warning.webp"))
)
.WithDescription(message);
}

View File

@@ -1,5 +1,8 @@
{
"GroupsOptions": {
"ActivityOptions": {
"OrganizerRoleId": 0,
"OrganizerEmote": "",
"HelperRoleId": 0,
"HelperEmote": "",
@@ -9,7 +12,7 @@
"TankRoleId": 0,
"TankEmote": "",
"HealerRoleId": 0,
"HealerEmote": ""
"SupportRoleId": 0,
"SupportEmote": ""
}
}

View File

@@ -3,7 +3,10 @@
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Cocotte": "Trace"
"Cocotte": "Trace",
"Quartz": "Information",
"Quartz.Core.QuartzSchedulerThread": "Information",
"Quartz.Core.JobRunShell": "Information"
}
}
}

View File

@@ -4,5 +4,8 @@
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"CocotteContext": "Data Source=cocotte.db"
}
}

0
Cocotte/cocotte.db Normal file
View File

View File

@@ -0,0 +1,26 @@
{
"CompositeRolesOptions": {
"CompositeRoles": {
"someGuildId": [
{
"TargetRoleId": 1,
"CompositeRolesIds": [
0,
1,
2
]
}
],
"anotherGuildId": [
{
"TargetRoleId": 45,
"CompositeRolesIds": [
98,
1,
2
]
}
]
}
}
}

0
identifier.sqlite Normal file
View File