Compare commits
52 Commits
b406de2e2c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 13d332d306 | |||
| 84cac63c9d | |||
| ad4ee02631 | |||
| 4b23dfd8a0 | |||
| 224fe4cb25 | |||
| ca3e951d9a | |||
| 37a4da26d7 | |||
| d000e6a299 | |||
| 08af0afbfb | |||
| b4e7e52827 | |||
| 7a76ca2147 | |||
| 54b4e6fb3f | |||
| 78fe2d5913 | |||
| a6e8ad0401 | |||
| a79ed18e26 | |||
| 1083f604f7 | |||
| 63e464e602 | |||
| cd6a9e87ad | |||
| 6001a3dc24 | |||
| cb0b993ceb | |||
| 14f90a2d18 | |||
| ced9d15083 | |||
| a3051ed060 | |||
| bd52e37454 | |||
| 79d9f16a8b | |||
| b6897f7327 | |||
| 8db87b9098 | |||
| ef948dba27 | |||
| 8dcefeeb39 | |||
| 3f2dc16fad | |||
| 21996a2e03 | |||
| bffa7d06bb | |||
| 094f07ea3c | |||
| d47442b984 | |||
| e3df53738f | |||
| 984f44585e | |||
| 066ac52199 | |||
| b4d41a449f | |||
| 194feb94dd | |||
| 0535655f58 | |||
| 12507b0fed | |||
| e853583371 | |||
| 24fc0103ec | |||
| 044fae0f99 | |||
| addaefdde7 | |||
| 21803eb728 | |||
| e334e02768 | |||
| 4f0ccb9464 | |||
| a6df178dff | |||
| ac9c3a405f | |||
| 5b4a15bc8f | |||
| 978b73233b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -684,5 +684,3 @@ fabric.properties
|
||||
# Additional files built by Visual Studio
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,rider,csharp,dotnetcore
|
||||
|
||||
Cocotte/discord.json
|
||||
@@ -1,14 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>dotnet-Cocotter-70E782F3-B3C1-4BA0-965D-D21E31F2F052</UserSecretsId>
|
||||
</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>
|
||||
|
||||
159
Cocotte/Migrations/20230326083141_InitialCreate.Designer.cs
generated
Normal file
159
Cocotte/Migrations/20230326083141_InitialCreate.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Cocotte/Migrations/20230326083141_InitialCreate.cs
Normal file
79
Cocotte/Migrations/20230326083141_InitialCreate.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
673
Cocotte/Migrations/20230326095220_AddQuartz.Designer.cs
generated
Normal file
673
Cocotte/Migrations/20230326095220_AddQuartz.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
331
Cocotte/Migrations/20230326095220_AddQuartz.cs
Normal file
331
Cocotte/Migrations/20230326095220_AddQuartz.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
676
Cocotte/Migrations/20230326133102_AddActivityIsClosed.Designer.cs
generated
Normal file
676
Cocotte/Migrations/20230326133102_AddActivityIsClosed.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Cocotte/Migrations/20230326133102_AddActivityIsClosed.cs
Normal file
29
Cocotte/Migrations/20230326133102_AddActivityIsClosed.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
686
Cocotte/Migrations/20230326160040_AddOrganizedActivities.Designer.cs
generated
Normal file
686
Cocotte/Migrations/20230326160040_AddOrganizedActivities.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Cocotte/Migrations/20230326160040_AddOrganizedActivities.cs
Normal file
29
Cocotte/Migrations/20230326160040_AddOrganizedActivities.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
689
Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.Designer.cs
generated
Normal file
689
Cocotte/Migrations/20230410102447_AddActivityPlayerHasCompleted.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
686
Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
Normal file
686
Cocotte/Migrations/CocotteDbContextModelSnapshot.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Cocotte/Modules/Activities/ActivityCloseJob.cs
Normal file
51
Cocotte/Modules/Activities/ActivityCloseJob.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
240
Cocotte/Modules/Activities/ActivityFormatter.cs
Normal file
240
Cocotte/Modules/Activities/ActivityFormatter.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
142
Cocotte/Modules/Activities/ActivityHelper.cs
Normal file
142
Cocotte/Modules/Activities/ActivityHelper.cs
Normal 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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
659
Cocotte/Modules/Activities/ActivityModule.cs
Normal file
659
Cocotte/Modules/Activities/ActivityModule.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
42
Cocotte/Modules/Activities/ActivityModuleDebug.cs
Normal file
42
Cocotte/Modules/Activities/ActivityModuleDebug.cs
Normal 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
|
||||
361
Cocotte/Modules/Activities/ActivityModuleThread.cs
Normal file
361
Cocotte/Modules/Activities/ActivityModuleThread.cs
Normal 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 d’exé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; }
|
||||
}
|
||||
18
Cocotte/Modules/Activities/ActivityName.cs
Normal file
18
Cocotte/Modules/Activities/ActivityName.cs
Normal 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
|
||||
}
|
||||
12
Cocotte/Modules/Activities/ActivityType.cs
Normal file
12
Cocotte/Modules/Activities/ActivityType.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Cocotte.Modules.Activities;
|
||||
|
||||
public enum ActivityType
|
||||
{
|
||||
Pve4Players,
|
||||
Pve8Players,
|
||||
Pvp8Players,
|
||||
Pvp3Players,
|
||||
Other8Players,
|
||||
Other4Players,
|
||||
OtherUnlimitedPlayers
|
||||
}
|
||||
8
Cocotte/Modules/Activities/ActivityUpdateReason.cs
Normal file
8
Cocotte/Modules/Activities/ActivityUpdateReason.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Cocotte.Modules.Activities;
|
||||
|
||||
public enum ActivityUpdateReason
|
||||
{
|
||||
PlayerJoin,
|
||||
PlayerLeave,
|
||||
Update
|
||||
}
|
||||
30
Cocotte/Modules/Activities/InterstellarFormatter.cs
Normal file
30
Cocotte/Modules/Activities/InterstellarFormatter.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
73
Cocotte/Modules/Activities/Models/ActivitiesRepository.cs
Normal file
73
Cocotte/Modules/Activities/Models/ActivitiesRepository.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
44
Cocotte/Modules/Activities/Models/Activity.cs
Normal file
44
Cocotte/Modules/Activities/Models/Activity.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
25
Cocotte/Modules/Activities/Models/ActivityPlayer.cs
Normal file
25
Cocotte/Modules/Activities/Models/ActivityPlayer.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
6
Cocotte/Modules/Activities/Models/ActivityRolePlayer.cs
Normal file
6
Cocotte/Modules/Activities/Models/ActivityRolePlayer.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Cocotte.Modules.Activities.Models;
|
||||
|
||||
public class ActivityRolePlayer : ActivityPlayer
|
||||
{
|
||||
public required PlayerRoles Roles { get; init; }
|
||||
}
|
||||
11
Cocotte/Modules/Activities/Models/InterstellarActivity.cs
Normal file
11
Cocotte/Modules/Activities/Models/InterstellarActivity.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
15
Cocotte/Modules/Activities/Models/InterstellarColor.cs
Normal file
15
Cocotte/Modules/Activities/Models/InterstellarColor.cs
Normal 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
|
||||
}
|
||||
8
Cocotte/Modules/Activities/Models/OrganizedActivity.cs
Normal file
8
Cocotte/Modules/Activities/Models/OrganizedActivity.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Cocotte.Modules.Activities.Models;
|
||||
|
||||
public class OrganizedActivity : Activity
|
||||
{
|
||||
|
||||
}
|
||||
11
Cocotte/Modules/Activities/Models/StagedActivity.cs
Normal file
11
Cocotte/Modules/Activities/Models/StagedActivity.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
12
Cocotte/Modules/Activities/PlayerRoles.cs
Normal file
12
Cocotte/Modules/Activities/PlayerRoles.cs
Normal 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
|
||||
}
|
||||
59
Cocotte/Modules/CompositeRoles/CompositeRolesListener.cs
Normal file
59
Cocotte/Modules/CompositeRoles/CompositeRolesListener.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Cocotte/Modules/CompositeRoles/CompositeRolesModule.cs
Normal file
88
Cocotte/Modules/CompositeRoles/CompositeRolesModule.cs
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
239
Cocotte/Modules/Ping/PingModule.cs
Normal file
239
Cocotte/Modules/Ping/PingModule.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
#if DEBUG
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Cocotte.Services;
|
||||
using Cocotte.Utils;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace Cocotte.Modules.Ping;
|
||||
|
||||
/// <summary>
|
||||
/// Module containing different test and debug commands
|
||||
/// </summary>
|
||||
[RequireOwner]
|
||||
[Group("ping", "Debug related commands")]
|
||||
public class PingModule : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
private readonly ILogger<PingModule> _logger;
|
||||
private static readonly SemaphoreSlim CounterWait = new(0);
|
||||
|
||||
public PingModule(ILogger<PingModule> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[SlashCommand("runtime-info", "Get runtime info")]
|
||||
public async Task RuntimeInfo()
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Cocotte runtime info")
|
||||
.WithColor(0x3196c8)
|
||||
.WithFields(
|
||||
new EmbedFieldBuilder()
|
||||
.WithName(":computer: OS")
|
||||
.WithValue(RuntimeInformation.OSDescription)
|
||||
.WithIsInline(true),
|
||||
new EmbedFieldBuilder()
|
||||
.WithName(":pencil: .Net info")
|
||||
.WithValue(RuntimeInformation.FrameworkDescription)
|
||||
.WithIsInline(true),
|
||||
new EmbedFieldBuilder()
|
||||
.WithName(":mirror_ball: Discord.Net info")
|
||||
.WithValue(Assembly.GetAssembly(typeof(Discord.Net.BucketId))!.ToString())
|
||||
.WithIsInline(false)
|
||||
);
|
||||
|
||||
await RespondAsync(embed: embed.Build());
|
||||
}
|
||||
|
||||
[SlashCommand("ping", "Check if Coco is alive and get latency")]
|
||||
public async Task Ping()
|
||||
{
|
||||
_logger.LogTrace("[Ping/ping] Received ping command");
|
||||
|
||||
await RespondAsync($":ping_pong: It took me {Context.Client.Latency}ms to respond to you!");
|
||||
}
|
||||
|
||||
[SlashCommand("echo", "Repeat the input")]
|
||||
public async Task Echo(string echo, [Summary(description: "mention the user")] bool mention = false)
|
||||
{
|
||||
_logger.LogTrace("[Ping/echo] Received ping command with arg: {{ echo:'{Echo}', mention:{Mention} }}", echo, mention);
|
||||
|
||||
await RespondAsync($"{echo} {(mention ? Context.User.Mention : string.Empty)}");
|
||||
}
|
||||
|
||||
// This command will greet target user in the channel this was executed in.
|
||||
[UserCommand("Greet")]
|
||||
public async Task GreetUserAsync(IUser user)
|
||||
{
|
||||
await RespondAsync($":wave: {Context.User.Username} said hi to you, <@{user.Id}>!");
|
||||
}
|
||||
|
||||
[SlashCommand("summon", "Summon Coco to test if she can see and write to this channel")]
|
||||
public async Task Summon()
|
||||
{
|
||||
_logger.LogTrace("{User} summoned Coco in {Channel}", Context.User.Username, Context.Channel.Name);
|
||||
|
||||
await RespondAsync(":white_check_mark: Coco at your service!");
|
||||
}
|
||||
|
||||
[SlashCommand("reply", "Reply to a message")]
|
||||
public async Task Reply(string channelId, string messageId)
|
||||
{
|
||||
var channel = Context.Client.GetChannel(ulong.Parse(channelId)) as ISocketMessageChannel;
|
||||
var message = await channel?.GetMessageAsync(ulong.Parse(messageId))!;
|
||||
|
||||
if (message is IUserMessage)
|
||||
{
|
||||
await message.Channel.SendMessageAsync("What can I do for you?",
|
||||
messageReference: new MessageReference(message.Id));
|
||||
|
||||
await RespondAsync("Reply successfully sent", ephemeral: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RespondAsync("An error occured while sending message", ephemeral: true);
|
||||
}
|
||||
}
|
||||
|
||||
// Pins a message in the channel it is in.
|
||||
[MessageCommand("Pin")]
|
||||
public async Task PinMessageAsync(IMessage message)
|
||||
{
|
||||
// make a safety cast to check if the message is ISystem- or IUserMessage
|
||||
if (message is not IUserMessage userMessage)
|
||||
await RespondAsync(":x: You cant pin system messages!");
|
||||
|
||||
// if the pins in this channel are equal to or above 50, no more messages can be pinned.
|
||||
else if ((await Context.Channel.GetPinnedMessagesAsync()).Count >= 50)
|
||||
await RespondAsync(":x: You cant pin any more messages, the max has already been reached in this channel!");
|
||||
|
||||
else
|
||||
{
|
||||
await userMessage.PinAsync();
|
||||
await RespondAsync(":white_check_mark: Successfully pinned message!");
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("emote", "Test sending an emote")]
|
||||
public async Task SendEmote(string emoteText)
|
||||
{
|
||||
if (Emote.TryParse(emoteText, out var emote))
|
||||
{
|
||||
await RespondAsync($"{emote}/{emoteText}: `{emoteText}/{emote}`");
|
||||
}
|
||||
else
|
||||
{
|
||||
await RespondAsync(embed: EmbedUtils.ErrorEmbed("Couldn't parse the emote").Build());
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("emoji", "Test sending an emoji")]
|
||||
public async Task SendEmoji(string emojiText)
|
||||
{
|
||||
if (Emoji.TryParse(emojiText, out var emoji))
|
||||
{
|
||||
await RespondAsync($"{emoji}/{emojiText}: `{emojiText}/{emoji}`");
|
||||
}
|
||||
else
|
||||
{
|
||||
await RespondAsync(embed: EmbedUtils.ErrorEmbed("Couldn't parse the emoji").Build());
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("test-button", "Test buttons components")]
|
||||
public async Task TestButton()
|
||||
{
|
||||
var component = new ComponentBuilder()
|
||||
.WithButton("Button1", $"button:1:{Context.User.Username}")
|
||||
.WithButton("Button2", $"button:2:{Context.User.Username}")
|
||||
.AddRow(new ActionRowBuilder()
|
||||
.WithButton("Button3", $"button:3:{Context.User.Username}"));
|
||||
|
||||
await RespondAsync(components: component.Build());
|
||||
}
|
||||
|
||||
[ComponentInteraction("button:*:*")]
|
||||
public async Task TestButtonClick(int buttonId, string userName)
|
||||
{
|
||||
await RespondAsync($"{userName} clicked on button: {buttonId}");
|
||||
}
|
||||
|
||||
[SlashCommand("select-test", "Test menu select")]
|
||||
public async Task SelectTest()
|
||||
{
|
||||
var menuBuilder = new SelectMenuBuilder()
|
||||
.WithPlaceholder("Select an option")
|
||||
.WithCustomId("menu-1")
|
||||
.WithMinValues(1)
|
||||
.WithMaxValues(1)
|
||||
.AddOption("Option A", "opt-a", "Option B is lying!")
|
||||
.AddOption("Option B", "opt-b", "Option A is telling the truth!");
|
||||
|
||||
var builder = new ComponentBuilder()
|
||||
.WithSelectMenu(menuBuilder);
|
||||
|
||||
await RespondAsync("Whos really lying?", components: builder.Build());
|
||||
}
|
||||
|
||||
[ComponentInteraction("menu-1")]
|
||||
public async Task TestMenuSelected(string[] selectedChoice)
|
||||
{
|
||||
await RespondAsync($"You have selected: {string.Join(", ", selectedChoice)}");
|
||||
}
|
||||
|
||||
[SlashCommand("modal-test", "Test a modal")]
|
||||
public async Task ModalTest()
|
||||
{
|
||||
await RespondWithModalAsync<FoodModal>("food_menu");
|
||||
}
|
||||
|
||||
[ModalInteraction("food_menu")]
|
||||
public async Task FoodMenuSubmit(FoodModal modal)
|
||||
{
|
||||
// Check if "Why??" field is populated
|
||||
string reason = string.IsNullOrWhiteSpace(modal.Reason)
|
||||
? "."
|
||||
: $" because {modal.Reason}";
|
||||
|
||||
// Build the message to send.
|
||||
string message = "hey @everyone, I just learned " +
|
||||
$"{Context.User.Mention}'s favorite food is " +
|
||||
$"{modal.Food}{reason}";
|
||||
|
||||
// Specify the AllowedMentions so we don't actually ping everyone.
|
||||
AllowedMentions mentions = new()
|
||||
{
|
||||
AllowedTypes = AllowedMentionTypes.Users
|
||||
};
|
||||
|
||||
// Respond to the modal.
|
||||
await RespondAsync(message, allowedMentions: mentions, ephemeral: true);
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the modal that will be sent.
|
||||
public class FoodModal : IModal
|
||||
{
|
||||
public string Title => "Fav Food";
|
||||
|
||||
// Strings with the ModalTextInput attribute will automatically become components.
|
||||
[NotNull]
|
||||
[InputLabel("What??")]
|
||||
[ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)]
|
||||
public string? Food { get; set; }
|
||||
|
||||
// Additional paremeters can be specified to further customize the input.
|
||||
// Parameters can be optional
|
||||
[RequiredInput(false)]
|
||||
[InputLabel("Why??")]
|
||||
[ModalTextInput("food_reason", TextInputStyle.Paragraph, "Kuz it's tasty", maxLength: 500)]
|
||||
public required string Reason { get; set; }
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,176 +0,0 @@
|
||||
#if DEBUG
|
||||
|
||||
using Cocotte.Services;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
|
||||
namespace Cocotte.Modules;
|
||||
|
||||
/// <summary>
|
||||
/// Module containing different test and debug commands
|
||||
/// </summary>
|
||||
[RequireOwner]
|
||||
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)
|
||||
{
|
||||
_logger = logger;
|
||||
_sharedCounter = sharedCounter;
|
||||
_transientCounter = transientCounter;
|
||||
}
|
||||
|
||||
[SlashCommand("ping", "Check if Coco is alive and get latency")]
|
||||
public async Task Ping()
|
||||
{
|
||||
_logger.LogTrace("[Ping/ping] Received ping command");
|
||||
|
||||
await RespondAsync($":ping_pong: It took me {Context.Client.Latency}ms to respond to you!");
|
||||
}
|
||||
|
||||
[SlashCommand("echo", "Repeat the input")]
|
||||
public async Task Echo(string echo, [Summary(description: "mention the user")] bool mention = false)
|
||||
{
|
||||
_logger.LogTrace("[Ping/echo] Received ping command with arg: {{ echo:'{Echo}', mention:{Mention} }}", echo, mention);
|
||||
|
||||
await RespondAsync($"{echo} {(mention ? Context.User.Mention : string.Empty)}");
|
||||
}
|
||||
|
||||
// This command will greet target user in the channel this was executed in.
|
||||
[UserCommand("Greet")]
|
||||
public async Task GreetUserAsync(IUser user)
|
||||
{
|
||||
await RespondAsync($":wave: {Context.User.Username} said hi to you, <@{user.Id}>!");
|
||||
}
|
||||
|
||||
// Pins a message in the channel it is in.
|
||||
[MessageCommand("Pin")]
|
||||
public async Task PinMessageAsync(IMessage message)
|
||||
{
|
||||
// make a safety cast to check if the message is ISystem- or IUserMessage
|
||||
if (message is not IUserMessage userMessage)
|
||||
await RespondAsync(":x: You cant pin system messages!");
|
||||
|
||||
// if the pins in this channel are equal to or above 50, no more messages can be pinned.
|
||||
else if ((await Context.Channel.GetPinnedMessagesAsync()).Count >= 50)
|
||||
await RespondAsync(":x: You cant pin any more messages, the max has already been reached in this channel!");
|
||||
|
||||
else
|
||||
{
|
||||
await userMessage.PinAsync();
|
||||
await RespondAsync(":white_check_mark: Successfully pinned message!");
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("test-button", "Test buttons components")]
|
||||
public async Task TestButton()
|
||||
{
|
||||
var component = new ComponentBuilder()
|
||||
.WithButton("Button1", $"button:1:{Context.User.Username}")
|
||||
.WithButton("Button2", $"button:2:{Context.User.Username}")
|
||||
.AddRow(new ActionRowBuilder()
|
||||
.WithButton("Button3", $"button:3:{Context.User.Username}"));
|
||||
|
||||
await RespondAsync(components: component.Build());
|
||||
}
|
||||
|
||||
[ComponentInteraction("button:*:*")]
|
||||
public async Task TestButtonClick(int buttonId, string userName)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
9
Cocotte/Modules/Raids/IPlayerInfosRepository.cs
Normal file
9
Cocotte/Modules/Raids/IPlayerInfosRepository.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public interface IPlayerInfosRepository
|
||||
{
|
||||
bool TryGetPlayerInfo(ulong playerId, [MaybeNullWhen(false)] out PlayerInfo playerInfo);
|
||||
void UpdatePlayerInfo(ulong id, Action<PlayerInfo> updater);
|
||||
}
|
||||
12
Cocotte/Modules/Raids/IRaidsRepository.cs
Normal file
12
Cocotte/Modules/Raids/IRaidsRepository.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public interface IRaidsRepository
|
||||
{
|
||||
Raid this[ulong raidId] { get; }
|
||||
|
||||
bool AddNewRaid(ulong raidId, DateTime dateTime);
|
||||
|
||||
bool TryGetRaid(ulong raidId, [MaybeNullWhen(false)] out Raid raid);
|
||||
}
|
||||
33
Cocotte/Modules/Raids/MemoryPlayerInfosRepository.cs
Normal file
33
Cocotte/Modules/Raids/MemoryPlayerInfosRepository.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class MemoryPlayerInfosRepository : IPlayerInfosRepository
|
||||
{
|
||||
private readonly IDictionary<ulong, PlayerInfo> _playerInfos;
|
||||
|
||||
public MemoryPlayerInfosRepository()
|
||||
{
|
||||
_playerInfos = new Dictionary<ulong, PlayerInfo>();
|
||||
}
|
||||
|
||||
public bool TryGetPlayerInfo(ulong playerId, [MaybeNullWhen(false)] out PlayerInfo playerInfo)
|
||||
{
|
||||
return _playerInfos.TryGetValue(playerId, out playerInfo);
|
||||
}
|
||||
|
||||
public void UpdatePlayerInfo(ulong id, Action<PlayerInfo> updater)
|
||||
{
|
||||
if (_playerInfos.TryGetValue(id, out var playerInfo))
|
||||
{
|
||||
updater(playerInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerInfo = new PlayerInfo(id, 0);
|
||||
updater(playerInfo);
|
||||
|
||||
_playerInfos[playerInfo.Id] = playerInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Cocotte/Modules/Raids/MemoryRaidRepository.cs
Normal file
25
Cocotte/Modules/Raids/MemoryRaidRepository.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class MemoryRaidRepository : IRaidsRepository
|
||||
{
|
||||
private readonly Dictionary<ulong, Raid> _raids;
|
||||
|
||||
public Raid this[ulong raidId] => _raids[raidId];
|
||||
|
||||
public MemoryRaidRepository()
|
||||
{
|
||||
_raids = new Dictionary<ulong, Raid>();
|
||||
}
|
||||
|
||||
public bool AddNewRaid(ulong raidId, DateTime dateTime)
|
||||
{
|
||||
return _raids.TryAdd(raidId, new Raid(raidId, dateTime));
|
||||
}
|
||||
|
||||
public bool TryGetRaid(ulong raidId, [MaybeNullWhen(false)] out Raid raid)
|
||||
{
|
||||
return _raids.TryGetValue(raidId, out raid);
|
||||
}
|
||||
}
|
||||
32
Cocotte/Modules/Raids/PlayerInfo.cs
Normal file
32
Cocotte/Modules/Raids/PlayerInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class PlayerInfo
|
||||
{
|
||||
public static TimeSpan FcUpdateInterval { get; } = TimeSpan.FromDays(3);
|
||||
|
||||
public ulong Id { get; }
|
||||
public PlayerRole PreferredRole { get; set; } = PlayerRole.Dps;
|
||||
|
||||
private uint _fc;
|
||||
public uint Fc
|
||||
{
|
||||
get => _fc;
|
||||
set
|
||||
{
|
||||
_fc = value;
|
||||
_lastFcUpdate = DateTime.Today;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFcUpdateRequired => DateTime.Today - _lastFcUpdate > FcUpdateInterval;
|
||||
|
||||
private DateTime _lastFcUpdate;
|
||||
|
||||
public PlayerInfo(ulong id, uint fc)
|
||||
{
|
||||
Id = id;
|
||||
Fc = fc;
|
||||
|
||||
_lastFcUpdate = DateTime.Today;
|
||||
}
|
||||
}
|
||||
8
Cocotte/Modules/Raids/PlayerRole.cs
Normal file
8
Cocotte/Modules/Raids/PlayerRole.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public enum PlayerRole
|
||||
{
|
||||
Dps,
|
||||
Tank,
|
||||
Healer
|
||||
}
|
||||
73
Cocotte/Modules/Raids/Raid.cs
Normal file
73
Cocotte/Modules/Raids/Raid.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Cocotte.Utils;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class Raid
|
||||
{
|
||||
public ulong Id { get; }
|
||||
public DateTime DateTime { get; }
|
||||
public IEnumerable<IGrouping<int, RosterPlayer>> Rosters => _players.Select(p => p.Value).GroupBy(p => p.RosterNumber);
|
||||
|
||||
private readonly IDictionary<ulong, RosterPlayer> _players = new Dictionary<ulong, RosterPlayer>();
|
||||
|
||||
public Raid(ulong id, DateTime dateTime)
|
||||
{
|
||||
Id = id;
|
||||
DateTime = dateTime;
|
||||
}
|
||||
|
||||
public bool AddPlayer(RosterPlayer rosterPlayer)
|
||||
{
|
||||
// TODO add logic to split player in multiple rosters
|
||||
rosterPlayer.RosterNumber = 1;
|
||||
|
||||
return _players.TryAdd(rosterPlayer.Id, rosterPlayer);
|
||||
}
|
||||
|
||||
public bool UpdatePlayer(RosterPlayer rosterPlayer)
|
||||
{
|
||||
if (!_players.ContainsKey(rosterPlayer.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_players[rosterPlayer.Id] = rosterPlayer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public RosterPlayer GetPlayer(ulong id)
|
||||
{
|
||||
return _players[id];
|
||||
}
|
||||
|
||||
public bool ContainsPlayer(ulong userId)
|
||||
{
|
||||
return _players.ContainsKey(userId);
|
||||
}
|
||||
|
||||
public bool RemovePlayer(ulong id)
|
||||
{
|
||||
return _players.Remove(id);
|
||||
}
|
||||
|
||||
public void AssignRosters(RosterAssigner assigner)
|
||||
{
|
||||
assigner.AssignRosters(_players.Values, 8);
|
||||
}
|
||||
|
||||
public override bool Equals(object? other)
|
||||
{
|
||||
return other is Raid roster && roster.Id == Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int) (Id % int.MaxValue);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Raid({DateTime})";
|
||||
}
|
||||
}
|
||||
60
Cocotte/Modules/Raids/RaidFormatter.cs
Normal file
60
Cocotte/Modules/Raids/RaidFormatter.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Numerics;
|
||||
using Cocotte.Options;
|
||||
using Cocotte.Utils;
|
||||
using Discord;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class RaidFormatter
|
||||
{
|
||||
private readonly RolesOptions _rolesOptions;
|
||||
|
||||
public RaidFormatter(RolesOptions rolesOptions)
|
||||
{
|
||||
_rolesOptions = rolesOptions;
|
||||
}
|
||||
|
||||
public IEmote RoleToEmote(PlayerRole role) => role switch
|
||||
{
|
||||
PlayerRole.Dps => _rolesOptions.DpsEmote.ToEmote(),
|
||||
PlayerRole.Tank => _rolesOptions.TankEmote.ToEmote(),
|
||||
PlayerRole.Healer => _rolesOptions.HealerEmote.ToEmote(),
|
||||
_ => ":question:".ToEmote()
|
||||
};
|
||||
|
||||
public static string FcFormat<T>(T fc) where T : IBinaryInteger<T> => fc switch
|
||||
{
|
||||
< 1_000 => $"{fc}",
|
||||
_ => $"{fc/T.CreateChecked(1000)}k"
|
||||
};
|
||||
|
||||
public string FormatRosterPlayer(RosterPlayer player) => player.Substitute switch
|
||||
{
|
||||
false => $"{RoleToEmote(player.Role)} {player.Name} ({FcFormat(player.Fc)} FC)",
|
||||
true => $"*{RoleToEmote(player.Role)} {player.Name} ({FcFormat(player.Fc)} FC)*"
|
||||
};
|
||||
|
||||
public EmbedBuilder RaidEmbed(Raid raid)
|
||||
{
|
||||
EmbedFieldBuilder RosterEmbed(int rosterNumber, IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
var rosterPlayers = players.OrderByDescending(p => p.Role).ThenByDescending(p => p.Fc).ToList();
|
||||
var nonSubstitute = rosterPlayers.Where(p => !p.Substitute);
|
||||
var substitute = rosterPlayers.Where(p => p.Substitute);
|
||||
|
||||
var separatorLength = players.Select(p => p.Name.Length).Max();
|
||||
separatorLength = (int) ((separatorLength + 5) * 1.31); // Don't ask why, it just works
|
||||
|
||||
return new EmbedFieldBuilder()
|
||||
.WithName($"Roster {rosterNumber} ({FcFormat(nonSubstitute.Sum(p => p.Fc))} FC)")
|
||||
.WithValue($"{string.Join("\n", nonSubstitute.Select(FormatRosterPlayer))}\n{new string('━', separatorLength)}\n{string.Join("\n", substitute.Select(FormatRosterPlayer))}")
|
||||
.WithIsInline(true);
|
||||
}
|
||||
|
||||
return new EmbedBuilder()
|
||||
.WithColor(Colors.CocotteBlue)
|
||||
.WithTitle(":crossed_swords: Raid")
|
||||
.WithDescription($"**Date:** {TimestampTag.FromDateTime(raid.DateTime, TimestampTagStyles.LongDateTime)}")
|
||||
.WithFields(raid.Rosters.OrderBy(r => r.Key).Select(r => RosterEmbed(r.Key, r)));
|
||||
}
|
||||
}
|
||||
358
Cocotte/Modules/Raids/RaidModule.cs
Normal file
358
Cocotte/Modules/Raids/RaidModule.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Cocotte.Utils;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
[Group("raid", "Raid related commands")]
|
||||
[SuppressMessage("Performance", "CA1822:Mark members as static")]
|
||||
public partial class RaidModule : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
private readonly ILogger<RaidModule> _logger;
|
||||
private readonly IRaidsRepository _raids;
|
||||
private readonly IPlayerInfosRepository _playerInfos;
|
||||
private readonly RaidFormatter _raidFormatter;
|
||||
private readonly RaidRegisterManager _registerManager;
|
||||
private readonly RosterAssigner _rosterAssigner;
|
||||
|
||||
public RaidModule(ILogger<RaidModule> logger, IRaidsRepository raids, IPlayerInfosRepository playerInfos,
|
||||
RaidFormatter raidFormatter, RaidRegisterManager registerManager, RosterAssigner rosterAssigner)
|
||||
{
|
||||
_logger = logger;
|
||||
_raids = raids;
|
||||
_playerInfos = playerInfos;
|
||||
_raidFormatter = raidFormatter;
|
||||
_registerManager = registerManager;
|
||||
_rosterAssigner = rosterAssigner;
|
||||
}
|
||||
|
||||
[EnabledInDm(false)]
|
||||
[SlashCommand("start", "Start a raid formation")]
|
||||
public async Task Start(DayOfWeek day, string time)
|
||||
{
|
||||
// Check if time is valid
|
||||
if (!TimeOnly.TryParse(time, out var timeOnly))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("Invalid time, try using format 'hh:mm'").Build()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Raids are identified using their original message id
|
||||
await RespondAsync("`Creating a new raid...`");
|
||||
|
||||
var response = await GetOriginalResponseAsync();
|
||||
var raidId = response.Id;
|
||||
|
||||
_logger.LogInformation("Created new raid with id {RaidId}", raidId);
|
||||
|
||||
// Calculate date
|
||||
var date = DateTime.Today;
|
||||
date = date.AddDays(DateTimeUtils.CalculateDayOfWeekOffset(date.DayOfWeek, day))
|
||||
.Add(timeOnly.ToTimeSpan());
|
||||
|
||||
// New raid instance
|
||||
if (!_raids.AddNewRaid(raidId, date))
|
||||
{
|
||||
// A raid with this message id already exists, how??
|
||||
_logger.LogWarning("Tried to create a new raid with already existing id: {RaidId}", raidId);
|
||||
|
||||
await FollowupAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("Can't create a new raid with same raid id").Build()
|
||||
);
|
||||
await DeleteOriginalResponseAsync();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the raid message
|
||||
var raid = _raids[raidId];
|
||||
var components = RaidComponents(raidId);
|
||||
var embed = _raidFormatter.RaidEmbed(raid);
|
||||
|
||||
await ModifyOriginalResponseAsync(m =>
|
||||
{
|
||||
m.Content = "";
|
||||
m.Components = components.Build();
|
||||
m.Embed = embed.Build();
|
||||
});
|
||||
}
|
||||
|
||||
[ComponentInteraction("raid raid_join:*", true)]
|
||||
public async Task RaidJoin(ulong raidId)
|
||||
{
|
||||
if (!_raids.TryGetRaid(raidId, out var raid))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("This raid does not exist").Build()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var user = (IGuildUser) Context.User;
|
||||
|
||||
// Check if player is already registered early
|
||||
if (raid.ContainsPlayer(user.Id))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.InfoEmbed("You're already registered for this raid").Build());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask player info
|
||||
_registerManager.RegisteringPlayers[(raidId, user.Id)] = new RosterPlayer(user.Id, user.DisplayName);
|
||||
|
||||
_logger.LogDebug("User {User} is registering for raid {Raid}", user.Username, raid);
|
||||
|
||||
// Add name
|
||||
await RespondAsync($"Please select a role for raid", components: PlayerRoleComponent(raid, user).Build(), ephemeral: true);
|
||||
}
|
||||
|
||||
[ComponentInteraction("raid player_select_role:*:*", ignoreGroupNames: true)]
|
||||
public async Task PlayerSelectRole(ulong raidId, ulong playerId, string selectedRoleInput)
|
||||
{
|
||||
var selectedRole = Enum.Parse<PlayerRole>(selectedRoleInput);
|
||||
|
||||
if (_registerManager.RegisteringPlayers.TryGetValue((raidId, playerId), out var rosterPlayer))
|
||||
{
|
||||
_registerManager.RegisteringPlayers[(raidId, playerId)] = rosterPlayer with { Role = selectedRole };
|
||||
|
||||
// Also update preferred role
|
||||
_playerInfos.UpdatePlayerInfo(playerId, p => p.PreferredRole = selectedRole);
|
||||
|
||||
await RespondAsync();
|
||||
}
|
||||
// The user is not currently registering, wonder how he got here then
|
||||
else
|
||||
{
|
||||
await RespondAsync(ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("You are not registering for this raid :thinking:").Build());
|
||||
}
|
||||
}
|
||||
|
||||
[ComponentInteraction("raid player_join:*:*", ignoreGroupNames: true)]
|
||||
public async Task PlayerJoinNonSubstitute(ulong raidId, ulong playerId)
|
||||
{
|
||||
await PlayerJoin(raidId, playerId, false);
|
||||
}
|
||||
|
||||
[ComponentInteraction("raid player_join_substitute:*:*", ignoreGroupNames: true)]
|
||||
public async Task PlayerJoinSubstitute(ulong raidId, ulong playerId)
|
||||
{
|
||||
await PlayerJoin(raidId, playerId, true);
|
||||
}
|
||||
|
||||
private async Task PlayerJoin(ulong raidId, ulong playerId, bool substitute)
|
||||
{
|
||||
// Check if player is registering
|
||||
if (!_registerManager.RegisteringPlayers.TryGetValue((raidId, playerId), out var rosterPlayer))
|
||||
{
|
||||
await RespondAsync(ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("You are not registering for this raid :thinking:").Build());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we need to ask FC
|
||||
if (!_playerInfos.TryGetPlayerInfo(playerId, out var playerInfo))
|
||||
{
|
||||
await Context.Interaction.RespondWithModalAsync<FcModal>($"raid modal_fc:{raidId}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Player already has FC registered but it's outdated
|
||||
if (playerInfo.IsFcUpdateRequired)
|
||||
{
|
||||
await Context.Interaction.RespondWithModalAsync<FcModal>($"raid modal_fc:{raidId}",
|
||||
modifyModal: m =>
|
||||
m.UpdateTextInput("fc", component => component.Value = playerInfo.Fc.FormatSpaced())
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Register user for raid
|
||||
await RegisterPlayer(raidId, rosterPlayer with { Fc = rosterPlayer.Fc, Substitute = substitute});
|
||||
}
|
||||
|
||||
[ModalInteraction("raid modal_fc:*", ignoreGroupNames: true)]
|
||||
public async Task ModalFcSubmit(ulong raidId, FcModal modal)
|
||||
{
|
||||
var playerId = Context.User.Id;
|
||||
_logger.LogTrace("Received modal FC modal from {User} with value: {Fc}", Context.User.Username, modal.Fc);
|
||||
|
||||
// Check if player is registering
|
||||
if (!_registerManager.RegisteringPlayers.TryGetValue((raidId, playerId), out var rosterPlayer))
|
||||
{
|
||||
await RespondAsync(ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("You are not registering for this raid :thinking:").Build());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var fcInput = modal.Fc.Replace(" ", "");
|
||||
|
||||
if (!uint.TryParse(fcInput, out var fc))
|
||||
{
|
||||
await RespondAsync(ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("Invalid fc, try registering again").Build());
|
||||
|
||||
_registerManager.RegisteringPlayers.Remove((raidId, playerId));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_playerInfos.UpdatePlayerInfo(playerId, p => p.Fc = fc);
|
||||
|
||||
await RegisterPlayer(raidId, rosterPlayer with { Fc = fc });
|
||||
}
|
||||
|
||||
private async Task RegisterPlayer(ulong raidId, RosterPlayer rosterPlayer)
|
||||
{
|
||||
// Check if raid exists
|
||||
if (!_raids.TryGetRaid(raidId, out var raid))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.InfoEmbed("This raid does not exist").Build());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_registerManager.RegisteringPlayers[(raidId, Context.User.Id)] = rosterPlayer;
|
||||
|
||||
// Player is already registered, update info
|
||||
if (!raid.AddPlayer(rosterPlayer))
|
||||
{
|
||||
raid.UpdatePlayer(rosterPlayer);
|
||||
|
||||
await UpdateRaidRosterEmbed(raid);
|
||||
// await RespondAsync(
|
||||
// ephemeral: true,
|
||||
// embed: EmbedUtils.SuccessEmbed($"Successfully update you're role to: {rosterPlayer.Role}").Build());
|
||||
await RespondAsync();
|
||||
}
|
||||
// It's a new player
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("User {User} joined raid {Raid}", Context.User.Username, raid);
|
||||
|
||||
await UpdateRaidRosterEmbed(raid);
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.SuccessEmbed($"Successfully joined the raid as {raid.GetPlayer(rosterPlayer.Id).Role}").Build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[ComponentInteraction("raid raid_leave:*", ignoreGroupNames: true)]
|
||||
public async Task Leave(ulong raidId)
|
||||
{
|
||||
if (!_raids.TryGetRaid(raidId, out var raid))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.ErrorEmbed("This raid does not exist").Build()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var user = (IGuildUser) Context.User;
|
||||
if (!raid.RemovePlayer(user.Id))
|
||||
{
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.WarningEmbed("You're not registered for this raid").Build()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await UpdateRaidRosterEmbed(raid);
|
||||
await RespondAsync(
|
||||
ephemeral: true,
|
||||
embed: EmbedUtils.SuccessEmbed("Successfully left the raid").Build()
|
||||
);
|
||||
}
|
||||
|
||||
public async Task UpdateRaidRosterEmbed(Raid raid)
|
||||
{
|
||||
var message = await Context.Channel.GetMessageAsync(raid.Id);
|
||||
|
||||
if (message is SocketUserMessage userMessage)
|
||||
{
|
||||
raid.AssignRosters(_rosterAssigner);
|
||||
|
||||
await userMessage.ModifyAsync(
|
||||
m => m.Embed = _raidFormatter.RaidEmbed(raid).Build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static ComponentBuilder RaidComponents(ulong raidId)
|
||||
{
|
||||
return new ComponentBuilder()
|
||||
.AddRow(new ActionRowBuilder()
|
||||
.WithButton(new ButtonBuilder()
|
||||
.WithLabel("Join")
|
||||
.WithCustomId($"raid raid_join:{raidId}")
|
||||
.WithStyle(ButtonStyle.Primary)
|
||||
)
|
||||
.WithButton(new ButtonBuilder()
|
||||
.WithLabel("Leave")
|
||||
.WithCustomId($"raid raid_leave:{raidId}")
|
||||
.WithStyle(ButtonStyle.Danger)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private ComponentBuilder PlayerRoleComponent(Raid raid, IGuildUser user)
|
||||
{
|
||||
var select = new SelectMenuBuilder()
|
||||
.WithCustomId($"raid player_select_role:{raid.Id}:{user.Id}")
|
||||
.WithMinValues(1)
|
||||
.WithMaxValues(1);
|
||||
|
||||
// Preselect preferred role
|
||||
var preferredRole = _playerInfos.TryGetPlayerInfo(user.Id, out var playerInfo) ? playerInfo.PreferredRole : PlayerRole.Dps;
|
||||
|
||||
foreach (var role in Enum.GetValues<PlayerRole>())
|
||||
{
|
||||
var roleText = role.ToString();
|
||||
select.AddOption(roleText, roleText, emote: _raidFormatter.RoleToEmote(role), isDefault: role == preferredRole);
|
||||
}
|
||||
|
||||
return new ComponentBuilder()
|
||||
.AddRow(new ActionRowBuilder()
|
||||
.WithSelectMenu(select)
|
||||
)
|
||||
.AddRow(new ActionRowBuilder()
|
||||
.WithButton("Join", $"raid player_join:{raid.Id}:{user.Id}")
|
||||
.WithButton("Join substitute", $"raid player_join_substitute:{raid.Id}:{user.Id}")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public class FcModal : IModal
|
||||
{
|
||||
public string Title => "Please enter your FC";
|
||||
|
||||
[NotNull]
|
||||
[InputLabel("FC")]
|
||||
[ModalTextInput("fc", placeholder: "30 000", minLength: 1, maxLength: 7)]
|
||||
public string? Fc { get; set; }
|
||||
}
|
||||
115
Cocotte/Modules/Raids/RaidModuleDebug.cs
Normal file
115
Cocotte/Modules/Raids/RaidModuleDebug.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Cocotte.Utils;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
#if DEBUG
|
||||
public partial class RaidModule
|
||||
{
|
||||
|
||||
[MessageCommand("Add dps")]
|
||||
public async Task AddDps(IMessage message)
|
||||
{
|
||||
await AddTestPlayer(message, PlayerRole.Dps);
|
||||
}
|
||||
|
||||
[MessageCommand("Add tank")]
|
||||
public async Task AddTank(IMessage message)
|
||||
{
|
||||
await AddTestPlayer(message, PlayerRole.Tank);
|
||||
}
|
||||
|
||||
[MessageCommand("Add healer")]
|
||||
public async Task AddHealer(IMessage message)
|
||||
{
|
||||
await AddTestPlayer(message, PlayerRole.Healer);
|
||||
}
|
||||
|
||||
[MessageCommand("Fill roster")]
|
||||
public async Task FillRoster(IMessage message)
|
||||
{
|
||||
if (message is IUserMessage userMessage && userMessage.Author.IsBot)
|
||||
{
|
||||
if (_raids.TryGetRaid(userMessage.Id, out var raid))
|
||||
{
|
||||
// Add 3 healers
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
raid.AddPlayer(new RosterPlayer(
|
||||
(ulong) Random.Shared.NextInt64(),
|
||||
$"Healer{Random.Shared.Next(1, 100)}",
|
||||
PlayerRole.Healer,
|
||||
(uint) (1000 * Random.Shared.Next(30, 60)))
|
||||
);
|
||||
}
|
||||
|
||||
// Add 3 tanks
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
raid.AddPlayer(new RosterPlayer(
|
||||
(ulong) Random.Shared.NextInt64(),
|
||||
$"Tank{Random.Shared.Next(1, 100)}",
|
||||
PlayerRole.Tank,
|
||||
(uint) (1000 * Random.Shared.Next(30, 60)))
|
||||
);
|
||||
}
|
||||
|
||||
// Add 8 dps
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
raid.AddPlayer(new RosterPlayer(
|
||||
(ulong) Random.Shared.NextInt64(),
|
||||
$"Dps{Random.Shared.Next(1, 100)}",
|
||||
PlayerRole.Dps,
|
||||
(uint) (1000 * Random.Shared.Next(30, 60)))
|
||||
);
|
||||
}
|
||||
|
||||
// Fill rest with substitutes
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
raid.AddPlayer(new RosterPlayer(
|
||||
(ulong) Random.Shared.NextInt64(),
|
||||
$"Dps{Random.Shared.Next(1, 100)}",
|
||||
PlayerRole.Dps,
|
||||
(uint) (1000 * Random.Shared.Next(30, 60)),
|
||||
true)
|
||||
);
|
||||
}
|
||||
|
||||
await UpdateRaidRosterEmbed(raid);
|
||||
}
|
||||
}
|
||||
|
||||
await RespondAsync(
|
||||
embed: EmbedUtils.SuccessEmbed("Successfully filled the roster").Build(),
|
||||
ephemeral: true
|
||||
);
|
||||
}
|
||||
|
||||
private async Task AddTestPlayer(IMessage message, PlayerRole playerRole)
|
||||
{
|
||||
if (message is IUserMessage userMessage && userMessage.Author.IsBot)
|
||||
{
|
||||
if (_raids.TryGetRaid(userMessage.Id, out var raid))
|
||||
{
|
||||
raid.AddPlayer(new RosterPlayer(
|
||||
(ulong) Random.Shared.NextInt64(),
|
||||
$"Player{Random.Shared.Next(1, 100)}",
|
||||
playerRole,
|
||||
(uint) (1000 * Random.Shared.Next(30, 60)),
|
||||
Random.Shared.Next(0, 2) == 0)
|
||||
);
|
||||
|
||||
await UpdateRaidRosterEmbed(raid);
|
||||
}
|
||||
}
|
||||
|
||||
await RespondAsync(
|
||||
embed: EmbedUtils.SuccessEmbed($"Successfully added a {playerRole} player").Build(),
|
||||
ephemeral: true
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
7
Cocotte/Modules/Raids/RaidRegisterManager.cs
Normal file
7
Cocotte/Modules/Raids/RaidRegisterManager.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class RaidRegisterManager
|
||||
{
|
||||
public readonly IDictionary<(ulong raidId, ulong playerId), RosterPlayer> RegisteringPlayers =
|
||||
new Dictionary<(ulong raidId, ulong playerId), RosterPlayer>();
|
||||
}
|
||||
170
Cocotte/Modules/Raids/RosterAssigner.cs
Normal file
170
Cocotte/Modules/Raids/RosterAssigner.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using Cocotte.Utils;
|
||||
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public class RosterAssigner
|
||||
{
|
||||
public void AssignRosters(IEnumerable<RosterPlayer> players, uint playersPerRoster)
|
||||
{
|
||||
// Start by grouping players
|
||||
var groups = GroupPlayers(players.OrderByDescending(p => p.Fc));
|
||||
|
||||
// Create rosters
|
||||
var neededRosters = (int)Math.Ceiling(players.Count(p => !p.Substitute) / (double)playersPerRoster);
|
||||
var rosters = Enumerable.Range(0, neededRosters).Select(_ => new RosterInfo(playersPerRoster)).ToList();
|
||||
|
||||
// Todo Check when there's more than max players per roster
|
||||
|
||||
// First pass: assign healers and tanks
|
||||
// Always assign to the group which have the least amount of healer/tank, biased towards healers
|
||||
// Skip groups without players
|
||||
var dpsGroup = new List<PlayerGroup>();
|
||||
foreach (var group in groups.Where(g => !g.AllSubstitutes))
|
||||
{
|
||||
if (group.Players.AnyHealer())
|
||||
{
|
||||
var nextHealerRoster = rosters.NonFull(group.Players.Count()).MinBy(r => r.RealHealerCount(), (x, y) => x.RealHealerFc() > y.RealHealerFc() ? y : x);
|
||||
|
||||
nextHealerRoster.AddGroup(group);
|
||||
}
|
||||
else if (group.Players.AnyTank())
|
||||
{
|
||||
var nextTankRoster = rosters.NonFull(group.Players.Count()).MinBy(r => r.RealTankCount(), (x, y) => x.RealTankFc() < y.RealTankFc() ? x : y);
|
||||
|
||||
nextTankRoster.AddGroup(group);
|
||||
}
|
||||
// Those groups will be used to assign dps, they should still be in descending order of FC
|
||||
else
|
||||
{
|
||||
dpsGroup.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: assign dps
|
||||
foreach (var group in dpsGroup)
|
||||
{
|
||||
var nextDpsRoster = rosters.NonFull(group.Players.Count()).MinBy(r => r.TotalRealFc);
|
||||
|
||||
nextDpsRoster!.AddGroup(group);
|
||||
}
|
||||
|
||||
// Last pass: do the same but with substitutes
|
||||
dpsGroup = new List<PlayerGroup>();
|
||||
foreach (var group in groups.Where(g => g.AllSubstitutes))
|
||||
{
|
||||
if (group.Substitutes.AnyHealer())
|
||||
{
|
||||
var nextHealerRoster = rosters.MinBy(r => r.TotalHealerCount(), (x, y) => x.TotalFc > y.TotalFc ? y : x);
|
||||
|
||||
nextHealerRoster.AddGroup(group);
|
||||
}
|
||||
else if (group.Substitutes.AnyTank())
|
||||
{
|
||||
var nextTankRoster = rosters.MinBy(r => r.TotalTankCount(), (x, y) => x.TotalFc < y.TotalFc ? x : y);
|
||||
|
||||
nextTankRoster.AddGroup(group);
|
||||
}
|
||||
// Those groups will be used to assign dps, they should still be in descending order of FC
|
||||
else
|
||||
{
|
||||
dpsGroup.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: assign dps
|
||||
foreach (var group in dpsGroup)
|
||||
{
|
||||
var nextDpsRoster = rosters.MinBy(r => r.TotalFc);
|
||||
|
||||
nextDpsRoster!.AddGroup(group);
|
||||
}
|
||||
|
||||
// Assign rosters
|
||||
for (int i = 0; i < rosters.Count; i++)
|
||||
{
|
||||
var roster = rosters[i];
|
||||
|
||||
roster.AssignRosterNumer(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private IList<PlayerGroup> GroupPlayers(IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
var groups = new List<PlayerGroup>();
|
||||
|
||||
// Todo create groups from player preferences
|
||||
foreach (var rosterPlayer in players)
|
||||
{
|
||||
groups.Add(new PlayerGroup(rosterPlayer));
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
|
||||
public class RosterInfo
|
||||
{
|
||||
public long TotalRealFc => _groups.Sum(g => g.RealFc);
|
||||
public long TotalFc => _groups.Sum(g => g.TotalFc);
|
||||
|
||||
public IEnumerable<PlayerGroup> PlayerGroups => _groups.Where(g => !g.AllSubstitutes);
|
||||
public IEnumerable<PlayerGroup> SubstituteGroups => _groups.Where(g => g.AllSubstitutes);
|
||||
|
||||
public uint MaxPlayerCount { get; }
|
||||
|
||||
private readonly IList<PlayerGroup> _groups;
|
||||
|
||||
public RosterInfo(uint maxPlayerCount)
|
||||
{
|
||||
MaxPlayerCount = maxPlayerCount;
|
||||
_groups = new List<PlayerGroup>();
|
||||
}
|
||||
|
||||
public void AddGroup(PlayerGroup group)
|
||||
{
|
||||
_groups.Add(group);
|
||||
}
|
||||
|
||||
public void AssignRosterNumer(int rosterNumber)
|
||||
{
|
||||
foreach (var group in _groups)
|
||||
{
|
||||
group.AssignRosterNumber(rosterNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerGroup
|
||||
{
|
||||
public long RealFc => _players.Where(p => !p.Substitute).TotalFc();
|
||||
public long TotalFc => _players.TotalFc();
|
||||
public bool AllSubstitutes => _players.All(p => p.Substitute);
|
||||
|
||||
public IEnumerable<RosterPlayer> Players => _players.Where(p => !p.Substitute);
|
||||
public IEnumerable<RosterPlayer> Substitutes => _players.Where(p => p.Substitute);
|
||||
|
||||
private readonly IList<RosterPlayer> _players;
|
||||
|
||||
public PlayerGroup()
|
||||
{
|
||||
_players = new List<RosterPlayer>();
|
||||
}
|
||||
|
||||
public PlayerGroup(params RosterPlayer[] players)
|
||||
{
|
||||
_players = players;
|
||||
}
|
||||
|
||||
public void AddPlayer(RosterPlayer player)
|
||||
{
|
||||
_players.Add(player);
|
||||
}
|
||||
|
||||
public void AssignRosterNumber(int rosterNumber)
|
||||
{
|
||||
foreach (var rosterPlayer in _players)
|
||||
{
|
||||
rosterPlayer.RosterNumber = rosterNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Cocotte/Modules/Raids/RosterExtensions.cs
Normal file
75
Cocotte/Modules/Raids/RosterExtensions.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public static class RosterExtensions
|
||||
{
|
||||
public static bool AnyHealer(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Any(p => p.Role == PlayerRole.Healer);
|
||||
}
|
||||
|
||||
public static bool AnyTank(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Any(p => p.Role == PlayerRole.Tank);
|
||||
}
|
||||
|
||||
public static int HealerCount(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Count(p => p.Role == PlayerRole.Healer);
|
||||
}
|
||||
|
||||
public static int TankCount(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Count(p => p.Role == PlayerRole.Tank);
|
||||
}
|
||||
|
||||
public static long TotalFc(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Sum(p => p.Fc);
|
||||
}
|
||||
|
||||
public static long HealerFc(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Where(p => p.Role == PlayerRole.Healer).Sum(p => p.Fc);
|
||||
}
|
||||
|
||||
public static long TankFc(this IEnumerable<RosterPlayer> players)
|
||||
{
|
||||
return players.Where(p => p.Role == PlayerRole.Tank).Sum(p => p.Fc);
|
||||
}
|
||||
|
||||
public static long RealHealerFc(this RosterInfo roster)
|
||||
{
|
||||
return roster.PlayerGroups.Sum(group => group.Players.HealerFc());
|
||||
}
|
||||
|
||||
public static long RealTankFc(this RosterInfo roster)
|
||||
{
|
||||
return roster.PlayerGroups.Sum(group => group.Players.TankFc());
|
||||
}
|
||||
|
||||
public static int RealHealerCount(this RosterInfo rosterInfo)
|
||||
{
|
||||
return rosterInfo.PlayerGroups.Sum(g => g.Players.HealerCount());
|
||||
}
|
||||
|
||||
public static int TotalHealerCount(this RosterInfo rosterInfo)
|
||||
{
|
||||
return rosterInfo.PlayerGroups.Concat(rosterInfo.SubstituteGroups).Sum(g => g.Players.Concat(g.Substitutes).HealerCount());
|
||||
}
|
||||
|
||||
public static int RealTankCount(this RosterInfo rosterInfo)
|
||||
{
|
||||
return rosterInfo.PlayerGroups.Sum(g => g.Players.TankCount());
|
||||
}
|
||||
|
||||
public static int TotalTankCount(this RosterInfo rosterInfo)
|
||||
{
|
||||
return rosterInfo.PlayerGroups.Concat(rosterInfo.SubstituteGroups).Sum(g => g.Players.Concat(g.Substitutes).TankCount());
|
||||
}
|
||||
|
||||
public static int TotalPlayerCount(this RosterInfo rosterInfo) =>
|
||||
rosterInfo.PlayerGroups.Sum(group => group.Players.Count());
|
||||
|
||||
public static IEnumerable<RosterInfo> NonFull(this IEnumerable<RosterInfo> rosters, int addedPlayersCount) =>
|
||||
rosters.Where(roster => roster.TotalPlayerCount() + addedPlayersCount <= roster.MaxPlayerCount);
|
||||
}
|
||||
16
Cocotte/Modules/Raids/RosterPlayer.cs
Normal file
16
Cocotte/Modules/Raids/RosterPlayer.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Cocotte.Modules.Raids;
|
||||
|
||||
public record RosterPlayer(ulong Id, string Name, PlayerRole Role = PlayerRole.Dps, uint Fc = 0, bool Substitute = false)
|
||||
{
|
||||
public int RosterNumber { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int) (Id % int.MaxValue);
|
||||
}
|
||||
|
||||
public virtual bool Equals(RosterPlayer? other)
|
||||
{
|
||||
return other is not null && other.Id == Id;
|
||||
}
|
||||
}
|
||||
21
Cocotte/Options/ActivityOptions.cs
Normal file
21
Cocotte/Options/ActivityOptions.cs
Normal 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; }
|
||||
}
|
||||
14
Cocotte/Options/CompositeRolesOptions.cs
Normal file
14
Cocotte/Options/CompositeRolesOptions.cs
Normal 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; }
|
||||
}
|
||||
8
Cocotte/Options/RolesOptions.cs
Normal file
8
Cocotte/Options/RolesOptions.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Cocotte.Options;
|
||||
|
||||
public class RolesOptions
|
||||
{
|
||||
public string DpsEmote { get; set; } = ":red_circle:";
|
||||
public string TankEmote { get; set; } = ":yellow_circle:";
|
||||
public string HealerEmote { get; set; } = ":green_circle:";
|
||||
}
|
||||
@@ -1,26 +1,53 @@
|
||||
using Cocotte;
|
||||
using Cocotte.Modules.Activities;
|
||||
using Cocotte.Modules.Activities.Models;
|
||||
using Cocotte.Modules.CompositeRoles;
|
||||
using Cocotte.Modules.Raids;
|
||||
using Cocotte.Options;
|
||||
using Cocotte.Services;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Quartz;
|
||||
|
||||
DiscordSocketConfig discordSocketConfig = new()
|
||||
{
|
||||
LogLevel = LogSeverity.Debug,
|
||||
MessageCacheSize = 200
|
||||
MessageCacheSize = 200,
|
||||
GatewayIntents = GatewayIntents.GuildMembers | GatewayIntents.Guilds
|
||||
};
|
||||
|
||||
IHost host = Host.CreateDefaultBuilder(args)
|
||||
.ConfigureAppConfiguration((_, configuration) =>
|
||||
{
|
||||
configuration.AddJsonFile("discord.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<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>();
|
||||
@@ -32,10 +59,49 @@ IHost host = Host.CreateDefaultBuilder(args)
|
||||
|
||||
services.AddHostedService<CocotteService>();
|
||||
|
||||
// Custom
|
||||
services.AddSingleton<SharedCounter>();
|
||||
services.AddTransient<TransientCounter>();
|
||||
// Data
|
||||
services.AddSingleton<IRaidsRepository, MemoryRaidRepository>();
|
||||
services.AddSingleton<IPlayerInfosRepository, MemoryPlayerInfosRepository>();
|
||||
services.AddSingleton<RolesOptions>();
|
||||
|
||||
// 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>();
|
||||
})
|
||||
.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();
|
||||
@@ -5,7 +5,16 @@
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"workingDirectory": "bin/Debug/net7.0"
|
||||
},
|
||||
"Cocotte-Prod": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Production"
|
||||
},
|
||||
"workingDirectory": "bin/Debug/net7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
Cocotte/Services/CocotteDbContext.cs
Normal file
30
Cocotte/Services/CocotteDbContext.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Reflection;
|
||||
using Cocotte.Modules.Activities;
|
||||
using Cocotte.Modules.CompositeRoles;
|
||||
using Cocotte.Options;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
@@ -15,18 +17,20 @@ public class CocotteService : BackgroundService
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly DiscordOptions _options;
|
||||
private readonly ActivityOptions _activityOptions;
|
||||
private readonly InteractionService _interactionService;
|
||||
|
||||
public CocotteService(ILogger<CocotteService> logger, IServiceProvider serviceProvider,
|
||||
IHostEnvironment hostEnvironment,
|
||||
IHostApplicationLifetime hostApplicationLifetime, DiscordSocketClient client,
|
||||
IOptions<DiscordOptions> options, InteractionService interactionService)
|
||||
IOptions<DiscordOptions> options, IOptions<ActivityOptions> groupOptions, InteractionService interactionService)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_hostApplicationLifetime = hostApplicationLifetime;
|
||||
_client = client;
|
||||
_options = options.Value;
|
||||
_activityOptions = groupOptions.Value;
|
||||
_interactionService = interactionService;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
}
|
||||
@@ -34,7 +38,7 @@ public class CocotteService : BackgroundService
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// Check token first
|
||||
if (_options.Token is null)
|
||||
if (string.IsNullOrWhiteSpace(_options.Token))
|
||||
{
|
||||
_logger.LogError("Couldn't find any discord bot token, exiting...");
|
||||
|
||||
@@ -43,8 +47,20 @@ public class CocotteService : BackgroundService
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateOptions())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -53,9 +69,35 @@ 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 ((_activityOptions.HelperRoleId
|
||||
| _activityOptions.DpsRoleId
|
||||
| _activityOptions.TankRoleId
|
||||
| _activityOptions.SupportRoleId) == 0)
|
||||
{
|
||||
_logger.LogError("One of the group options id is invalid, it cannot be 0");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ClientOnReady()
|
||||
{
|
||||
// Context & Slash commands can be automatically registered, but this process needs to happen after the client enters the READY state.
|
||||
@@ -63,7 +105,7 @@ public class CocotteService : BackgroundService
|
||||
if (_hostEnvironment.IsDevelopment())
|
||||
{
|
||||
// Check that a dev guild is set
|
||||
if (!_options.DevGuildId.HasValue)
|
||||
if (!_options.DevGuildId.HasValue && _options.DevGuildId!.Value != 0)
|
||||
{
|
||||
_logger.LogError("Couldn't find any dev guild while application is run in dev mode, exiting...");
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Cocotte.Services;
|
||||
|
||||
public class SharedCounter
|
||||
{
|
||||
public int Count { get; set; } = 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Cocotte.Services;
|
||||
|
||||
public class TransientCounter
|
||||
{
|
||||
public int Count { get; set; } = 0;
|
||||
}
|
||||
19
Cocotte/Utils/CdnUtils.cs
Normal file
19
Cocotte/Utils/CdnUtils.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
16
Cocotte/Utils/ChannelUtils.cs
Normal file
16
Cocotte/Utils/ChannelUtils.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
21
Cocotte/Utils/CocotteDayOfWeek.cs
Normal file
21
Cocotte/Utils/CocotteDayOfWeek.cs
Normal 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
|
||||
}
|
||||
17
Cocotte/Utils/Colors.cs
Normal file
17
Cocotte/Utils/Colors.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Discord;
|
||||
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
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);
|
||||
public static Color InfoColor => new(0x66D9EF);
|
||||
public static Color SuccessColor => new(0x2Ecc71);
|
||||
public static Color WarningColor => new(0xf1c40F);
|
||||
}
|
||||
35
Cocotte/Utils/DateTimeUtils.cs
Normal file
35
Cocotte/Utils/DateTimeUtils.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class DateTimeUtils
|
||||
{
|
||||
// Source: https://stackoverflow.com/a/50033489
|
||||
public static int CalculateDayOfWeekOffset(DayOfWeek current, DayOfWeek desired) {
|
||||
// f( c, d ) = [7 - (c - d)] mod 7
|
||||
// f( c, d ) = [7 - c + d] mod 7
|
||||
// c is current day of week and 0 <= c < 7l
|
||||
// d is desired day of the week and 0 <= d < 7
|
||||
int c = (int)current;
|
||||
int d = (int)desired;
|
||||
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);
|
||||
}
|
||||
50
Cocotte/Utils/EmbedUtils.cs
Normal file
50
Cocotte/Utils/EmbedUtils.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Discord;
|
||||
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class EmbedUtils
|
||||
{
|
||||
public static EmbedBuilder ErrorEmbed(string message, string title = "Erreur")
|
||||
{
|
||||
return new EmbedBuilder()
|
||||
.WithColor(Colors.ErrorColor)
|
||||
.WithAuthor(a => a
|
||||
.WithName(title)
|
||||
.WithIconUrl(CdnUtils.GetAsset("icons/error.webp"))
|
||||
)
|
||||
.WithDescription(message);
|
||||
}
|
||||
|
||||
public static EmbedBuilder InfoEmbed(string message, string title = "Info")
|
||||
{
|
||||
return new EmbedBuilder()
|
||||
.WithColor(Colors.InfoColor)
|
||||
.WithAuthor(a => a
|
||||
.WithName(title)
|
||||
.WithIconUrl(CdnUtils.GetAsset("icons/info.webp"))
|
||||
)
|
||||
.WithDescription(message);
|
||||
}
|
||||
|
||||
public static EmbedBuilder SuccessEmbed(string message, string title = "Succès")
|
||||
{
|
||||
return new EmbedBuilder()
|
||||
.WithColor(Colors.SuccessColor)
|
||||
.WithAuthor(a => a
|
||||
.WithName(title)
|
||||
.WithIconUrl(CdnUtils.GetAsset("icons/success.webp"))
|
||||
)
|
||||
.WithDescription(message);
|
||||
}
|
||||
|
||||
public static EmbedBuilder WarningEmbed(string message, string title = "Attention")
|
||||
{
|
||||
return new EmbedBuilder()
|
||||
.WithColor(Colors.WarningColor)
|
||||
.WithAuthor(a => a
|
||||
.WithName(title)
|
||||
.WithIconUrl(CdnUtils.GetAsset("icons/warning.webp"))
|
||||
)
|
||||
.WithDescription(message);
|
||||
}
|
||||
}
|
||||
16
Cocotte/Utils/EmoteUtils.cs
Normal file
16
Cocotte/Utils/EmoteUtils.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Discord;
|
||||
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class EmoteUtils
|
||||
{
|
||||
public static IEmote ToEmote(this string emoteText)
|
||||
{
|
||||
if (Emote.TryParse(emoteText, out var emote))
|
||||
{
|
||||
return emote;
|
||||
}
|
||||
|
||||
return Emoji.Parse(emoteText);
|
||||
}
|
||||
}
|
||||
31
Cocotte/Utils/EnumerableExtensions.cs
Normal file
31
Cocotte/Utils/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
|
||||
Func<TSource, TSource, TSource> conflictResolver)
|
||||
{
|
||||
var comparer = Comparer<TKey>.Default;
|
||||
|
||||
var min = source.First();
|
||||
var minKey = keySelector(min);
|
||||
|
||||
foreach (var element in source.Skip(1))
|
||||
{
|
||||
var key = keySelector(element);
|
||||
|
||||
if (comparer.Compare(key, minKey) < 0)
|
||||
{
|
||||
min = element;
|
||||
minKey = key;
|
||||
}
|
||||
else if (comparer.Compare(key, minKey) == 0)
|
||||
{
|
||||
min = conflictResolver(min, element);
|
||||
minKey = keySelector(min);
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
}
|
||||
34
Cocotte/Utils/ModalExtensions.cs
Normal file
34
Cocotte/Utils/ModalExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Discord;
|
||||
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class ModalExtensions
|
||||
{
|
||||
public static ModalBuilder UpdateTextInput(this ModalBuilder modal, string customId, Action<TextInputBuilder> inputUpdater)
|
||||
{
|
||||
var components = modal.Components.ActionRows.SelectMany(r => r.Components).OfType<TextInputComponent>();
|
||||
var component = components.First(c => c.CustomId == customId);
|
||||
|
||||
var builder = new TextInputBuilder
|
||||
{
|
||||
CustomId = customId,
|
||||
Label = component.Label,
|
||||
MaxLength = component.MaxLength,
|
||||
MinLength = component.MinLength,
|
||||
Placeholder = component.Placeholder,
|
||||
Required = component.Required,
|
||||
Style = component.Style,
|
||||
Value = component.Value
|
||||
};
|
||||
|
||||
inputUpdater(builder);
|
||||
|
||||
foreach (var row in modal.Components.ActionRows.Where(row => row.Components.Any(c => c.CustomId == customId)))
|
||||
{
|
||||
row.Components.RemoveAll(c => c.CustomId == customId);
|
||||
row.AddComponent(builder.Build());
|
||||
}
|
||||
|
||||
return modal;
|
||||
}
|
||||
}
|
||||
43
Cocotte/Utils/NumberUtils.cs
Normal file
43
Cocotte/Utils/NumberUtils.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Cocotte.Utils;
|
||||
|
||||
public static class NumberUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a string representation of an <see cref="IBinaryInteger{TSelf}" /> with digits separated by spaces
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static string FormatSpaced<T>(this IBinaryInteger<T> number) where T : IBinaryInteger<T>?
|
||||
{
|
||||
var stringNumber = number.ToString(null, null);
|
||||
|
||||
Span<char> result = stackalloc char[stringNumber.Length + stringNumber.Length / 3];
|
||||
int resultOffset = 0;
|
||||
|
||||
for (int i = 0; i < stringNumber.Length; i++)
|
||||
{
|
||||
// Add a space
|
||||
if (i > 2 && (i + 1) % 3 == 1)
|
||||
{
|
||||
result[resultOffset] = ' ';
|
||||
resultOffset++;
|
||||
}
|
||||
|
||||
result[resultOffset] = stringNumber[^(i + 1)];
|
||||
resultOffset++;
|
||||
}
|
||||
|
||||
var realResult = result[..resultOffset];
|
||||
Span<char> reversed = stackalloc char[resultOffset];
|
||||
|
||||
for (int i = 0; i < reversed.Length; i++)
|
||||
{
|
||||
reversed[i] = realResult[^(i + 1)];
|
||||
}
|
||||
|
||||
return reversed.ToString();
|
||||
}
|
||||
}
|
||||
18
Cocotte/activity.json
Normal file
18
Cocotte/activity.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"ActivityOptions": {
|
||||
"OrganizerRoleId": 0,
|
||||
"OrganizerEmote": "",
|
||||
|
||||
"HelperRoleId": 0,
|
||||
"HelperEmote": "",
|
||||
|
||||
"DpsRoleId": 0,
|
||||
"DpsEmote": "",
|
||||
|
||||
"TankRoleId": 0,
|
||||
"TankEmote": "",
|
||||
|
||||
"SupportRoleId": 0,
|
||||
"SupportEmote": ""
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"CocotteContext": "Data Source=cocotte.db"
|
||||
}
|
||||
}
|
||||
|
||||
0
Cocotte/cocotte.db
Normal file
0
Cocotte/cocotte.db
Normal file
26
Cocotte/compositeRoles.json
Normal file
26
Cocotte/compositeRoles.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"CompositeRolesOptions": {
|
||||
"CompositeRoles": {
|
||||
"someGuildId": [
|
||||
{
|
||||
"TargetRoleId": 1,
|
||||
"CompositeRolesIds": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
]
|
||||
}
|
||||
],
|
||||
"anotherGuildId": [
|
||||
{
|
||||
"TargetRoleId": 45,
|
||||
"CompositeRolesIds": [
|
||||
98,
|
||||
1,
|
||||
2
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Cocotte/discord.json
Normal file
6
Cocotte/discord.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"DiscordOptions": {
|
||||
"Token": "",
|
||||
"DevGuildId": 0
|
||||
}
|
||||
}
|
||||
7
global.json
Normal file
7
global.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
||||
0
identifier.sqlite
Normal file
0
identifier.sqlite
Normal file
Reference in New Issue
Block a user